Sergey Kolesnik
Sergey Kolesnik

Reputation: 3640

Is there a simple way to pass result of a callable when it returns void?

Here's a simple syntetic example: https://godbolt.org/z/MKWh9a464

#include <iostream>

template <typename ... Args>
void foo(Args ...){
    std::cout << sizeof...(Args) << '\n';
}

void foo(void){
    std::cout << "void\n";
}

template <typename Callable>
void bar(Callable c){
    foo(c());
}

int main() 
{
    bar([](){
        return 42;
    });

    bar([](){});

    return 0;
}

It results in error:

<source>: In instantiation of 'void bar(Callable) [with Callable = main()::<lambda()>]':
<source>:23:8:   required from here
<source>:14:10: error: invalid use of void expression
   14 |     foo(c());
      |         ~^~
ASM generation compiler returned: 1
<source>: In instantiation of 'void bar(Callable) [with Callable = main()::<lambda()>]':
<source>:23:8:   required from here
<source>:14:10: error: invalid use of void expression
   14 |     foo(c());
      |         ~^~

I wrote some SFINAE soultion, but it is kind of ugly: https://godbolt.org/z/4r9b4h34r


#include <type_traits>
#include <utility>

struct void_result{};

template <typename Result>
struct _get_result
{
    template <typename Callable, typename ... Args>
    constexpr decltype(auto) operator()(Callable c, Args &&... args) const noexcept {
        return c(std::forward<Args>(args)...);
    }
};

template <>
struct _get_result<void>
{
    template <typename Callable, typename ... Args>
    constexpr void_result operator()(Callable c, Args &&... args) const noexcept {
        c(std::forward<Args>(args)...);
        return {};
    }
};

template <typename Callable, typename ... Args>
constexpr decltype(auto) voidable(Callable c, Args && ... args) noexcept {
    return _get_result<std::invoke_result_t<Callable, Args...>>{}(c, std::forward<Args>(args)...);
}


#include <iostream>

template <typename ... Args>
void foo(Args ...){
    std::cout << sizeof...(Args) << '\n';
}

void foo(void_result){
    std::cout << "void\n";
}

template <typename Callable, typename ... Args>
void bar(Callable c, Args && ...args){
    foo(voidable(c, std::forward<Args>(args)...));
}

int main() 
{
    bar([](){
        return 42;
    });

    bar([](){});

    return 0;
}

Is there a simpler way to pass the result when it is void?

Upvotes: 4

Views: 67

Answers (1)

Pepijn Kramer
Pepijn Kramer

Reputation: 12891

You can use if constexpr like this, I let lambda capture do the args capture (demo : https://onlinegdb.com/LK2fzamlX)

#include <iostream>

template<typename fn_t>
auto meta_function(fn_t fn) -> decltype(fn())
{
    using retval_t = decltype(fn());

    if constexpr (std::is_same_v<void, retval_t>)
    {
        std::cout << "void code\n";
        return;
    }
    else
    {
        retval_t retval = fn();
        std::cout << "retval inside meta_function = " << retval << "\n";
        return retval;
    }
}

int main()
{
    auto retval = meta_function([] {return 42; });
    std::cout << "retval returned from meta_function = " << retval << "\n";
    meta_function([] {});
    return 0;
}

Upvotes: 4

Related Questions