The most efficient way is to voidify the lambda directly.
#include <iostream>
#include <tuple>
#include <memory>
template<class...Args>
struct callback {
void(*function)(void*, Args...)=nullptr;
std::unique_ptr<void, void(*)(void*)> state;
};
template<typename... Args, typename Lambda>
callback<Args...> voidify( Lambda&& l ) {
using Func = typename std::decay<Lambda>::type;
std::unique_ptr<void, void(*)(void*)> data(
new Func(std::forward<Lambda>(l)),
+[](void* ptr){ delete (Func*)ptr; }
);
return {
+[](void* v, Args... args)->void {
Func* f = static_cast< Func* >(v);
(*f)(std::forward<Args>(args)...);
},
std::move(data)
};
}
void register_callback( void(*function)(void*), void * p ) {
function(p); // to test
}
void test() {
int x = 0;
auto closure = [&]()->void { ++x; };
auto voidified = voidify(closure);
register_callback( voidified.function, voidified.state.get() );
register_callback( voidified.function, voidified.state.get() );
std::cout << x << "\n";
}
int main() {
test();
}
here voidify takes a lambda and (optionally) a list of arguments, and generates a traditional C-style callback-void* pair. The void* is owned by a unique_ptr with a special deleter so its resources are properly cleaned up.
The advantage of this over a std::function solution is efficiency -- I eliminated one level of run-time indirection. The lifetime that the callback is valid is also clear, in that it is in the std::unique_ptr<void, void(*)(void*)> returned by voidify.
unique_ptr<T,D>s can be moved into shared_ptr<T> if you want a more complex lifetime.
The above mixes lifetime with data, and type erasure with utility. We can split it:
template<class Lambda, class...Args>
struct callback {
void(*function)(void*, Args...)=nullptr;
Lambda state;
};
template<typename... Args, typename Lambda>
callback<typename std::decay<Lambda>::type, Args...> voidify( Lambda&& l ) {
using Func = typename std::decay<Lambda>::type;
return {
+[](void* v, Args... args)->void {
Func* f = static_cast< Func* >(v);
(*f)(std::forward<Args>(args)...);
},
std::forward<Lambda>(l)
};
}
Now voidify does not allocate. Simply store your voidify for the lifetime of the callback, passing a pointer-to-second as your void* along side the first function pointer.
If you need to store this construct off the stack, converting the lambda to a std::function may help. Or use the first variant above.
void test() {
int x = 0;
auto closure = [&]()->void { ++x; };
auto voidified = voidify(closure);
register_callback( voidified.function, &voidified.state );
register_callback( voidified.function, &voidified.state );
std::cout << x << "\n";
}