I have a step(f)
function that:
Executes some code before calling f
.
Calls f()
.
Executes some code after calling f
.
Returns f
's result value if f
does not return void
.
The small piece of code implementing step
bothers me more than it should, because of the fourth point described above:
template <typename TF>
auto step(TF&& f)
{
// Execute some actions before `f`.
do_something();
using f_return_type = decltype(f());
return static_if(std::is_same<f_return_type, void>{})
.then([](auto&& xf) mutable
{
// Do not return anything if `xf` returns void.
xf();
// Execute some actions after `f`.
do_something_after_f();
})
.else_([](auto&& xf) mutable
{
auto result = xf();
// Execute some actions after `f`.
do_something_after_f();
return result;
})(std::forward<TF>(f));
}
(Note the repetition of the call to f
and to do_something_after_f
.)
I have to use some kind of conditional compile-time (either template specialization or static_if
, as shown in the example) to branch depending on f
's return type.
Ideally, I would like this to compile:
template <typename TF>
auto step(TF&& f)
{
// Execute some actions before `f`.
do_something();
decltype(auto) eventual_result = f();
// Execute some actions after `f`.
do_something_after_f();
return result;
}
But it doesn't, because eventual_result
can be void
, which is an incomplete type.
Is there any way of refactoring this code in order to avoid the repetition in calling f()
and do_something_after_f()
?
You can have do_something_after_f()
run after the return statement by putting it into the destructor of a local variable.
struct Cleanup {
~Cleanup() { do_something_after_f(); }
} cleanup;
return f(); // it's legal to return a void expression in C++
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