How to break a package of parameters?

I would like to write a template for the apply function that gets some function f , an integer i and a package of parameters. apply it is necessary to unpack the parameters and apply f to them, with the exception of the parameter i th, pi . For pi you need to pass some other function g before passing it as the parameter f .

I seem to need a way to split the parameter packet into the left side, the i th parameter and the right side. Is it possible? In code:

 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...); } 
+6
source share
2 answers

OK, here we are! This is really ugly, but I could not come up with a more pleasant version in a hurry;) Most of the material is the standard specification of swamp patterns. The biggest problem is creating a list of integers of the correct size. I think I remember that I came up with a good version, but for some reason I can’t remember what I did. Enjoy it!

 #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); } 
+3
source

Initially, something worked a while ago. So try the following code:

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

This is a quick adaptation of my source code, so it has not passed a thorough check and may not be the best way to do this, but it should work at least (at least for a quick test and depending on what exactly you want). It should be possible to do this without a tuple, but I didn’t get it for compilation with g ++ (it doesn’t like nested variation templates). However, changing apply to:

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

likely to escape most of the overhead entered by the tuple. It would be even better to properly forward the results of std::get calls, but I'm too tired to work on it now.

+3
source

Source: https://habr.com/ru/post/905675/


All Articles