Here goes (untested):
template <typename HOOK, typename RET, typename... ARGS>
struct BeforeHook {
std::function<RET(ARGS...)> functor;
HOOK hook;
BeforeHook(blah) : blah {};
RET operator()(ARGS&&... args) const {
hook();
return functor(args...);
}
};
template <typename HOOK, typename RET, typename... ARGS>
BeforeHook<HOOK, RET, ARGS...> hook_before(const std::function<RET(ARGS...)> &original, HOOK hook) {
return BeforeHook<HOOK, RET, ARGS...>(original, hook);
}
Usage:
auto hooked = hook_before(original_functor, hook_functor);
hooked(args_for_original_functor); // calls hook_functor, then original_functor
Or something along those lines. The original_functor needs to be convertible to std::function
, but pretty much everything callable is. Both functors need to be cost-callable, but you could remove the const
from operator()
if you like.
If you want to experiment with returning a lambda rather than an instance of BeforeHook
, use the same trick with the template arguments RET
and ...ARGS
, and find out whether it's possible to use a template argument pack in a lambda:
template <typename HOOK, typename RET, typename... ARGS>
std::function<RET(ARGS...)> hook_before(const std::function<RET(ARGS...)> &original, HOOK hook) {
return [=](ARGS&&... args) -> RET {
hook();
return original(args...);
};
}
Either way, I think the key trick is using std::function
in a template argument deduction to separate the return type from the arguments.