Как использовать boost :: bind в C ++ / CLI для привязки члена управляемого класса
-
03-07-2019 - |
Вопрос
Я использую boost :: signal в собственном классе C ++, и сейчас я пишу оболочку .NET в C ++ / CLI, чтобы я мог представить собственные обратные вызовы C ++ в качестве событий .NET. Когда я пытаюсь использовать boost :: bind для получения адреса функции-члена моего управляемого класса, я получаю сообщение об ошибке компилятора 3374, в котором говорится, что я не могу взять адрес функции-члена, пока я не создаю экземпляр делегата. Кто-нибудь знает, как связать функцию-член управляемого класса, используя boost :: bind?
Для пояснения следующий пример кода вызывает ошибку компилятора 3374:
#include <boost/bind.hpp>
public ref class Managed
{
public:
Managed()
{
boost::bind(&Managed::OnSomeEvent, this);
}
void OnSomeEvent(void)
{
}
};
Решение
Пока ваш ответ работает, он раскрывает часть вашей реализации миру (Managed :: OnSomeEvent). Если вы не хотите, чтобы люди могли вызывать событие OnChange, просто вызвав OnSomeEvent (), вы можете обновить свой класс Managed следующим образом (на основе этот совет ):
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();
}
Обратите внимание на альтернативную bind<R>()
форму, которая используется.
Другие советы
Еще немного погуглив, я наконец-то нашел хороший пост в блоге о том, как это сделать. Код в этом посте был немного больше, чем мне было нужно, но основной слепок заключался в использовании глобальной свободной функции, которая принимает аргумент управляемого указателя this, заключенного в gcroot & Lt; & Gt; шаблон. См. Пример SomeEventProxy (...) в приведенном ниже коде. Затем эта функция поворачивается и вызывает управляемого члена, которого я пытался связать. Мое решение отображается ниже для дальнейшего использования.
#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;
}