Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrapping a callable that may be void return

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:

  1. template<class T, typename... Args> decltype(auto) wrap(std::function<T(Args...)>, Args&... args)
  2. 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?

like image 360
Bailey Parker Avatar asked Jul 21 '18 08:07

Bailey Parker


People also ask

Can you return on a void function?

Void functions do not have a return type, but they can do return values.

Do void functions need a return statement?

Within the body of the method, you use the return statement to return the value. Any method declared void doesn't return a value. It does not need to contain a return statement, but it may do so.

How do you return a void in Python?

In Python, it is possible to compose a function without a return statement. Functions like this are called void, and they return None, Python's special object for "nothing". Here's an example of a void function: >>> def sayhello(who): print 'Hello,', who + '!'

Does a void function need a return statement in C++?

A void function will automatically return to the caller at the end of the function. No return statement is required. Do not put a return statement at the end of a non-value returning function. In the above program, the value to be printed needs to be provided on the right-side of the std::cout << .


1 Answers

The way I like to solve this problem is with regular void. Well, we don't actually have proper regular void, but we can make our own type Void that is regular, that is kind of like void. And then provide a wrapper around invoke that understands that.

The shortest version (there's more detail in that blog):

struct Void { };

template <typename F, typename ...Args,
    typename Result = std::invoke_result_t<F, Args...>,
    std::enable_if_t<!std::is_void_v<Result>, int> = 0>
Result invoke_void(F&& f, Args&& ...args) {
    return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}

// void case
template <typename F, typename ...Args,
    typename Result = std::invoke_result_t<F, Args...>,
    std::enable_if_t<std::is_void_v<Result>, int> = 0>
Void invoke_void(F&& f, Args&& ...args) {
    std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
    return Void();
}

This lets you, in your original code, do:

template<class F, typename... Args>
decltype(auto) wrap(F&& func, Args&... args) {
    // Do some stuff before
    auto result = invoke_void(std::forward<decltype(func)>(func),
                              std::forward<Args>(args)...);
    // Do some stuff after
    return result;
}

And this works, even if func returns void.

like image 152
Barry Avatar answered Nov 02 '22 06:11

Barry