I have C++ code that wraps an arbitrary lambda and returns the result of the lambda.
template <typename F> auto wrapAndRun(F fn) -> decltype(F()) { // foo(); auto result = fn(); // bar(); return result; } This works unless F returns void (error: variable has incomplete type 'void'). I thought of using a ScopeGuard to run bar, but I don't want bar to run if fn throws. Any ideas?
P.S. I found out later there's a proposal to fix this inconsistency.
You can write a simple wrapper class that handles this part of it:
template <class T> struct CallAndStore { template <class F> CallAndStore(F f) : t(f()) {} T t; T get() { return std::forward<T>(t); } }; And specialize:
template <> struct CallAndStore<void> { template <class F> CallAndStore(F f) { f(); } void get() {} }; You can improve usability with a small factory function:
template <typename F> auto makeCallAndStore(F&& f) -> CallAndStore<decltype(std::declval<F>()())> { return {std::forward<F>(f)}; } Then use it.
template <typename F> auto wrapAndRun(F fn) { // foo(); auto&& result = makeCallAndStore(std::move(fn)); // bar(); return result.get(); } Edit: with the std::forward cast inside get, this also seems to handle returning a reference from a function correctly.
The new C++17 if constexpr addition may be helpful here. You can choose whether to return fn()'s result at compile-time:
#include <type_traits> template <typename F> auto wrapAndRun(F fn) -> decltype(fn()) { if constexpr (std::is_same_v<decltype(fn()), void>) { foo(); fn(); bar(); } else { foo(); auto result = fn(); bar(); return result; } } As you said C++2a is an option as well, you could also make use of concepts, putting a constraint on the function:
template <typename F> requires requires (F fn) { { fn() } -> void } void wrapAndRun(F fn) { foo(); fn(); bar(); } template <typename F> decltype(auto) wrapAndRun(F fn) { foo(); auto result = fn(); bar(); return result; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With