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 
thisin 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