Say I have some wrapper function, in which I'd like to do some setup, call a callback (and save its result), do some cleanup, and then return what the callback returned:
#include <functional>
#include <utility>
template<class F, typename... Args>
decltype(auto) wrap(F&& func, Args&... args) {
// Do some stuff before
auto result = std::invoke(std::forward<decltype(func)>(func),
std::forward<Args>(args)...);
// Do some stuff after
return result;
}
A practical example of this is a timer utility function that returns the elapsed time of the function call as well as its return value (in a tuple, perhaps).
Such a function works fine for callables with return types:
void foo() {
auto a = 1;
wrap([](auto a) { return 1; }, a);
}
But with a callable with void return type, during the template specialization the compiler complains that auto result
has incomplete type void:
void foo() {
auto a = 1;
wrap([](auto a) {}, a);
}
This makes sense of course, because while you can return void()
, you can't store it in a variable.
I want wrap
to work for both kinds of callables. I've attempted using std::function
to give two signatures for wrap
:
template<class T, typename... Args> decltype(auto) wrap(std::function<T(Args...)>, Args&... args)
template<typename... Args> decltype(auto) wrap(std::function<void(Args...)>, Args&... args)
The first of those will continue to match callables with a non-void
return, but the latter fails to match those with return type void
.
Is there a way to make wrap work in both the return type void
and non-void
callable cases?