How can you call a function from a std::variant
of different return type function pointers?
For example, if I dont use std::variant
(assuming the function return type is void
):
void print()
{
std::cout << "Hello World!" << std::endl;;
}
void run_func(void (*func)())
{
func();
}
int main()
{
run_func(print);
}
Output:
Hello World!
(as expected)
How can this be done while using a std::variant
of return types, so that i can call functions of different return types.
For example:
void print()
{
std::cout << "Hello World!" << std::endl;
}
void run_func(std::variant<void (*)(), int (*)(), unsigned int (*)()> func)
{
func(); // will return whatever type it is (void in this case)
}
int main()
{
run_func(print);
}
The code above is non-functional but gives an idea of what im trying to explain.
CodePudding user response:
You must visit your variant:
void run_func(std::variant<void (*)(), int (*)(), unsigned int (*)()> variant)
{
std::visit([](auto fptr){fptr();}, variant);
}
Live C 17 demo
If you actually want to get the int
or unsigned int
back out, we can return another variant as a result of calling run_func
. I'll be using C 20 because it's more straightforward thanks to template parameter support for lambdas:
uto run_func(std::variant<void (*)(), int (*)(), unsigned int (*)()> variant)
-> std::variant<std::monostate, int, unsigned int>
{
using ret_t = std::variant<std::monostate, int, unsigned int>;
return std::visit([]<class Ret>(Ret (*fptr)()) -> ret_t
{
if constexpr (std::is_same_v<void, Ret>)
{
fptr();
return std::monostate{};
}
else
{
return fptr();
}
}, variant);
}
And we can call it like so:
int get_answer(){return 42;}
// ...
std::visit([](auto i){
if constexpr(!std::is_same_v<decltype(i), std::monostate>)
std::cout << i << std::endl;
}, run_func(&get_answer));
C 20 demo
With some more work, we could probably generalize run_func
to accept any number of different function pointer types, and return an appropriate variant
CodePudding user response:
std::variant<>
contains exactly one of the types listed, and the type stored is known (and can be changed) runtime. Therefore, you cannot make the return type dependent on it, unless that's also a variant (void intentionally skipped):
std::variant<int, unsigned> run_func(int (*)(), unsigned int (*)()> func)
{ { std::visit([&](auto f) -> std::variant<int, unsigned> { return f(); }, func); }
}
However, if all you need is to be able to process different outputs from a function, it's sometimes beneficial to use continuation passing style. It means, instead of returning, you accept a lambda that processes the output:
template<typename Cont>
void run_func(int (*)(), unsigned int (*)()> func, Cont cont)
{ std::visit([&](auto f) { cont(f); }, func); }
CodePudding user response:
You could use a placeholder parameter with auto
:
#include <iostream>
#include <functional>
void print()
{
std::cout << "Hello World!" << std::endl;;
}
void print_int(int i)
{
std::cout << i << std::endl;
}
void run_func(auto f)
{
f();
}
int main()
{
run_func(print);
run_func(std::bind(print_int, 1));
}
Output:
Hello World!
1
EDIT: just realized you asked specifically about std::variant
, sorry, my answer might not be relevant