【发布时间】:2019-07-24 07:21:32
【问题描述】:
我有很多本地类接受某种形式的回调,通常是boost::signals2::slot-object。
但为了简单起见,我们假设类:
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);
}
现在我有一个封装了这个原生类的托管类,我想给原生类传递一个回调方法。
public ref class TestWrapper
{
public:
TestWrapper()
: _native(new Test())
{
}
~TestWrapper()
{
delete _native;
}
private:
void CallbackMethod(bool value);
Test* _native;
};
现在我通常会做以下事情:
- 在托管包装器中声明一个方法,这是我想要的回调。
- 为此方法创建一个托管委托对象。
- 使用 GetFunctionPointerForDelegate 获取指向函数的指针
- 将指针指向正确的签名
- 将指针作为回调传递给本机类。
- 我还让委托保持活动状态,因为我担心它会被垃圾回收并且我会有一个悬空函数指针(这个假设是否正确?)
这看起来有点像这样:
_managedDelegateMember = gcnew ManagedEventHandler(this, &TestWrapper::Callback);
System::IntPtr stubPointer = Marshal::GetFunctionPointerForDelegate(_managedDelegateMember);
UnmanagedEventHandlerFunctionPointer functionPointer = static_cast<UnmanagedEventHandlerFunctionPointer >(stubPointer.ToPointer());
_native->SetCallback(functionPointer);
我想减少代码量并且不必执行任何强制转换或声明任何委托类型。我想使用没有委托的 lambda 表达式。
这是我的新方法:
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;
}
);
}
- 声明一个接受
this的静态方法,以便能够使用 C++11 lambda。 - 使用 gcroot 捕获 lambda 中的托管类,并在 lambda 处于活动状态时延长其生命周期。
- 没有强制转换,没有额外的委托类型和成员,最少的额外分配。
问题:
这种方法安全吗?我担心我遗漏了一些东西,这可能会在某些意外情况下导致内存泄漏/未定义的行为。
编辑:
当 lambda 调用其托管包装类的私有方法时,这种方法会导致 MethodAccessException。看来这个方法至少必须是internal。
【问题讨论】: