Способ создания apply_on_each()
, который получает лямбду (или функцию), которая получает неопределенное количество универсальных аргументов и вызывает их (частично) развертывание способом C ++ 17.
Длячестно говоря, это всего лишь обобщение ответа Болова по вуду.
Прежде всего, набор constexpr
функций для определения количества аргументов функции (предположим, что аргументы являются общими, поэтому предположим списокдопустимы целые нули)
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); }
Теперь функция apply_on_each()
, которая определяет количество аргументов для функции func
и, следуя примеру Болова, вызывает (первую) вспомогательную функцию, добавляя (double, в этом обобщении) список индексов и std::tuple
аргументов
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...));
}
Теперь первая вспомогательная функция, которая «распаковывает» первую последовательность индексов, используя свёртывание C ++ 17, и вызывает второйвспомогательная функция
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), ...) ; }
Теперь последняя вспомогательная функция, которая, играя с индексами, вызывает func
с правильными аргументами
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)...); }
это полный пример
#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);
}