典型的なクラスを考えると:
なんでも構造体 { void Doit(); }; なんでもw;
pthread_create() やシグナル ハンドラなどの C void* ベースのコールバックによって呼び出されるメンバー関数を取得する最良の方法は何ですか?
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 を返します。 }
*
おそらく、サンクが void と voidを返すバージョンと、メンバー関数が void と void を返すバージョンの 4 つのバージョンを定義する必要があります*
。そうすれば、コンパイラは状況に応じて最適なものを一致させることができます (実際、すべてが一致しない場合はエラーが発生します)。
次に、これらの状況のいずれかに遭遇するたびに入力する必要があるのは、次のとおりです。
pthread_create(&pid, 0, &thunk<なんでも, &Whatever::doit>, &w);
これは、メソッドがクラスのコード内から参照されている限り、メソッドがプライベートであっても機能します。(そうでなければ、なぜコードがプライベート メソッドを参照しているのか不思議に思う必要があります。)
メンバー関数はプライベートですか? そうでない場合は、標準的な慣用句を使用します。
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;
}
次のように C 関数ラッパーを使用します。
struct Whatever
{
void Doit();
};
extern "C" static int DoItcallback (void * arg)
{
Whatever * w = (Whatever *) arg;
w->DoIt();
return something;
}
何らかの方法でクラスへのポインターを渡すことができる場合にのみ機能します。ほとんどのコールバック メカニズムでこれが可能です。
Afaikこれがこれを行う唯一の方法です。多くのハッキングなしでは、C からメソッドを直接呼び出すことはできません。
注意すべきことの 1 つは、次のようなコードを記述する場合です。
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 ブロックで 1 つの C 呼び出しを確認し、「古き良き C であるためスローできない C 関数を呼び出しています! Calloo-cally ! try/catch に到達することはないため、try/catch の痕跡をすべて削除します。
愚かなコンパイラ。
コールバックする C を呼び出して、キャッチできることを期待しないでください。
メンバー関数は静的でなければなりません。非静的には暗黙の「this」引数があります。静的メンバーがインスタンスを取得できるように、Whatever インスタンスへのポインターを 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 から使用したことはありませんが、コールバックを行うために、libsigc++を確認することを強くお勧めします。これはまさに、C++ コールバックを行うときに何度も必要としていたものです。
「非静的メンバーへのポインターは、クラス オブジェクトの this ポインターを渡す必要があるため、通常の C 関数ポインターとは異なります。したがって、通常の関数ポインターと [ポインターへの] 非静的メンバーメンバー関数には、異なる互換性のないシグネチャがあります」