使C ++成员函数被C回调调用的最佳方法?
给定一个典型的类:
struct Whatever { void Doit(); }; Whatever w;
获得基于C void *的回调(例如pthread_create()或者信号处理程序)要调用的成员函数的最佳方法是什么?
pthread_t pid; pthread_create(&pid, 0, ... &w.Doit() ... );
解决方案
成员函数必须是静态的。非静态有一个隐含的" this"参数。将指针作为void *传递到任何实例,以便静态成员可以获取该实例。
大多数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));
看到这个链接
基本上,这是不可能直接实现的,因为:
"指向非静态成员的指针与普通C函数指针不同,因为它们需要传递类对象的this指针。因此,普通函数指针和非静态成员函数的指针具有不同且不兼容的签名。"
使用C函数包装器,如下所示:
struct Whatever { void Doit(); }; extern "C" static int DoItcallback (void * arg) { Whatever * w = (Whatever *) arg; w->DoIt(); return something; }
仅当我们可以通过某种方式将指针传递给类时才有效。大多数回调机制都允许这样做。
Afaik这是执行此操作的唯一方法。没有大量的黑客攻击,就不能直接从C中调用方法。
虽然我还没有使用C语言来进行回调,但是我强烈建议我们查看libsigc ++。正是在执行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; }
这是一种简单的方法,请不要忘记正确管理" 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; }
最简洁的解决方案是在所有代码共享的头文件中定义:
template <typename T, void (T::*M)()> void* thunk( void* p) { T* pt = static_cast<T*>(p); (pt->*M)(); return 0; }
我们可能需要定义4个版本:每个版本的thunk返回void和void*
,每个版本的成员函数返回void和void*
。这样一来,编译器就可以根据情况匹配最佳的编译器(实际上,如果所有内容都不匹配,它将抱怨)。
然后,每次遇到以下一种情况时,我们只需输入以下内容:
pthread_create(&pid,0,&thunk <无论,&Whatever :: doit>,&w);
只要该方法是私有的,只要从类的代码中引用该方法,这甚至将起作用。 (如果没有,我不得不怀疑为什么代码引用了私有方法。)
我们应该意识到的一件事是,如果我们编写如下代码:
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-cally !我将删除try / catch的所有痕迹,因为它将永远无法实现。
愚蠢的编译器。
不要调用C来回叫我们并期望能够被追上。