使用libevent回调成员函数

发布于 2024-12-10 08:20:37 字数 1837 浏览 5 评论 0原文

我试图将一个成员函数传递给 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.

我在做什么错误的?

I am trying to pass a member function to libevent which should be treated as a callback.

#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).

Also, passing a global function (as one would do in C) on row 22 also works fine.

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)

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

拥抱没勇气 2024-12-17 08:20:37

实例方法指针需要一个实例来调用。由于 libevent 是一个 C 库,因此它不直接提供关联实例和实例方法的机制,因此您必须自己完成。 libevent 的各种事件创建函数允许您将任意数据作为回调参数传递。实例指针可以通过此参数直接传递,或者如果回调需要附加数据,则可以将实例指针与其他参数一起打包在类中。事件回调可以是自由函数或静态方法;采取哪种方法取决于类的责任(在固体、单一责任意义上)。

使用静态方法且不传递额外数据的示例:

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 中即将推出的 lambda 表达式依赖于函数调用运算符 (operator()),这在普通 C 中不受支持因此,匿名函数不适合用作 libevent 回调或任何其他 C 库回调。

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's various event creation functions let you pass arbitrary data as a callback argument. 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).

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.

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. 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 (e.g. 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.

〃安静 2024-12-17 08:20:37

您可以使用 lambda 函数,只要它有一个空的捕获列表(即 [])。您需要将 A 对象作为 void* 传递,并将其强制转换回回调中的已知类型。

#include <event.h>

class A
{
public:
    void eventcb(evutil_socket_t , short) { }
};

int main()
{
    auto *evbase = event_base_new();

    auto *instance = new A{};
    auto callback =
        [](evutil_socket_t fd, short events, void* instance)
        {
            static_cast<A*>(instance)->eventcb(fd, events);
        };

    struct event *timer2 = evtimer_new(evbase, callback, instance);
}

捕获列表必须为空,这一点很重要,否则 lambda 无法转换为函数指针。

You can use a lambda function, provided it has an empty capture list (i.e. []). You need to pass the A object as a void* and cast it back to the known type inside the callback.

#include <event.h>

class A
{
public:
    void eventcb(evutil_socket_t , short) { }
};

int main()
{
    auto *evbase = event_base_new();

    auto *instance = new A{};
    auto callback =
        [](evutil_socket_t fd, short events, void* instance)
        {
            static_cast<A*>(instance)->eventcb(fd, events);
        };

    struct event *timer2 = evtimer_new(evbase, callback, instance);
}

It's important that the capture list is empty, otherwise the lambda isn't convertible to a function pointer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文