給定一個典型的類:通過C回調調用C++成員函數的最佳方法是什麼?
struct Whatever { void Doit(); }; Whatever w;
什麼是讓成員函數的最好辦法通過C void *的基於回調,如在pthread_create()或信號處理函數中調用?
pthread_t pid; pthread_create(&pid, 0, ... &w.Doit() ...);
給定一個典型的類:通過C回調調用C++成員函數的最佳方法是什麼?
struct Whatever { void Doit(); }; Whatever w;
什麼是讓成員函數的最好辦法通過C void *的基於回調,如在pthread_create()或信號處理函數中調用?
pthread_t pid; pthread_create(&pid, 0, ... &w.Doit() ...);
成員函數必須是靜態的。非靜態有一個暗示的「這個」論點。將指針傳遞給你的任何實例作爲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));
看到這個link
基本上,它不是直接的,因爲: 「指針到非靜態成員是因爲普通的C函數指針不同他們需要傳遞一個類對象的this指針,因此普通函數指針和指向非靜態成員函數的指針具有不同的和不兼容的簽名「
使用C函數的包裝是這樣的:
struct Whatever
{
void Doit();
};
extern "C" static int DoItcallback (void * arg)
{
Whatever * w = (Whatever *) arg;
w->DoIt();
return something;
}
只有工作,如果你可以將指針傳遞到類莫名其妙。大多數回調機制都允許這樣做。
Afaik這是執行此操作的唯一方法。沒有大量黑客攻擊,你不能直接從C中調用方法。
這需要爲每個回調單獨封裝。它確實有效,但會造成大量的代碼管理開銷。 – Catskul 2009-09-15 16:41:25
雖然我沒有使用它從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 *
。這樣編譯器可以匹配最好的一個,這取決於具體情況(事實上,如果一切不匹配,它都會抱怨。)
然後你必須輸入你每次碰到這些情形之一的時間:
在pthread_create(& PID,0,&咚<不管結果如何,&無論:: 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調用你回來,希望能夠趕上。
只是注意myclass_doit必須有c鏈接(即extern「C」) – 2008-09-26 15:37:56
不,它顯然是從C++文件引用的 – keraba 2008-09-26 18:08:47
這需要爲每個回調分別包裝。它創造了一個擁抱e亂碼管理開銷。 – Catskul 2009-09-15 16:42:58