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?
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:
In the example, since
A::handle_timeoutis only called from withinA::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.