Как разделить пакет параметров? - PullRequest
6 голосов
/ 11 января 2012

Я хотел бы написать шаблон функции, apply, который получает некоторую функцию f, целое число i и пакет параметров. apply необходимо распаковать параметры и применить к ним f, за исключением i th параметра pi. Для pi необходимо вызвать некоторую другую функцию g, прежде чем передать ее в качестве параметра f.

Кажется, мне нужен способ разбить пакет параметров на левую сторону, i-й параметр и правую сторону. Это возможно? В коде:

template<int i, typename Function, typename... Parms>
  void apply(Function f, Parms... parms)
{
  auto lhs = // what goes here?
  auto pi =  // what goes here?
  auto rhs = // what goes here?

  f(lhs..., g(pi), rhs...);
}

Ответы [ 2 ]

3 голосов
/ 11 января 2012

Я на самом деле сделал нечто похожее недавно. Поэтому попробуйте следующий код:

template<unsigned N, unsigned M>
struct call_up_impl{
    template<class Func, class Mutator, class Tuple, class... Args>
    static void do_call(const Func& func, const Mutator& mutator, const Tuple& args, Args&&... unpacked_args) {
        call_up_impl<N-1, M>::do_call(func, mutator, args, std::get<N-1>(args), std::forward<Args>(unpacked_args)...);
    }
};

template<unsigned M>
struct call_up_impl<0, M> {
    template<class Func, class Mutator, class Tuple, class... Args>
    static void do_call(const Func& func, const Mutator&, const Tuple&, Args&&... unpacked_args) {
        func(std::forward<Args>(unpacked_args)...);
    }
};
template<unsigned M>
struct call_up_impl<M, M> {
    template<class Func, class Mutator, class Tuple, class... Args>
    static void do_call(const Func& func, const Mutator& mutator, const Tuple& args, Args&&... unpacked_args) {
        call_up_impl<M-1, M>::do_call(func, mutator, args, mutator(std::get<M-1>(args)), std::forward<Args>(unpacked_args)...);
    }
};
template<int i, typename Function, typename... Parms>
void apply(Function f, Parms... parms) {
      std::tuple<Parms...> t(parms...);
      call_up_impl<std::tuple_size<decltype(t)>::value, i + 1>::do_call(f, &g, t);
}

Это быстрая адаптация моего исходного кода, поэтому он не полностью протестирован и, возможно, не является оптимальным способом сделать это, но он должен работать как минимум (по крайней мере, в соответствии с быстрым тестом и в зависимости от того, что именно вы хочу). Должна быть возможность сделать это без кортежа, но я не получил этого для компиляции с g ++ (похоже, ему не нравятся вложенные переменные шаблоны). Однако меняется apply на:

template<int i, typename Function, typename... Parms>
void apply(Function f, Parms&&... parms) {
      std::tuple<Parms&&...> t(std::forward<Parms>(parms)...);
      call_up_impl<std::tuple_size<decltype(t)>::value, i + 1>::do_call(f, &g, t);
}

, вероятно, позволит избежать большинства накладных расходов, вносимых кортежем. Было бы даже лучше сделать правильную переадресацию результатов вызовов std::get, но я слишком устала, чтобы писать это сейчас.

3 голосов
/ 11 января 2012

ОК, поехали!Это действительно безобразно, но я не смог придумать более приятную версию в спешке;) Большинство вещей - это стандартная специализация на болоте.Самая большая проблема - это создание списка целых чисел подходящего размера.Кажется, я помню, что я придумал хорошую версию, но почему-то не могу вспомнить, что я сделал.Наслаждайтесь!

#include <iostream>
#include <utility>

// printing the values
void print_args() {}
template <typename F> void print_args(F f) { std::cout << f; }
template <typename F, typename... T>
void print_args(F f, T... args)
{
    std::cout << f << ", ";
    print_args(args...);
}

// the function object to be called:
struct Functor
{
    template <typename... T>
    void operator()(T... args)
    {
        std::cout << "f(";
        print_args(args...);
        std::cout << ")\n";
    }
};

// conditionally apply g():
template <typename T> T g(T value) { return 1000 + value; }
template <int i, int j, typename T>
typename std::enable_if<i != j, T>::type forward(T t) { return t; }
template <int i, int j, typename T>
typename std::enable_if<i == j, T>::type forward(T t) { return g(t); }

// create a series of integers:
template <int... Values> struct values {};

template <int Add, typename> struct combine_values;
template <int Add, int... Values>
struct combine_values<Add, values<Values...>>
{
    typedef values<Values..., Add> type;
};

template <int Size> struct make_values;
template <> struct make_values<0> { typedef values<> type; };
template <int Size>
struct make_values
{
    typedef typename combine_values<Size, typename make_values<Size -1>::type>::type type;
};

// applying f(t...) except for ti where g(ti) is called
template <int i, int... Values, typename Function, typename... T>
void apply_aux(values<Values...>, Function f, T... t)
{
    f(forward<i, Values>(t)...);
}

template <int i, typename Function, typename... T>
void apply(Function f, T... t)
{
    apply_aux<i>(typename make_values<sizeof...(T)>::type(), f, t...);
}

int main()
{
    apply<3>(Functor(), 1, 2, 3, 4, 5, 6, 7, 8);
    apply<4>(Functor(), 1, 2, 3, 4, 5, 6, 7, 8);
    apply<5>(Functor(), 1, 2, 3, 4, 5, 6, 7, 8);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...