Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return `f`'s result if `f` has a non-void return type - how to refactor this pattern?

I have a step(f) function that:

  1. Executes some code before calling f.

  2. Calls f().

  3. Executes some code after calling f.

  4. 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()?

like image 421
Vittorio Romeo Avatar asked May 27 '16 18:05

Vittorio Romeo


1 Answers

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++
like image 183
Brian Bi Avatar answered Oct 23 '22 14:10

Brian Bi