Reputation: 3640
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
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