给定一个典型的类:
结构随便 { 无效 Doit(); }; 不管w;
让基于 C void* 的回调(例如 pthread_create() 或信号处理程序)调用成员函数的最佳方法是什么?
pthread_t pid; pthread_create(&pid, 0, ... &w.Doit() ... );
大多数 C 回调允许指定一个参数,例如
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine)(void*), void *arg);
所以你可以有
void myclass_doit(void* x)
{
MyClass* c = reinterpret_cast<MyClass*>(x);
c->doit();
}
pthread_create(..., &myclass_doit, (void*)(&obj));
最简洁的解决方案是在所有代码共享的头文件中定义:
模板 <typename T, void (T::*M)()> 无效*重击( 无效* p) { T* pt = static_cast<T*>(p); (pt->*M)(); 返回0; }
您可能想要定义 4 个版本:一个是 thunk 返回 void 和 void 的版本*
,一个是每个成员函数返回 void 和 void的版本*
。这样编译器可以根据情况匹配最好的编译器(事实上,如果一切都不匹配,它会抱怨。)
然后,每次遇到这些情况之一时,您只需输入:
pthread_create(&pid, 0, &thunk<Whatever, &Whatever::doit>, &w);
只要方法是从类的代码中引用的,这甚至可以在方法是私有的时起作用。(如果不是,我想知道为什么代码引用了私有方法。)
使用这样的 C 函数包装器:
struct Whatever
{
void Doit();
};
extern "C" static int DoItcallback (void * arg)
{
Whatever * w = (Whatever *) arg;
w->DoIt();
return something;
}
仅当您可以以某种方式将指针传递给类时才有效。大多数回调机制都允许这样做。
Afaik 这是唯一的方法。如果没有大量黑客攻击,您不能直接从 C 调用方法。
成员函数是私有的吗?如果没有,请使用标准成语:
void* pthread_foo_caller(void* arg) {
Foo* foo = static_cast<Foo*>(arg);
foo->bar();
return NULL;
}
如果成员函数是私有的,您可以在类中声明一个静态方法,该方法采用“this”指针并调用适当的方法。例如:
class Foo {
public:
static pthread_foo_caller(void* arg);
...
};
void* Foo::pthread_foo_caller(void* arg) {
Foo* foo = static_cast<Foo*>(arg);
foo->private_bar();
return NULL;
}
您应该注意的一件事是,如果您编写这样的代码:
try {
CallIntoCFunctionThatCallsMeBack((void *)this, fCallTheDoItFunction);
} catch (MyException &err)
{
stderr << "badness.";
}
void fCallTheDoItFunction(void *cookie)
{
MyClass* c = reinterpret_cast<MyClass*>(cookie);
if (c->IsInvalid())
throw MyException;
c->DoIt();
}
根据您的编译器,您可能会遇到一些严重的问题。事实证明,在一些编译器中,在优化时,他们在 try/catch 块中看到一个 C 调用,并高兴地惊呼:“我正在调用一个 C 函数,因为它是老式的 C,所以不能抛出!Calloo-catch ! 我将删除 try/catch 的所有痕迹,因为它永远无法到达。
愚蠢的编译器。
不要调用 C 来回叫你并期望能够赶上。
成员函数必须是静态的。非静态有一个隐含的“this”参数。将指针作为 void* 传递给您的任何实例,以便静态成员可以访问该实例。
这是一个简单的方法,不要忘记正确管理“MemberFunction”对象的生命周期。
#include
class MyClass
{
public:
void DoStuff()
{
printf("Doing Stuff!");
}
};
struct MemberFunction
{
virtual ~MemberFunction(){}
virtual void Invoke() = 0;
};
void InvokeMember(void *ptr)
{
static_cast(ptr)->Invoke();
}
template
struct MemberFunctionOnT : MemberFunction
{
typedef void (T::*function_t)();
public:
MemberFunctionOnT(T* obj, function_t fun)
{
m_obj = obj;
m_fun = fun;
}
void Invoke()
{
(m_obj->*m_fun)();
}
private:
T *m_obj;
function_t m_fun;
};
template
MemberFunction* NewMemberFunction(T *obj, void (T::*fun)())
{
return new MemberFunctionOnT(obj, fun);
}
//simulate a C-style function offering callback functionality.
void i_will_call_you_later(void (*fun)(void*), void *arg)
{
fun(arg);
}
int main()
{
//Sample usage.
MyClass foo;
MemberFunction *arg = NewMemberFunction(&foo, &MyClass::DoStuff);
i_will_call_you_later(&InvokeMember, arg);
return 0;
}
看到这个链接
基本上,这不是直接可能的,因为:“指向非静态成员的指针与普通 C 函数指针不同,因为它们需要传递类对象的 this 指针。因此,普通函数指针和 [pointers to] 非静态成员函数具有不同且不兼容的签名"
虽然我没有在 C 中使用过它,但为了进行回调,我强烈建议您查看libsigc++。这正是我在执行 C++ 回调时多次需要的。