генерация звонков в лямбды с произвольным количеством параметров - PullRequest
0 голосов
/ 23 июня 2018

Следующее определение оказалось очень полезным для меня:

template<class Func, class... Args>
void apply_on_each_args(Func f, Args... args)
{
    (f(args), ...);
}

По сути, пакет аргументов, сложенный в оператор запятой, позволяет определять несколько вызовов функции, принимающей аргумент.Например:

apply_on_each_args([] (auto x) { cout << x << endl; }, 1, 2, "hello");

вызовет анонимную лямбду на 1, 2 и "hello".

В этой представленной идее я хотел бы сделать то же самое, но передавлямбды принимают два, три и так далее аргументов.Например, что-то в этом роде

apply_on_each_args([] (auto x, auto y) { /* use x and y */ }, 1, 2, "hello",  "bye");

Любая подсказка, техника, идея и т. Д., Позволяющие достичь этого?

Ответы [ 3 ]

0 голосов
/ 23 июня 2018

Хорошо, мой вуду сегодня крепок:

auto foo(int, int) -> void;

template <class Func, class... Args, std::size_t... I>
void apply_on_2x_indexes(Func f,  std::index_sequence<I...>, std::tuple<Args...> t)
{
    (f(std::get<I * 2>(t), std::get<I * 2 + 1>(t)), ...);
}

template<class Func, class... Args>
void apply_on_each_2_args(Func f, Args... args)
{
    apply_on_2x_indexes(f, std::make_index_sequence<sizeof...(Args) / 2>{},
                        std::tuple{args...});   
}

auto test()
{
    apply_on_each_2_args(foo, 1, 2, 3, 4); // calls foo(1, 2) foo(3, 4)
}

Для краткости пересылка опущена.

Чтобы лучше понять, как это работает, мы можем расширить вручную:

apply(on_each_2_args(foo, 1, 2, 3, 4))
↳ apply_on_2x_indexes(f, std::index_sequence<0, 1>{}, std::tuple{1, 2, 3, 4})
  ↳ (f(std::get<0 * 2>(t), std::get<0 * 2 + 1>(t)),  f(std::get<1 * 2>(t), std::get<1 * 2 + 1>(t)))
    (f(std::get<0>(t), std::get<1>(t)),  f(std::get<2>(t), std::get<3>(t)))
    (f(1, 2), f(3, 4))

Другой подход:

Одна вещь, которая мне не нравится в вашем синтаксисе вызовов

apply_on_each_2_args([] (auto x, auto y) { }, 1, 2, "hello",  "bye");

не ясно, как сгруппированы аргументы за вызов.

Так что я хотел бы сгруппировать их. К сожалению, я не могу заставить его так работать для varargs:

apply_on_each_2_args([] (auto x, auto y) { }, {1, 2}, {"hello",  "bye"});

но мы можем быть немного более многословны с tuple:

template<class Func, class... Args>
void apply_on_each_2_args(Func f, Args... args)
{
    (std::apply(f, args), ...);
}

auto test()
{
    apply_on_each_2_args([](auto a, auto b){ /*use a, b*/ },
                         std::tuple{1, 2}, std::tuple{"hello", "bye"});
}

Это не совсем то, что вы просили, но подход стоит рассмотреть.

0 голосов
/ 24 июня 2018

Способ создания 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);
 }
0 голосов
/ 23 июня 2018

Лучшее, что я могу себе представить (на данный момент), это старый добрый рекурсивный способ.

К примеру

// ground case
template <typename Func>
void apply_on_each_2_args (Func)
 { }

// recursive case
template <typename Func, typename A0, typename A1, typename ... Args>
void apply_on_each_2_args (Func f, A0 a0, A1 a1, Args ... args)
 { f(a0, a1); apply_on_each_2_args(f, args...); }
...