4

典型的なクラスを考えると:

なんでも構造体
{
    void Doit();
};

なんでもw;

pthread_create() やシグナル ハンドラなどの C void* ベースのコールバックによって呼び出されるメンバー関数を取得する最良の方法は何ですか?

pthread_t pid;

pthread_create(&pid, 0, ... &w.Doit() ... );
4

9 に答える 9

6

ほとんどの 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));
于 2008-09-26T15:30:00.587 に答える
6

最も簡潔な解決策は、すべてのコードで共有されるヘッダー ファイルで次のように定義することです。

テンプレート <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);

これは、メソッドがクラスのコード内から参照されている限り、メソッドがプライベートであっても機能します。(そうでなければ、なぜコードがプライベート メソッドを参照しているのか不思議に思う必要があります。)

于 2008-09-26T19:51:32.487 に答える
3

メンバー関数はプライベートですか? そうでない場合は、標準的な慣用句を使用します。

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;
}
于 2008-09-26T15:34:58.517 に答える
3

次のように C 関数ラッパーを使用します。

struct Whatever
{
    void Doit();
};

extern "C" static int DoItcallback (void * arg)
{
  Whatever * w = (Whatever *) arg;
  w->DoIt();
  return something;
}

何らかの方法でクラスへのポインターを渡すことができる場合にのみ機能します。ほとんどのコールバック メカニズムでこれが可能です。

Afaikこれがこれを行う唯一の方法です。多くのハッキングなしでは、C からメソッドを直接呼び出すことはできません。

于 2008-09-26T15:30:48.807 に答える
1

注意すべきことの 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 を呼び出して、キャッチできることを期待しないでください。

于 2008-09-26T20:10:31.037 に答える
1

メンバー関数は静的でなければなりません。非静的には暗黙の「this」引数があります。静的メンバーがインスタンスを取得できるように、Whatever インスタンスへのポインターを void* として渡します。

于 2008-09-26T15:29:31.790 に答える
1

「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;
}
于 2008-09-26T16:21:14.927 に答える
0

C から使用したことはありませんが、コールバックを行うために、libsigc++を確認することを強くお勧めします。これはまさに、C++ コールバックを行うときに何度も必要としていたものです。

于 2008-09-26T15:33:13.293 に答える
0

このリンクを参照してください

「非静的メンバーへのポインターは、クラス オブジェクトの this ポインターを渡す必要があるため、通常の C 関数ポインターとは異なります。したがって、通常の関数ポインターと [ポインターへの] 非静的メンバーメンバー関数には、異なる互換性のないシグネチャがあります」

于 2008-09-26T15:30:11.070 に答える