I have the function GetThing
as follows:
auto GetThing(size_t index, auto&& l1)
{
return l1;
}
auto GetThing(size_t index, auto&& l1, auto&&... rest)
{
if (index == 0)
return l1;
return GetThing(index - 1, rest...);
}
I want it to be able to work with different lambdas too while being able to handle other types (meaning non-lambdas, non functions, like int
, and ...) , such as
std::cout << GetThing(1, 2, 3, 4); //works, return 3
std::cout << GetThing(1, [] {return 0; },
[] {return 1; }, [] {return 2; },
[] {return 3; } )(); //nope
But the problem here being the lambdas are different type, therefore the recursive function will deduced to incompatible return type, so I seems to have to use std::function
like this, but it's ugly.
std::cout << GetThing(1, std::function{ [] {return 0; } }, std::function{ [] {return 1; } }, std::function{ [] {return 2; } }, std::function{ [] {return 3; } })();//works
Any possible way to get around this, for example if there is an overloaded operator()
then it automatically enforce the type to be std::function
?
EDIT: I am aware of capture-less lambdas can be converted to a function pointer, but how to deduce it that way without std::decay
in the template? Because I still want to to be handle other types as references
EDIT2: I receive a few answers utilizing std::variant
, and am thinking of that, besides lambda, the parameter types shall be the same, eg. std::variant<int, int, int>
. It maybe possible to add overload to GetThing
, such that whenstd::variant
is holding the same types, it return the thing of that type, otherwise (which is the case of receiving lambdas), returns a std::function
You may store your functions in an array of variants. This comes with some overhead of course. But this enables to have functions also using captured vars.
This enable to pick a function from such function collection and execute it with given parms as follows:
template < typename ARR_T >
struct Collect
{
template < typename ... T >
Collect( T&&...args ): arr{std::forward<T>(args)...}{}
ARR_T arr;
using VARIANT_T = ARR_T::value_type;
VARIANT_T& operator[]( size_t index) { return arr[index]; }
};
template < typename ... T >
Collect( T&& ... args ) -> Collect< std::array< std::variant<T... >, sizeof...(T) >>;
template < typename C, typename ... PARMS >
auto GetThing( size_t index, C&& c, PARMS&&... parms )
{
return std::visit( [ &parms...]( auto&& func)
{
return func(std::forward<PARMS>(parms)...);
}, c[index]);
}
int main()
{
std::cout << GetThing( 2, Collect( []( int, double) {return 0; }, []( int, double) {return 1; }, []( int, double) {return 2; }, []( int, double) {return 3; }), 1,5.6)<< std::endl;
int y = 8;
double d = 9.99;
std::cout << GetThing( 0, Collect( [y,d]( int, double) {return d*y; }, []( int, double) {return 1.; }, []( int, double) {return 2.; }, []( int, double) {return 3.; }), 1,5.6)<< std::endl;
}
In this case GetThing
also take the function parameters for calling the lambda, because the call is using std::visit
. If you "only" want to pick the function, you will get the std::variant
if you like and can call the function your self.
auto func = Collect( []( int i, double d) {return d+i; }, []( int i, double d) {return d*i; }, []( int i, double d) {return d-i; } )[2];
std::cout << std::visit( []( auto&& f) { return f( 9, 7.77 ); }, func ) << std::endl;
}
You can return a std::variant
that contains all input types:
template <typename... Args>
std::variant<std::decay_t<Args>...>
GetThing(std::size_t index, Args&&... args)
{
return [index, t=std::forward_as_tuple(std::forward<Args>(args)...)]
<std::size_t... Is>(std::index_sequence<Is...>) {
return std::array{ +[](const std::tuple<Args&&...>& t) {
return std::variant<std::decay_t<Args>...>{
std::in_place_index<Is>, std::get<Is>(t)};
} ...
}[index](t);
}(std::index_sequence_for<Args...>{});
}
Then you need std::visit
to visit your returned value:
for (std::size_t index = 0; index < 4; index++)
std::visit(
[](auto&& f) { std::cout << f() << " "; },
GetThing(index, []{return 0;}, []{return 1;}, []{return 2;}, []{return 3;})
);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With