2016-07-29 2 views
2

Я пишу обертку в C++/CLI (VS 2015) для данного кода на C++, чтобы использовать, наконец, сгенерированную управляемую dll в C#. Поэтому я создал управляемый класс, который вызывает родной класс. Этот родной класс ссылается на данный код C++ из-за фабричного метода, который возвращает unique_ptr <>.Сопоставление специфических функций C++ с C++/CLI

Теперь я застрял, потому что C++ код имеет функцию обратного вызова:

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

Результат имеет тип

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

Вопросы:

  1. Как можно сопоставить функцию обратного вызова аргумент std :: function <> в C++/CLI?
  2. Где & как я должен преобразовать C++ в результате структура (вектор пар) для структуры данных .NET (список кортежей) в терминах обратного вызова для доступа к нему в C#?

  3. Можно ли написать лямбда-выражения (с коллекциями .NET), которые будут отображаться в неуправляемой структуре данных (вектор)?

    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++; 
    }); 
    

    Должен ли я написать обертку для лямбда-выражения?

ответ

1

sligthly перефразировать я буду своими оригинальные вопросы, чтобы лучше соответствовать предложенному моему решению:

  1. Как обернуть метод C# как неуправляемого C++ std::function<> для использования в качестве обратного вызова?
  2. Как преобразовать данные метода обратного вызова из C++ (std::vector<std::pair<>>) в C# (List<Tuple<>>)?
  3. Можно ли использовать выражения лямбда C#, включая захваченную переменную в качестве методов обратного вызова?

См. Ниже решение. Имейте в виду, что это некрасиво и не требует обработки ошибок или ресурсов. Он отвечает на эти вопросы следующим образом (Inner быть дано C++ класс, который вы хотите, чтобы обернуть):

  1. Метод обратного вызова может быть предоставлен как C# Action<List<Tuple<>>> (см Middle::SetCallback). Он вызывается методом обёртки Middle::WrapCallback. Это, в свою очередь, может быть привязано к неуправляемому указателю функции, который предоставляется внутреннему классу (см. Конструктор).
  2. Мне неизвестны какие-либо сортирующие помощники для коллекций, поэтому это просто делается путем копирования соответствующих структур данных (предполагается только чтение) (см. Middle::WrapCallback). Middle::DoCallback просто предназначен для вызова обратного вызова извне, поэтому он должен сделать это преобразование в обратном направлении.
  3. Несомненно, это возможно, но это полностью зависит от кода клиента C#, использующего эту оболочку.
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); 
    } 
} 
Смежные вопросы