Function template accept and return different lambdas

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;})
  );

For captureless lambdas you can use function pointers

#include <utility> // std::forward

auto GetThing(size_t index, auto&& l1)
{
    return  std::forward<decltype(l1)>(l1);
}

auto GetThing(size_t index, auto&& l1, auto&&... rest)
{
    if (index == 0)
        return std::forward<decltype(l1)>(l1);
    return GetThing(index - 1, std::forward<decltype(rest)>(rest)...);
}

std::cout << GetThing(1,
+[] {return 0; }, +[] {return 1; }, 
+[] {return 2; }, +[] {return 3; } 
)();// works now

will work: Demo

Also note that, you need to add + for converting the lambda to function pointer. Read more: A positive lambda: '+[]{}' - What sorcery is this?


Also, in case of lambda with capture you need to use std::function. The above one will not be enough/will not work!

Tags:

C++

C++20