I\'m trying to register a callback in a C-API that uses the standard function-pointer+context paradigm. Here\'s what the api looks like:
void register_callba
The most efficient way is to voidify the lambda directly.
#include
#include
#include
template
std::pair< void(*)(void*, Args...), std::unique_ptr > voidify( Lambda&& l ) {
typedef typename std::decay::type Func;
std::unique_ptr data(
new Func(std::forward(l)),
+[](void* ptr){ delete (Func*)ptr; }
);
return {
+[](void* v, Args... args)->void {
Func* f = static_cast< Func* >(v);
(*f)(std::forward(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.first, voidified.second.get() );
register_callback( voidified.first, voidified.second.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 returned by voidify.
unique_ptrs can be moved into shared_ptr if you want a more complex lifetime.
The above mixes lifetime with data, and type erasure with utility. We can split it:
template
std::pair< void(*)(void*, Args...), std::decay_t > voidify( Lambda&& l ) {
typedef typename std::decay::type Func;
return {
+[](void* v, Args... args)->void {
Func* f = static_cast< Func* >(v);
(*f)(std::forward(args)...);
},
std::forward(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.