52

My question is in the code:

template<typename... Ts>
struct TupleOfVectors {
  std::tuple<std::vector<Ts>...> tuple;

  void do_something_to_each_vec() {
    //Question: I want to do this:
    //  "for each (N)": do_something_to_vec<N>()
    //How?
  }

  template<size_t N>
  void do_something_to_vec() {
    auto &vec = std::get<N>(tuple);
    //do something to vec
  }
};
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
7cows
  • 4,974
  • 6
  • 25
  • 30
  • 2
    Possible duplicate of [iterate over tuple](https://stackoverflow.com/questions/1198260/iterate-over-tuple) – Justin Aug 18 '17 at 02:08

7 Answers7

51

You can quite easily do that with some indices machinery. Given a meta-function gen_seq for generating compile-time integer sequences (encapsulated by the seq class template):

namespace detail
{
    template<int... Is>
    struct seq { };

    template<int N, int... Is>
    struct gen_seq : gen_seq<N - 1, N - 1, Is...> { };

    template<int... Is>
    struct gen_seq<0, Is...> : seq<Is...> { };
}

And the following function templates:

#include <tuple>

namespace detail
{
    template<typename T, typename F, int... Is>
    void for_each(T&& t, F f, seq<Is...>)
    {
        auto l = { (f(std::get<Is>(t)), 0)... };
    }
}

template<typename... Ts, typename F>
void for_each_in_tuple(std::tuple<Ts...> const& t, F f)
{
    detail::for_each(t, f, detail::gen_seq<sizeof...(Ts)>());
}

You can use the for_each_in_tuple function above this way:

#include <string>
#include <iostream>

struct my_functor
{
    template<typename T>
    void operator () (T&& t)
    {
        std::cout << t << std::endl;
    }
};

int main()
{
    std::tuple<int, double, std::string> t(42, 3.14, "Hello World!");
    for_each_in_tuple(t, my_functor());
}

Here is a live example.

In your concrete situation, this is how you could use it:

template<typename... Ts>
struct TupleOfVectors
{
    std::tuple<std::vector<Ts>...> t;

    void do_something_to_each_vec()
    {
        for_each_in_tuple(t, tuple_vector_functor());
    }

    struct tuple_vector_functor
    {
        template<typename T>
        void operator () (T const &v)
        {
            // Do something on the argument vector...
        }
    };
};

And once again, here is a live example.

Update

If you're using C++14 or later, you can replace the seq and gen_seq classes above with std::integer_sequence like so:

namespace detail
{
    template<typename T, typename F, int... Is>
    void
    for_each(T&& t, F f, std::integer_sequence<int, Is...>)
    {
        auto l = { (f(std::get<Is>(t)), 0)... };
    }
} // namespace detail

template<typename... Ts, typename F>
void
for_each_in_tuple(std::tuple<Ts...> const& t, F f)
{
    detail::for_each(t, f, std::make_integer_sequence<int, sizeof...(Ts)>());
}

If you're using C++17 or later you can do this (from this comment below):

std::apply([](auto ...x){std::make_tuple(some_function(x)...);} , the_tuple);
Olivia Stork
  • 4,660
  • 5
  • 27
  • 40
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • 18
    metaprogramming is eating my brains. not sure where things start, what is calling what, and what/where the end result is. not like regular programming at all. Will take some time to decipher this :) – 7cows May 05 '13 at 18:27
  • 2
    @7cows: Of course. In the beginning it is not easy at all, and perhaps this is not exactly the easiest example to begin with, but I'm sure with some practice you will soon grasp it ;) – Andy Prowl May 05 '13 at 18:28
  • 1
    `void for_each(T&& t, F f, seq)`: Why does the last argument not have an identifier? – template boy May 05 '13 at 18:30
  • 1
    @AndyProwl What is its purpose? – template boy May 05 '13 at 18:38
  • Yeah :) Why the `auto l` variable that isn't used, and why (in the same line) the extra `0` argument? – 7cows May 05 '13 at 19:42
  • 6
    The `auto l` is to allow treating what comes on the right side as an initializer list, which ensures the expanded expressions are evaluated in the correct order. The `(f(x), 0)` uses the comma operator so that `f(x)` is evaluated, but the resulting value is discarded, and the value of the expression `(f(x), 0)` is `0`. This is to give all elements of the initializer list the same type (`int`, here), in order to make it possible deducing the type of the initializer list (`initializer_list`) – Andy Prowl May 05 '13 at 20:13
  • 1
    If your compiler supports it, try `void do_in_order() {} template void do_in_order( F0&& f0, Fs&&... fs ) { std::forward(f0)(); do_in_order(std::forward(fs)...); }`, which is called like `do_in_order([&]{f(std::get(t));}...);` -- you create a variardic set of lambdas which are invoked by `do_in_order`. Above and beyond the problem that the above hack has holes in it (if `f` returns a type that overrides `operator,`, the result is unexpected), this better describes what you are doing. Sadly, many compilers lack the C++11 support required for this... – Yakk - Adam Nevraumont May 06 '13 at 01:24
  • 2
    So many year later, could the first part be replaced by [`std::integer_sequence`](https://en.cppreference.com/w/cpp/utility/integer_sequence)? – JHBonarius Jul 03 '18 at 20:24
  • 1
    @JHBonarius yes you are correct! If you're running C++14 or later you can use `std::integer_sequence` instead of the `seq` and `gen_seq` structs. – Olivia Stork Jul 30 '20 at 19:18
45

In C++17 you can do this:

std::apply([](auto ...x){std::make_tuple(some_function(x)...);} , the_tuple);

given that some_function has suitable overloads for all the types in the tuple.

This already works in Clang++ 3.9, using std::experimental::apply.

Dev Null
  • 4,731
  • 1
  • 30
  • 46
Mohammad Alaggan
  • 3,749
  • 1
  • 28
  • 20
  • 8
    Doesn't this lead to the iteration - i.e. calls of `do_something()` - occurring in an unspecified order, because the parameter pack is expanded within a function call `()`, wherein arguments have unspecified ordering? That might be very significant; I'd imagine most people would expect the ordering to be guaranteed to occur in the same order as the members, i.e. as the indices to `std::get<>()`. AFAIK, to get guaranteed ordering in cases like this, the expansion must be done within `{braces}`. Am I wrong? This answer puts emphasis on such ordering: http://stackoverflow.com/a/16387374/2757035 – underscore_d Oct 09 '16 at 14:42
  • 4
    @underscore_d C++17 guarantees the execution order of function arguments. Since this answer applies to C++17, this is valid. – Guillaume Racicot Apr 27 '17 at 17:08
  • 3
    @GuillaumeRacicot I'm aware of some changes to evaluation order/guarantees in certain contexts, but not in arguments to functions - other than some back-and-forth where left-to-right ordering was _considered_ but rejected. Look at the current draft of the Standard: https://github.com/cplusplus/draft/blob/master/source/expressions.tex#L1621 `The initialization of a parameter, including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other parameter.` – underscore_d Apr 28 '17 at 21:59
  • 1
    I agree that this will to undefined iteration order, even with C++17. – Baptiste Wicht Jun 13 '17 at 14:01
  • 3
    `std::apply([](auto&& ...x){ (static_cast(some_function(std::forward(x))), ...);} , the_tuple);` to guaranty order of evaluation, and allows `some_function` to return `void` (and even evil classes with overloaded `operator ,`). – Jarod42 Feb 09 '18 at 13:12
  • 4
    @Jarod42 since we talk about c++17 here, a fold expression like `std::apply([](auto& ...x){(..., some_function(x));}, the_tuple);` is better https://stackoverflow.com/a/45498003/8414561 – Dev Null Apr 21 '18 at 10:48
  • @DevNull: Mine also uses fold expression, but uses forward argument and handles class with evil overload comma. Sad than simplicity has some pitfall :-/ – Jarod42 Apr 21 '18 at 12:06
  • @Jarod42 yeah, didn't notice that, my bad; that's why I decided not to use `forward` to simplify reading :) could you please give an example of "evil overload comma" and problems that it can cause? – Dev Null Apr 21 '18 at 22:42
  • 2
    @DevNull: [Demo](http://coliru.stacked-crooked.com/a/d731bb5ce28a4b76) of evil operator comma in action. – Jarod42 Apr 21 '18 at 22:59
  • Wondering how I could get this to apply only to a function where a tuple is passed though... – Treviño Oct 15 '20 at 19:14
40

In addition to the answer of @M. Alaggan, if you need to call a function on tuple elements in order of their appearance in the tuple, in C++17 you can also use a fold expression like this:

std::apply([](auto& ...x){(..., some_function(x));}, the_tuple);

(live example).

Because otherwise order of evaluation of function arguments is unspecified.

Dev Null
  • 4,731
  • 1
  • 30
  • 46
  • This is IMHO a better solution than the accepted one because its syntax is clearer and the call order is same as the tuple order. – Zheng Qu Feb 09 '22 at 14:39
  • Both left and right fold, i.e. ```(..., some_function(x))``` vs. ```(some_function(x), ...)``` return the same answer in this case. Are there use cases where one would prefer one over the other? – user3882729 May 02 '22 at 01:29
  • @user3882729 not that I know of. – Dev Null May 02 '22 at 04:41
  • @user3882729 Left and right folds are the same when using the comma operator. They resolve to `(some_function(arg1), some_function(arg2)), some_function(arg3)` and `some_function(arg1), (some_function(arg2), some_function(arg3))` respectively, which are equivalent. – Stacker Oct 22 '22 at 02:13
8

Here's one approach which may work well in your case:

template<typename... Ts>
struct TupleOfVectors {
    std::tuple<std::vector<Ts>...> tuple;

    void do_something_to_each_vec()
    {
        // First template parameter is just a dummy.
        do_something_to_each_vec_helper<0,Ts...>();
    }

    template<size_t N>
    void do_something_to_vec()
    {
        auto &vec = std::get<N>(tuple);
        //do something to vec
    }

private:
    // Anchor for the recursion
    template <int>
    void do_something_to_each_vec_helper() { }

    // Execute the function for each template argument.
    template <int,typename Arg,typename...Args>
    void do_something_to_each_vec_helper()
    {
        do_something_to_each_vec_helper<0,Args...>();
        do_something_to_vec<sizeof...(Args)>();
    }
};

The only thing that is a bit messy here is the extra dummy int template parameter to do_something_to_each_vec_helper. It is necessary to make the do_something_to_each_vec_helper still be a template when no arguments remain. If you had another template parameter you wanted to use, you could use it there instead.

Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
  • It's pretty brilliant how this manages to call the `do_something_to_vec` method in correct 0,1,2,... order isn't it? I like it... :) – 7cows May 05 '13 at 21:14
  • `template void do_something_to_each_vec_helper() { do_something_to_vec<)(); template void do_something_to_each_vec_helper() { do_something_to_each_vec_helper(); do_something_to_vec(); }` gets rid of that unused `int`, but violates DRY (don't repeat yourself) to some degree. Hmm. – Yakk - Adam Nevraumont May 06 '13 at 01:30
  • @VaughnCato really? I thought in the case of an ambiguity there, the one without the parameter pack was picked. Guess I was wrong -- maybe that only applies when it is a variardic set of arguments, and not pure types? – Yakk - Adam Nevraumont May 06 '13 at 14:21
6

If you are not particularly wedded to a solution in the form of generic "for each" function template then you can use one like this:

#ifndef TUPLE_OF_VECTORS_H
#define TUPLE_OF_VECTORS_H

#include <vector>
#include <tuple>
#include <iostream>

template<typename... Ts>
struct TupleOfVectors 
{
    std::tuple<std::vector<Ts>...> tuple;

    template<typename ...Args>
    TupleOfVectors(Args... args)
    : tuple(args...){}

    void do_something_to_each_vec() {
        do_something_to_vec(tuple);
    }

    template<size_t I = 0, class ...P>
    typename std::enable_if<I == sizeof...(P)>::type
    do_something_to_vec(std::tuple<P...> &) {}

    template<size_t I = 0, class ...P>
    typename std::enable_if<I < sizeof...(P)>::type
    do_something_to_vec(std::tuple<P...> & parts) {
        auto & part = std::get<I>(tuple);
        // Doing something...
        std::cout << "vector[" << I << "][0] = " << part[0] << std::endl;
        do_something_to_vec<I + 1>(parts);
    }
};

#endif // EOF

A test program, built with GCC 4.7.2 and clang 3.2:

#include "tuple_of_vectors.h"

using namespace std;

int main()
{
    TupleOfVectors<int,int,int,int> vecs(vector<int>(1,1),
        vector<int>(2,2),
        vector<int>(3,3),
        vector<int>(4,4));

    vecs.do_something_to_each_vec();
    return 0;
}

The same style of recursion can be used in a generic "for_each" function template without auxiliary indices apparatus:

#ifndef FOR_EACH_IN_TUPLE_H
#define FOR_EACH_IN_TUPLE_H

#include <type_traits>
#include <tuple>
#include <cstddef>

template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I == sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> &, Func) {}

template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I < sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> & tpl, Func func) 
{
    func(std::get<I>(tpl));
    for_each_in_tuple<I + 1>(tpl,func);
}

#endif //EOF

And a test program for that:

#include "for_each_in_tuple.h"
#include <iostream>

struct functor
{
    template<typename T>
    void operator () (T&& t)
    {
        std::cout << t << std::endl;
    }
};

int main()
{
    auto tpl = std::make_tuple(1,2.0,"Three");
    for_each_in_tuple(tpl,functor());
    return 0;
}
Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182
2

I was testing with tuples and metaprograming and found the current thread. I think my work can inspire someone else although I like the solution of @Andy.

Anyway, just get fun!

#include <tuple>
#include <type_traits>
#include <iostream>
#include <sstream>
#include <functional>

template<std::size_t I = 0, typename Tuple, typename Func>
typename std::enable_if< I != std::tuple_size<Tuple>::value, void >::type
for_each(const Tuple& tuple, Func&& func)
{
    func(std::get<I>(tuple));
    for_each<I + 1>(tuple, func);
}

template<std::size_t I = 0, typename Tuple, typename Func>
typename std::enable_if< I == std::tuple_size<Tuple>::value, void >::type
for_each(const Tuple& tuple, Func&& func)
{
    // do nothing
}


struct print
{
    template<typename T>
    void operator () (T&& t)
    {
        std::cout << t << std::endl;
    }
};

template<typename... Params>
void test(Params&& ... params)
{
    int sz = sizeof...(params);
    std::tuple<Params...> values(std::forward<Params>(params)...);
    for_each(values, print() );
}


class MyClass
{
public:
    MyClass(const std::string& text) 
        : m_text(text)
    {
    }

    friend std::ostream& operator <<(std::ostream& stream, const MyClass& myClass)
    {
        stream << myClass.m_text;
        return stream;
    }

private:
    std::string m_text;
};


int main()
{
    test(1, "hello", 3.f, 4, MyClass("I don't care") );
}
dmayola
  • 502
  • 5
  • 16
  • Great piece of code! However, as-is it does not work with in-line lambdas as it expects an l-value `func`. The function signature should be changed to `for_each(const Tuple& tuple, Func&& func)`, with a `Func&& func` argument to allow passing a temporary lambda. – Adi Shavit Jul 25 '16 at 13:16
0

Boost mp11 has this functionality:

#include <iostream>
#include <string>
#include <boost/mp11.hpp>

using namespace std;
using boost::mp11::tuple_for_each;

std::tuple t{string("abc"), 47 };

int main(){
    tuple_for_each(t,[](const auto& x){
        cout << x + x << endl;
    });
}
NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277