How to use boost::bind in C++/CLI to bind a member of a managed class

后端 未结 2 611
無奈伤痛
無奈伤痛 2020-12-30 08:41

I am using boost::signal in a native C++ class, and I now I am writing a .NET wrapper in C++/CLI, so that I can expose the native C++ callbacks as .NET events. When I try to

相关标签:
2条回答
  • 2020-12-30 09:00

    After googling some more, I finally found a nice blog post about how to do this. The code in that post was a little more than I needed, but the main nugget was to use a global free function that takes an argument of the managed this pointer wrapped in a gcroot<> template. See the SomeEventProxy(...) in the code below for an example. This function then turns around and calls the managed member I was trying to bind. My solution appears below for future reference.

    #include <msclr/marshal.h>
    
    #include <boost/bind.hpp>
    #include <boost/signal.hpp>
    #include <iostream>
    
    #using <mscorlib.dll>
    
    using namespace System;
    using namespace msclr::interop;
    
    typedef boost::signal<void (void)> ChangedSignal;
    typedef boost::signal<void (void)>::slot_function_type ChangedSignalCB;
    typedef boost::signals::connection  Callback;
    
    
    class Native
    {
    public:
    
        void ChangeIt() 
        {
            changed();
        }
    
        Callback RegisterCallback(ChangedSignalCB Subscriber)
        {
            return changed.connect(Subscriber);
        }
    
        void UnregisterCallback(Callback CB)
        {
            changed.disconnect(CB);
        }
    
    private:
        ChangedSignal changed;
    };
    
    
    
    delegate void ChangeHandler(void);
    
    
    public ref class Managed
    {
    public:
        Managed(Native* Nat);
        ~Managed();
        void OnSomeEvent(void);
    
        event ChangeHandler^ OnChange;
    
    private:
        Native* native;
        Callback* callback;
    };
    
    
    void SomeEventProxy(gcroot<Managed^> This)
    {
        This->OnSomeEvent();
    }
    
    
    Managed::Managed(Native* Nat)
     : native(Nat)
    {
        native = Nat;
        callback = new Callback;
        *callback = native->RegisterCallback(boost::bind( SomeEventProxy, gcroot<Managed^>(this) ) );
    }
    
    Managed::~Managed()
    {
        native->UnregisterCallback(*callback);
        delete callback;
    }
    
    void Managed::OnSomeEvent(void)
    {
        OnChange();
    }
    
    
    void OnChanged(void)
    {
        Console::WriteLine("Got it!");
    }
    
    int main(array<System::String ^> ^args)
    {
        Native* native = new Native;
        Managed^ managed = gcnew Managed(native);
    
        managed->OnChange += gcnew ChangeHandler(OnChanged);
    
        native->ChangeIt();
    
        delete native;
        return 0;
    }
    
    0 讨论(0)
  • 2020-12-30 09:01

    While your answer works, it exposes some of your implementation to the world (Managed::OnSomeEvent). If you don't want people to be able to raise the OnChange event willy-nilly by invoking OnSomeEvent(), you can update your Managed class as follows (based on this advice):

    public delegate void ChangeHandler(void);
    typedef void (__stdcall *ChangeCallback)(void);
    
    public ref class Managed
    {
    public:
        Managed(Native* Nat);
        ~Managed();
    
        event ChangeHandler^ OnChange;
    
    private:
        void OnSomeEvent(void);
        Native* native;
        Callback* callback;
        GCHandle gch;
    };
    
    Managed::Managed(Native* Nat)
     : native(Nat)
    {
        callback = new Callback;
    
        ChangeHandler^ handler = gcnew ChangeHandler( this, &Managed::OnSomeEvent );
        gch = GCHandle::Alloc( handler );
        System::IntPtr ip = Marshal::GetFunctionPointerForDelegate( handler );
        ChangeCallback cbFunc = static_cast<ChangeCallback>( ip.ToPointer() );
    
        *callback = native->RegisterCallback(boost::bind<void>( cbFunc ) );
    }
    
    Managed::~Managed()
    {
        native->UnregisterCallback(*callback);
        delete callback;
        if ( gch.IsAllocated )
        {
            gch.Free();
        }
    }
    
    void Managed::OnSomeEvent(void)
    {
        OnChange();
    }
    

    Note the alternate bind<R>() form that's used.

    0 讨论(0)
提交回复
热议问题