template<class T>using type=T; // makes some declarations easier
template<class F>
struct callback_t;
template<class F, class Sig>
struct cast_helper_pvoid_last;
template<class F, class R, class... Args>
struct cast_helper_pvoid_last<F, R(Args...)> {
type<R(*)(Args..., void*)> operator()() const {
return [](Args... args, void* pvoid)->R {
auto* callback = static_cast<callback_t<F>*>(pvoid);
return callback->f( std::forward<Args>(args)... );
};
}
};
template<class F>
struct callback_t {
F f;
void* pvoid() { return this; }
template<class Sig>
auto pvoid_at_end()->decltype( cast_helper_pvoid_last<F, Sig>{}() ) {
return cast_helper_pvoid_last<F,Sig>{}();
}
};
template<class T>using decay_t=typename std::decay<T>::type;
template<class F>
callback_t<decay_t<F>> make_callback( F&& f ) { return {std::forward<F>(f)}; }
Example use:
int x = 3;
auto callback = make_callback( [&]( int y ) { return x+y; } );
int (*func)(int, void*) = callback.pvoid_at_end<int(int)>();
std::cout << func( 1, callback.pvoid() ) << "\n";
should print 4. (live example)
The lifetime of your callback_t
must exceed the pvoid
it produces.
I could auto-deduce the signature of the function pointer instead of requiring you to pass <int(int)>
(the signature without the void*
), but again that makes the code much easier.
and add this if you want the void*
to be first:
template<class F, class Sig>
struct cast_helper_pvoid_first;
template<class F, class R, class... Args>
struct cast_helper_pvoid_first<class F, R(Args...)> {
type<R(*)(void*, Args...)> operator()() const {
return [](void* pvoid, Args... args)->R {
auto* callback = static_cast<callback<F>*>(pvoid);
return callback->f( std::forward<Args>(args)... );
};
}
};
// inside the callback_t<?> template:
template<class Sig>
auto pvoid_at_start()->decltype( cast_helper_pvoid_first<F, Sig>{}() ) {
return cast_helper_pvoid_first<F,Sig>{}();
}
doing a void*
in the middle gets trickier.
If you have calling convention issues, then we need to use a helper object
Have pvoid_at_end
return a cast_helper_pvoid_last
instead of calling ()
on it.
Then, add operator
overloads to cast-to-function-pointer of each calling convention you need to support. The body is identical to the operator()
, as the lambda should support any of them.
Alternatively, with some C++14 support you can change the return type of operator()
to auto
, and leave the code otherwise intact, and rely on direct cast-from-lambda to get the calling convention right.