I´ll sligthly rephrase your original questions to better fit my proposed solution:
- How do I wrap a C# method as an unmanaged C++
std::function<>
for use as a callback?
- How do I convert the callback method´s data from C++ (
std::vector<std::pair<>>
) to C# (List<Tuple<>>
)?
- Is it possible to use C# lambda expressions including captured variable as callback methods?
See below for a solution. Please keep in mind that it is not pretty and does not do any error or resource handling. It answers the above questions as follows (Inner
being the given C++ class you want to wrap):
- The callback method can be provided as a C#
Action<List<Tuple<>>>
(see Middle::SetCallback
). It gets called by a wrapper method Middle::WrapCallback
. This in turn can be marshalled to an unmanaged function pointer which is provided to the inner class (see constructor).
- I am not aware of any marshalling helpers for collections, so this is simply done by copying the respective (assumed to be read only) data structures (see
Middle::WrapCallback
). Middle::DoCallback
is simply there to trigger the callback from outside, so it needs to do that conversion in the reverse direction.
- Sure, that´s possible, but it is entirely up to the C# client code using this wrapper.
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);
}
}