Search code examples
c++lambdaclosuresc++14function-pointers

Is it possible to wrap C-style callbacks so that you can use C++ lambdas with them?


I am in the unfortunate position where some C++ code needs to talk with external libraries written in C. To be specific, the Enlightenment suite, which is used, amongst other places, to handle GUIs on certain smart watches.

As an example, let's take the function ecore_timer_add():

EAPI Ecore_Timer * ecore_timer_add  (double interval, Eina_Bool(*function)(void *), const void *data)

When called, this will call function() after interval seconds, passing data as a single parameter to it.

In C, this is the only way to have a generic function callback, since there is no function overloading and no templating. However, in C++, we usually use lambdas, or possibly tools like std::function to handle callbacks.

But how can we mix these techniques?

Capturing lambdas are not convertible to a function pointer (which makes sense, since they have to pass along their internally captured state somehow).

However, maybe it is possible to create a 'wrapper' function that does not capture, which receives the lambda packed in its void * data argument, and then is able to call this?

I have tried writing this, but have not been successful so far.

It probably requires some advanced template trickery to allow the compiler to figure out how to cast the lambda to/from the void *.


Solution

  • Just add another level of indirection:

    auto f = [=]() { /* ... */ };
    ecore_timer_add(interval, [](void *data) { return (*reinterpret_cast<decltype(f)*>(data))(); }, &f);
    

    Be wary of lifetime issues here (might want to stuff the capturing lambda f into a std::function and store it in a container somewhere so you can maintain a valid pointer to it).