我需要将一个方法绑定到一个函数回调中,除了demote-boostfunction-to-a-plain-function-pointer中讨论的这个代码段不合法。
获得此行为的最简单方法是什么?
struct C {
void m(int x) {
(void) x;
_asm int 3;
}};
typedef void (*cb_t)(int);
int main() {
C c;
boost::function<void (int x)> cb = boost::bind(&C::m, &c, _1);
cb_t raw_cb = *cb.target<cb_t>(); //null dereference
raw_cb(1);
return 0;
}
答案 0 :(得分:2)
您可以将该绑定参数推送到全局变量中并创建一个静态函数,该函数可以获取值并在其上调用函数,或者您将不得不动态生成每个实例函数 - 这将涉及某种类型的动态代码生成堆上的存根函数,该函数具有设置为您想要的值的静态局部变量,然后在其上调用函数。
第一种方式简单易懂,但根本不是线程安全的或可重入的。第二个版本是混乱和困难的,但如果做得对,则是线程安全的和可重入的。
编辑:我刚刚发现ATL使用代码生成技术来做到这一点 - 他们动态生成thunk,设置this
指针和其他数据,然后跳转到回调函数。 Here's a CodeProject article解释了它是如何工作的,可能会让你知道如何自己做。特别要看最后一个样本(程序77)。
请注意,自从撰写文章以来,DEP已经存在,您需要使用VirtualAlloc
和PAGE_EXECUTE_READWRITE
来获取一块内存,您可以在其中分配您的thunk并执行它们。< / p>
答案 1 :(得分:2)
你可以让你自己的类做与boost绑定功能相同的事情。所有类所要做的就是接受函数类型和指向包含该函数的对象的指针。例如,这是一个void return和void param delegate:
template<typename owner>
class VoidDelegate : public IDelegate
{
public:
VoidDelegate(void (owner::*aFunc)(void), owner* aOwner)
{
mFunction = aFunc;
mOwner = aOwner;
}
~VoidDelegate(void)
{}
void Invoke(void)
{
if(mFunction != 0)
{
(mOwner->*mFunction)();
}
}
private:
void (owner::*mFunction)(void);
owner* mOwner;
};
用法:
class C
{
void CallMe(void)
{
std::cout << "called";
}
};
int main(int aArgc, char** aArgv)
{
C c;
VoidDelegate<C> delegate(&C::CallMe, &c);
delegate.Invoke();
}
现在,由于VoidDelegate<C>
是一个类型,拥有这些类型的集合可能不实用,因为如果列表也包含类B的函数呢?它不能。
这就是多态性发挥作用的地方。您可以创建一个IDelegate接口,它具有一个Invoke函数:
class IDelegate
{
virtual ~IDelegate(void) { }
virtual void Invoke(void) = 0;
}
如果VoidDelegate<T>
实现了IDelegate,你可以拥有一个IDelegates集合,因此可以回调不同类类型的方法。
答案 2 :(得分:1)
#include <iostream>
typedef void(*callback_t)(int);
template< typename Class, void (Class::*Method_Pointer)(void) >
void wrapper( int class_pointer )
{
Class * const self = (Class*)(void*)class_pointer;
(self->*Method_Pointer)();
}
class A
{
public:
int m_i;
void callback( )
{ std::cout << "callback: " << m_i << std::endl; }
};
int main()
{
A a = { 10 };
callback_t cb = &wrapper<A,&A::callback>;
cb( (int)(void*)&a);
}
答案 3 :(得分:0)
我现在通过将C转换为单例,将C :: m分解为C :: m_Impl,并声明转发到单例实例的静态C :: m(int)来使其正常工作。谈论黑客攻击。