I have a lot of c++ classes that accept some form of callbacks, usually a boost::signals2::slot object.
But for simplicity, lets assume the class:
class Test
{
// set a callback that will be invoked at an unspecified time
// will be removed when Test class dies
void SetCallback(std::function<void(bool)> callback);
}
Now I have a managed class that wraps this c++ class, I would like to pass a callback method to the c++ class.
public ref class TestWrapper
{
public:
TestWrapper()
: _native(new Test())
{
}
~TestWrapper()
{
delete _native;
}
private:
void CallbackMethod(bool value);
Test* _native;
};
now usually what I would do is the following:
- Declare a method in the managed wrapper that is the callback I want.
- Create a managed delegate object to this method.
- Use GetFunctionPointerForDelegate to obtain a pointer to a function
- Cast the pointer to the correct signature
- Pass the pointer to the native class as callback.
- I also keep the delegate alive since I fear it will be garbage collected and I will have a dangling function pointer (is this assumption correct?)
this looks kind of like this:
_managedDelegateMember = gcnew ManagedEventHandler(this, &TestWrapper::Callback);
System::IntPtr stubPointer = Marshal::GetFunctionPointerForDelegate(_managedDelegateMember);
UnmanagedEventHandlerFunctionPointer functionPointer = static_cast<UnmanagedEventHandlerFunctionPointer >(stubPointer.ToPointer());
_native->SetCallback(functionPointer);
I Would like to reduce the amount of code and not have to perform any casts nor declare any delegate types. I want to use a lambda expression with no delegate.
This is my new approach:
static void SetCallbackInternal(TestWrapper^ self)
{
gcroot<TestWrapper^> instance(self);
self->_native->SetCallback([instance](bool value)
{
// access managed class from within native code
instance->Value = value;
}
);
}
- Declare a static method that accepts
this
in order to be able to use C++11 lambda. - Use gcroot to capture the managed class in the lambda and extend its lifetime for as long as the lambda is alive.
- No casts, no additional delegate type nor members, minimal extra allocation.
Question: Is this approach safe? I'm fearing I'm missing something and that this can cause a memory leak / undefined behavior in some unanticipated scenario.
Aucun commentaire:
Enregistrer un commentaire