2011-10-16 10 views
2

コールバックとして扱われるlibeventにメンバー関数を渡そうとしています。libeventを使用したメンバー関数へのコールバック

#include <event.h> 

class A 
{ 
    public: 
     void eventcb(evutil_socket_t fd, short events, void *ctx) { } 
}; 


static void global_eventcb(evutil_socket_t fd, short events, void *ctx) { } 

typedef void (A::*mthd)(evutil_socket_t, short, void*); 

int main(void) 
{ 
    struct event_base *evbase = event_base_new(); 

    mthd eventcb = &A::eventcb; 
    A *instance = new A; 
    (instance->*eventcb)(NULL, 0, NULL); 

    struct event *timer1 = evtimer_new(evbase, global_eventcb, NULL); 
    struct event *timer2 = evtimer_new(evbase, (instance->*eventcb), NULL); 

    return 0; 
} 

私は正常クラスAにおけるeventcbにメソッドポインタを作成し(行20)のインスタンスでそれを呼び出すことができます。

また、22行目のグローバル関数(Cと同じように)を渡すとうまく動作します。

しかし、私は何をしています

example.cpp:23:25: error: no matching function for call to 'event_new' 
     struct event *timer2 = evtimer_new(evbase, (instance->*eventcb), NULL); 
           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
In file included from example.cpp:1: 
In file included from /usr/local/include/event.h:71: 
/usr/local/include/event2/event.h:749:40: note: instantiated from: 
#define evtimer_new(b, cb, arg)  event_new((b), -1, 0, (cb), (arg)) 
             ^~~~~~~~~ 
/usr/local/include/event2/event.h:833:15: note: candidate function not viable: no know conversion from '<bound member function type>' to 'event_callback_fn' 
     (aka 'void (*)(int, short, void *)') for 4th argument 
struct event *event_new(struct event_base *, evutil_socket_t, short, event_callback_fn, void *); 
      ^
1 error generated. 

、行23で、私はlibeventに私のメソッドポインタを渡そうとすると、私はこれをコンパイルするとき、私は(打ち鳴らすコンパイラを使用して)、次のエラーを取得します間違っている?

+0

[create_pthread()呼び出しのキャストメンバ関数]の複製が可能です。(http://stackoverflow.com/questions/6826620/cast-member-function-for-create-pthread-call) – Mat

+0

「正確な重複"答えはまったく同じです。それを直接行うことはできません。メンバ関数へのポインタは、通常のポインタへのポインタと互換性がありません。あなたはそれを動作させるためにいくつかのグルーコードが必要です。 – Mat

+0

Allright、私は私の現在の解決策に固執しなければならないと思います。コールバック(グローバル)にAのインスタンスを渡してから、(A *)ctx) - > eventcb (...)。 あなたの返信ありがとう –

答えて

5

インスタンスメソッドポインタは、呼び出されるインスタンスを必要とします。 libeventはCライブラリであるため、インスタンスとインスタンスメソッドを関連付けるメカニズムを直接提供するわけではありません。したがって、自分で行う必要があります。 libeventのさまざまなイベント作成関数を使用すると、任意のデータをコールバック引数として渡すことができます。インスタンスポインタは、この引数を介して直接渡すことも、コールバックが追加のデータを受け取った場合に他の引数を持つクラスにパッケージ化することもできます。イベントコールバックは、フリー関数または静的メソッドにすることができます。どのアプローチを取るかは、クラスの責任(SOLID、単一責任の意味で)に依存します。

追加データを静的メソッドを使用していないと通過例:A::handle_timeout以降の例で

class A { 
public: 
    A(struct event_base *); 

    bool start_timer(); 

    static void invoke_timer_handler(evutil_socket_t fd, short events, void *ctx); 
    void handle_timeout(evutil_socket_t fd, short events); 

protected: 
    struct event_base *evbase; 
    struct event *timer; 
}; 

A::A(struct event_base *event_base) : evbase(event_base), timer(NULL) {} 

bool A::start_timer() { 
    // not thread safe. 
    if (! timer) { 
     timer = evtimer_new(evbase, &A::invoke_timer_handler, this); 
     return true; 
    } 
    return false; 
} 

void A::invoke_timer_handler(evutil_socket_t fd, short events, void *ctx) { 
    (static_cast<A*>(ctx))->handle_timeout(fd, events); 
} 

void A::handle_timeout(evutil_socket_t fd, short events) { 
    ... 
    if (evtimer_del(timer)) { 
     // error deleting event 
     ... 
    } else { 
     timer=NULL; 
    } 
} 

は、唯一A::invoke_timer_handler内から呼び出され、それは、プライベートまたは保護作ることができます。

サンプルには非常に基本的なメモリ管理機能があります。一般的に、コードでは、アクセスエラーを防ぐために、イベントの存続期間中インスタンス(およびコールバック引数が単にA*でない場合は他のコールバック引数)が存在することを保証する必要があります。イベントがもはや必要でなくなったら、インスタンスが漏れないようにする必要があります。インスタンスがイベントを所有している場合、メモリ管理は比較的簡単です。並行処理は、メモリ管理に影響を与える複雑さを加えることもあります。

匿名関数(boost :: lambdaなど)とC++ 11の今後のラムダ式の既存のコードレベル実装は、関数呼び出し演算子(operator())に依存しています。これはプレーンCではサポートされていません。 libeventコールバックやその他のCライブラリコールバックとして使用するには不適切です。

関連する問題