2016-07-29 26 views
2

Ich schreibe einen Wrapper in C++/CLI (VS 2015) für gegebenen C++ - Code, um schließlich die generierte verwaltete DLL in C# zu verwenden. Daher habe ich eine verwaltete Klasse erstellt, die eine native Klasse aufruft. Diese native Klasse verknüpft den angegebenen C++ - Code aufgrund einer Factory-Methode, die einen unique_ptr <> zurückgibt.Zuordnung von C++ - spezifischen Funktionen zu C++/CLI

Jetzt habe ich stecken, weil die C++ Code einen Rückruf hat:

Callback(std::function<void(const Result &>) 

Ergebnis ist vom Typ

std::vector<std::pair<float, float>> 

Fragen:

  1. Wie kann ich die Callback-Karte Argument Std :: Funktion <> in C++/CLI?
  2. Wo & wie muss ich die C++ verwandeln sich ergebende Struktur (Vektor von Paaren) zu .NET Datenstruktur (Liste von Tupeln) in Bezug auf den Rückruf es in C# zugreifen?

  3. Ist es möglich, Lambda-Ausdrücke (mit .NET-Auflistungen) zu schreiben, die der nicht verwalteten Datenstruktur (Vektor) zugeordnet werden?

    unsigned int cnt = 0; 
    
    nativeClass->Callback([&cnt] (const Result &v) { 
    auto it = d::max_element(v.begin(), v.end(), 
         [](const Pair &a, const Pair &b) { 
          return a.second < b.second; 
         }); 
    
         // do something with iterator it 
         // … 
    
         cnt++; 
    }); 
    

    Muss ich einen Lambda-Ausdruck-Wrapper deshalb schreiben?

Antwort

1

Schlecht sligthly ursprüngliche Fragen umformulieren besser meine vorgeschlagene Lösung passen:

  1. Wie wickeln wir eine C# Methode als nicht verwaltete C++ std::function<> für die Verwendung als Rückruf?
  2. Wie konvertiere ich die Daten der Rückrufmethode von C++ (std::vector<std::pair<>>) in C# (List<Tuple<>>)?
  3. Ist es möglich, C# Lambda-Ausdrücke einschließlich der erfassten Variablen als Callback-Methoden zu verwenden?

Siehe unten für eine Lösung. Bitte beachten Sie, dass es nicht schön ist und keine Fehler oder Ressourcen behandelt. Es beantwortet die oben gestellten Fragen wie folgt (Inner wobei die gegebene C++ Klasse, die Sie wickeln wollen):

  1. Die Callback-Methode kann als ein C# Action<List<Tuple<>>> (siehe Middle::SetCallback) zur Verfügung gestellt werden. Es wird von einer Wrapper-Methode Middle::WrapCallback aufgerufen. Dies wiederum kann zu einem nicht verwalteten Funktionszeiger geleitet werden, der der inneren Klasse zur Verfügung gestellt wird (siehe Konstruktor).
  2. Mir ist kein Marshalling Helfer für Sammlungen bekannt, so dass dies einfach durch Kopieren der jeweiligen (vermutlich nur lesbaren) Datenstrukturen (siehe Middle::WrapCallback) erfolgt. Middle::DoCallback ist einfach da, um den Rückruf von außen auszulösen, also muss diese Konvertierung in umgekehrter Richtung erfolgen.
  3. Sicher, das ist möglich, aber es liegt ganz an dem C# -Client-Code, der diesen Wrapper verwendet.
using namespace System; 
using namespace System::Collections::Generic; 
using namespace System::Runtime::InteropServices; 

namespace Wrapper { 
    delegate void CallbackPointer(const Result &data); 
    typedef void(*UnmanagedPointer)(const Result &data); 

    Middle::Middle() 
    { 
     instance = new Inner(); 

     CallbackPointer^ managed = gcnew CallbackPointer(this, &Middle::WrapCallback); 
     IntPtr stubPointer = Marshal::GetFunctionPointerForDelegate(managed); 
     UnmanagedPointer UnmanagedCallback = static_cast<UnmanagedPointer>(stubPointer.ToPointer()); 

     instance->SetCallback(UnmanagedCallback); 
    } 

    void Middle::SetCallback(Action<List<Tuple<float, float>^>^>^ callback) 
    { 
     handler = callback; 
    } 

    void Middle::DoCallback(List<Tuple<float, float>^>^ data) 
    { 
     Result *buffer = new Result(); 

     for (int i = 0; i < data->Count; i++) 
     { 
      std::pair<float, float> *el = new std::pair<float, float>(data[i]->Item1, data[i]->Item2); 
      buffer->push_back(*el); 
     } 

     instance->Callback(*buffer); 
    } 

    void Middle::WrapCallback(const Result &data) 
    { 
     List<Tuple<float, float>^>^ buffer = gcnew List<Tuple<float, float>^>(); 

     for (Result::size_type i = 0; i < data.size(); i++) 
     { 
      buffer->Add(Tuple::Create(data[i].first, data[i].second)); 
     } 

     handler->Invoke(buffer); 
    } 
}