简体   繁体   English

使用libevent回调成员函数

[英]Callback to member function using libevent

I am trying to pass a member function to libevent which should be treated as a callback. 我正在尝试将成员函数传递给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;
}   

I can successfully create a method pointer to eventcb in class A and call it on an instance of A (row 20). 我可以在类A中成功创建一个指向eventcb的方法指针, 在A的实例(第20行)上调用它。

Also, passing a global function (as one would do in C) on row 22 also works fine. 同样,在第22行传递全局函数(就像在C中一样)也可以正常工作。

However, on row 23, I attempt to pass my method pointer to libevent , and when I compile this I get the following error (using the clang compiler) 但是,在第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.

What am I doing wrong? 我究竟做错了什么?

Instance method pointers need an instance to be invoked on. 实例方法指针需要实例被调用。 Since libevent is a C library, it doesn't directly provide a mechanism to associate an instance and an instance method, so you'll have to do it yourself. 由于libevent是C库,因此它不直接提供将实例和实例方法相关联的机制,因此您必须自己做。 libevent's various event creation functions let you pass arbitrary data as a callback argument. libevent的各种事件创建函数使您可以传递任意数据作为回调参数。 The instance pointer can be passed via this argument, either directly or packaged in a class with other arguments if the callback takes additional data. 实例指针可以通过此参数直接传递,也可以与其他参数一起包装在类中(如果回调需要其他数据)。 The event callback can be a free function or a static method; 事件回调可以是自由函数或静态方法。 which approach to take depends on the class's responsibility (in the SOLID, single-responsibilty sense). 采取哪种方法取决于班级的责任(就SOLID而言,是单一责任)。

An example using a static method and passing no additional data: 一个使用静态方法并且不传递任何其他数据的示例:

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;
    }
}

In the example, since A::handle_timeout is only called from within A::invoke_timer_handler , it could be made private or protected. 在该示例中,由于A::handle_timeout内部调用A::invoke_timer_handler ,因此可以将其设为私有或受保护。

The sample has very basic memory management. 该示例具有非常基本的内存管理。 In general, the code must ensure the instance (and other callback arguments, if the callback argument isn't simply an A* ) exists for the lifetime of the event to prevent access errors. 通常,代码必须在事件的生存期内确保实例(以及其他回调参数,如果该回调参数不仅仅是A* )存在,以防止访问错误。 It should also ensure the instance doesn't leak once the event is no longer needed. 它还应确保一旦不再需要该事件,该实例就不会泄漏。 If the instance owns the event, memory management is relatively straightforward. 如果实例拥有事件,则内存管理相对简单。 Concurrency can also add complication that affect memory management. 并发还可能增加影响内存管理的复杂性。

Existing code-level implementations of anonymous functions (eg boost::lambda) and the forthcoming lambda expressions from C++11 rely on the function call operator ( operator() ), which is unsupported in plain C. Thus anonymous functions are unsuitable for use as libevent callbacks or any other C-library callbacks. 匿名函数的现有代码级实现(例如boost :: lambda)和C ++ 11中即将出现的lambda表达式依赖于函数调用运算符( operator() ),普通C语言不支持该函数。因此,匿名函数不适合用作libevent回调或任何其他C库回调。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM