2

コールバックとして扱われるべきメンバー関数を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へのメソッド ポインターを正常に作成し、それを A のインスタンス (行 20) で呼び出すことができます。

また、行 22 でグローバル関数を (C で行うように) 渡すことも正常に機能します。

ただし、行 23 で、メソッド ポインターをlibeventに渡そうとしました。これをコンパイルすると、( clangコンパイラーを使用して) 次のエラーが発生します。

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.

私は何を間違っていますか?

4

1 に答える 1

5

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

静的メソッドを使用し、追加のデータを渡さない例:

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::handle_timeoutは 内からのみ呼び出されるためA::invoke_timer_handler、非公開または保護することができます。

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

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

于 2011-11-03T02:17:07.250 に答える