A way to make a apply_on_each()
that receive a lambda (or a function) that receive an undefined number of generic arguments and call they (partially) unfolding in a C++17 way.
To be honest, it's only the generalization of the Bolov's voodoo answer.
First of all, a set of constexpr
functions to detect the number of arguments of a function (supposing the arguments are generic, so supposing a list of integer zeros is acceptable)
template <typename F, typename ... Ts>
constexpr auto numArgsH (int, Ts ... ts)
-> decltype( std::declval<F>()(ts...), std::size_t{} )
{ return sizeof...(Ts); }
template <typename F, typename ... Ts>
constexpr auto numArgsH (long, Ts ... ts)
{ return numArgsH<F>(0, 0, ts...); }
template <typename F>
constexpr auto numArgs ()
{ return numArgsH<F>(0); }
Now the apply_on_each()
function that detect the number of arguments for the function func
and, following the Bolov's example, call a (first) helper function adding a (double, in this generalization) list of indexes and the std::tuple
of arguments
template <typename F, typename ... Ts>
void apply_on_each (F func, Ts ... ts)
{
static constexpr auto num_args { numArgs<F>() };
apply_on_each_h1(func,
std::make_index_sequence<sizeof...(Ts)/num_args>{},
std::make_index_sequence<num_args>{},
std::make_tuple(ts...));
}
Now the first helper function that "unpack" the first index sequence, using C++17 folding, and call the second helper function
template <typename F, std::size_t ... Is, std::size_t ... Js,
typename ... Ts>
void apply_on_each_h1 (F func,
std::index_sequence<Is...> const &,
std::index_sequence<Js...> const & js,
std::tuple<Ts...> const & t)
{ (apply_on_each_h2<Is>(func, js, t), ...) ; }
Now the last helper function that, playing with indexes, call the func
with the right arguments
template <std::size_t I, typename F, std::size_t ... Js, typename ... Ts>
void apply_on_each_h2 (F func,
std::index_sequence<Js...> const & js,
std::tuple<Ts...> const & t)
{ func(std::get<I*sizeof...(Js)+Js>(t)...); }
The following is a full example
#include <tuple>
#include <utility>
#include <iostream>
#include <type_traits>
template <typename F, typename ... Ts>
constexpr auto numArgsH (int, Ts ... ts)
-> decltype( std::declval<F>()(ts...), std::size_t{} )
{ return sizeof...(Ts); }
template <typename F, typename ... Ts>
constexpr auto numArgsH (long, Ts ... ts)
{ return numArgsH<F>(0, 0, ts...); }
template <typename F>
constexpr auto numArgs ()
{ return numArgsH<F>(0); }
template <std::size_t I, typename F, std::size_t ... Js, typename ... Ts>
void apply_on_each_h2 (F func,
std::index_sequence<Js...> const & js,
std::tuple<Ts...> const & t)
{ func(std::get<I*sizeof...(Js)+Js>(t)...); }
template <typename F, std::size_t ... Is, std::size_t ... Js,
typename ... Ts>
void apply_on_each_h1 (F func,
std::index_sequence<Is...> const &,
std::index_sequence<Js...> const & js,
std::tuple<Ts...> const & t)
{ (apply_on_each_h2<Is>(func, js, t), ...) ; }
template <typename F, typename ... Ts>
void apply_on_each (F func, Ts ... ts)
{
static constexpr auto num_args { numArgs<F>() };
apply_on_each_h1(func,
std::make_index_sequence<sizeof...(Ts)/num_args>{},
std::make_index_sequence<num_args>{},
std::make_tuple(ts...));
}
int main()
{
auto l1 = [](auto a)
{ std::cout << "- l1:" << a << std::endl; };
auto l2 = [](auto a, auto b)
{ std::cout << "- l2:" << a << ", " << b << std::endl; };
auto l3 = [](auto a, auto b, auto c)
{ std::cout << "- l3:" << a << ", " << b << ", " << c << std::endl; };
apply_on_each(l1, 1, 2l, 3ll, "4", '5', 6.0);
apply_on_each(l2, 1, 2l, 3ll, "4", '5', 6.0);
apply_on_each(l3, 1, 2l, 3ll, "4", '5', 6.0);
}
f
has default arguments? – Pyrophoricapply_on_each_args(lambda, 1, 2, "hello", "bye")
do you expect to calllambda(1, 2), lambda(2, "hello"), lambda("hello", "bye")
or onlylambda(1, 2), lambda("hello", "bye")
? – Riderlambda(1, 2)
, nextlambda("hello", "bye")
. Thanks – Dianadiandraapply_on_each_2_args(lambda, 1, 2, "hello", "bye")
,apply_on_each_3_args(lambda, 1, 2, 3, "hello", "intermezzo", "bye")
, – Dianadiandra