Applying func to elements in std :: tuple in natural (not reverse) order

I need to call a template function or an overloaded function for each element in an arbitrary tuple. To be precise, I need to call this function for elements, as they are specified in the tuple.

For example. I have a tuple std::tuple<int, float> t{1, 2.0f}; and functional

 class Lambda{ public: template<class T> void operator()(T arg){ std::cout << arg << "; "; } }; 

I need an Apply structure / function that, if called as Apply<Lambda, int, float>()(Lambda(), t) , will give:

 1; 2.0f; 

and NOT 2.0f; 1; 2.0f; 1; .

Please note that I know the solution if a package of "raw" parameters is passed to the function, and I know how to do this for tuples in the reverse order. But the following attempt to partial specialize Apply not performed:

 template<class Func, size_t index, class ...Components> class ForwardsApplicator{ public: void operator()(Func func, const std::tuple<Components...>& t){ func(std::get<index>(t)); ForwardsApplicator<Func, index + 1, Components...>()(func, t); } }; template<class Func, class... Components> class ForwardsApplicator < Func, sizeof...(Components), Components... > { public: void operator()(Func func, const std::tuple<Components...>& t){} }; int main{ ForwardsApplicator<Lambda, 0, int, float>()(Lambda{}, std::make_tuple(1, 2.0f)); } 

The code compiles, but only the first argument is printed. However, if I replace the ForwardsApplicator specialization with

 template<class Func, class... Components> class ForwardsApplicator < Func, 2, Components... >{...} 

it works correctly - but, of course, only for tuples of length 2. How can I do this - if possible, elegantly - for tuples of arbitrary length?

EDIT: Thanks guys for your answers! All three are really straightforward and explain the problem from all possible points of view.

+7
c ++ c ++ 11 functional-programming stdtuple
source share
3 answers

The problem is that size...(Components) cannot be used in specialization for an unknown list of Components types. GCC complains about this with an error:

 prog.cpp:16:7: error: template argument 'sizeof... (Components)' involves template parameter(s) class ForwardsApplicator < Func, sizeof...(Components), Components... > { ^ 

I propose a slightly different approach. First, move the list of Components types to the template parameter for operator() , i.e.:

 template<class ...Components> void operator()(Func func, const std::tuple<Components...>& t) { ... } 

Then reverse the order of the call: first make a recursive call, then invokation with index-1 (i.e. call the last element of the set). Start this recursion with index = sizeof...(Components) and go to index = 0 , which is noop (so the specialization is 0 , regardless of sizeof...(Components) ), which I started talking about).

To call this, add a function to output the template argument:

 // General case (recursion) template<class Func, size_t index> class ForwardsApplicator{ public: template<class ...Components> void operator()(Func func, const std::tuple<Components...>& t){ ForwardsApplicator<Func, index - 1>()(func, t); func(std::get<index - 1>(t)); } }; // Special case (stop recursion) template<class Func> class ForwardsApplicator<Func, 0> { public: template<class ...Components> void operator()(Func func, const std::tuple<Components...>& t){} }; // Helper function for template type deduction template<class Func, class ...Components> void apply(Func func, const std::tuple<Components...>& t) { ForwardsApplicator<Func, sizeof...(Components)>()(func, t); } 

Then it is easy to call, without the need for any template parameters on the call site:

 apply(Lambda{}, std::make_tuple(1, 2.0f)); 

Live demo

+6
source share

This is a tutorial for the integer_sequence trick.

 template<class Func, class Tuple, size_t...Is> void for_each_in_tuple(Func f, Tuple&& tuple, std::index_sequence<Is...>){ using expander = int[]; (void)expander { 0, ((void)f(std::get<Is>(std::forward<Tuple>(tuple))), 0)... }; } template<class Func, class Tuple> void for_each_in_tuple(Func f, Tuple&& tuple){ for_each_in_tuple(f, std::forward<Tuple>(tuple), std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>()); } 

Demo

std::index_sequence , and friends are C ++ 14, but this is a pure library extension and can be easily implemented in C ++ 11. You can easily find half a dozen implementations in SO.

+9
source share

Counting in a template does not mean that you process the tuple elements in the same order. A simple approach to head-to-tail tuple processing is head recursion (as opposed to tail recursion):

 #include <tuple> #include <iostream> // Our entry point is the tuple size template<typename Tuple, std::size_t i = std::tuple_size<Tuple>::value> struct tuple_applicator { template<typename Func> static void apply(Tuple &t, Func &&f) { // but we recurse before we process the element associated with that // number, reversing the order so that the front elements are processed // first. tuple_applicator<Tuple, i - 1>::apply(t, std::forward<Func>(f)); std::forward<Func>(f)(std::get<i - 1>(t)); } }; // The recursion stops here. template<typename Tuple> struct tuple_applicator<Tuple, 0> { template<typename Func> static void apply(Tuple &, Func &&) { } }; // and this is syntactical sugar template<typename Tuple, typename Func> void tuple_apply(Tuple &t, Func &&f) { tuple_applicator<Tuple>::apply(t, std::forward<Func>(f)); } int main() { std::tuple<int, double> t { 1, 2.3 }; // The generic lambda requires C++14, the rest // works with C++11 as well. Put your Lambda here instead. tuple_apply(t, [](auto x) { std::cout << x * 2 << '\n'; }); } 

Output signal

 2 4.6 
+2
source share

All Articles