Iterating over a std::tuple

I’ve been trying to wrap my brain around the new variadic templates capability in C++11 lately and wondered if it would be possible to write a generic routine to iterate over the members of a std::tuple. I decided to start with the simple case of printing out the members of a tuple to the console via std::cout. First, I came up with a compile time recursive definition of a variadic template struct that overloads the function call operator like so. You might wonder why I didn’t stick with a plain variadic template function instead. That will become evident in a moment.

 template<int index, typename... Ts>
 struct print_tuple {
     void operator() (tuple<Ts...>& t) {
         cout << get<index>(t) << " ";
         print_tuple<index - 1, Ts...>{}(t);
     }
 };

Clearly, we’ll need to define a base case to break out of the compile time recursion for the print_tuple call in the function call operator overload – which in this case would be when the non-type template parameter index is equal to zero. So I went ahead and defined the following partial specialization of print_tuple specializing on the value zero for the non-type template parameter index.

 template<typename... Ts>
 struct print_tuple<0, Ts...> {
     void operator() (tuple<Ts...>& t) {
         cout << get<0>(t) << " ";
     }
 };

The reason I had to use a variadic template struct instead of a regular variadic template function is that it is not possible to partially specialize a template function in C++. Using a struct/class allows us to do so. Now that we have this setup, we can write a utility print routine to wrap calls to print_tuple and we should be done. Here’s a complete example:

 #include <iostream>
 #include <tuple>

 using namespace std;

 template<int index, typename... Ts>
 struct print_tuple {
     void operator() (tuple<Ts...>& t) {
         cout << get<index>(t) << " ";
         print_tuple<index - 1, Ts...>{}(t);
     }
 };

 template<typename... Ts>
 struct print_tuple<0, Ts...> {
     void operator() (tuple<Ts...>& t) {
         cout << get<0>(t) << " ";
     }
 };

 template<typename... Ts>
 void print(tuple<Ts...>& t) {
     const auto size = tuple_size<tuple<Ts...>>::value;
     print_tuple<size - 1, Ts...>{}(t);
 }

 int main() {
     auto t = make_tuple(1, 2, "abc", "def", 4.0f);
     print(t);

     return 0;
 }

All that print does is to first determine the size of the tuple via tuple_size and then instantiates print_tuple and invokes the function call operator passing the tuple object in question. As you might have noticed we are essentially working our way backwards till we hit the base case where index is zero – i.e. it’ll print the tuple members in the reverse order. Here’s the output this produces:

 4 def abc 2 1

Iterate in order?

I figured, implementing a version that iterates over the tuple members in the order that they are specified should be fairly straightforward. We should just need to define a different base case (for the last item in the tuple instead of the first) and the recursive implementation should simply increment the index instead of decrementing it. Here’s what I came up with:

 template<int index, typename... Ts>
 struct print_tuple {
     void operator() (tuple<Ts...>& t) {
         cout << get<index>(t) << " ";
         print_tuple<index + 1, Ts...>{}(t);
     }
 };

 template<typename... Ts>
 struct print_tuple<tuple_size<tuple<Ts...>>::value - 1, Ts...> {
     void operator() (tuple<Ts...>& t) {
         cout << get<tuple_size<tuple<Ts...>>::value - 1>(t) << " ";
     }
 };

The code shown in bold above are the changes of interest. In particular, turns out, the base case definition which handles the situation when print_tuple is being instantiated with an index that is equal to the size of the tuple minus one, is not really valid C++. Non-type template specialization in C++ can only be done using “simple identifiers”. The expression tuple_size<tuple<Ts...>>::value - 1 is a compile time constant for sure and ideally the compiler should be able to compute that value (which in fact it does for the code in the body of that method definition) but, well, it doesn’t! So, we’re kind of out of luck there.

Generalized iteration?

One might imagine that it should be possible to generalize this iteration (even if it can be done in reverse order only) so that we are able to supply arbitrary callbacks for processing tuple members. Turns out this again, is not possible which I think is reasonable. Because at that point one really needs to question whether using a tuple is the correct choice – a std::vector or std::list or some of the other containers maybe a more appropriate option. Having said that, you might be thinking that we should still be able to generalize this by adding another template parameter for a callback routine and passing in a template function for that parameter. Maybe something like this?

 #include <iostream>
 #include <tuple>

 using namespace std;

 template<int index, typename TCallback, typename... Ts>
 struct iterate_tuple {
     void operator() (tuple<Ts...>& t, TCallback callback) {
         callback(get<index>(t));
         iterate_tuple<index - 1, TCallback, Ts...>{}(t, callback);
     }
 };

 template<typename TCallback, typename... Ts>
 struct iterate_tuple<0, TCallback, Ts...> {
     void operator() (tuple<Ts...>& t, TCallback callback) {
         callback(get<0>(t));
     }
 };

 template<typename TCallback, typename... Ts>
 void for_each(tuple<Ts...>& t, TCallback callback) {
     iterate_tuple<tuple_size<tuple<Ts...>>::value - 1, TCallback, Ts...> it;
     it(t, callback);
 }

 template<typename T>
 void print(T v) {
     cout << v << " ";
 }

 int main() {
     auto t = make_tuple(1, 2, "abc", "def", 4.0f);
     for_each(t, print);

     return 0;
 }

This won’t work because the compiler needs to know the type of TCallback when it is instantiating the for_each template function which in this case it doesn’t and neither do we know the type because we want a different version of print to be used for each unique type in the tuple. If there is some way of telling the compiler to postpone resolution of TCallback till it is actually used then this might have worked. As far as I know, that isn’t possible. But then I might be wrong. If you know a way of doing that, it’ll be great if you could please let me know in the comments.

comments powered by Disqus