92

This is a follow-up to my previous question on pretty-printing STL containers, for which we managed to develop a very elegant and fully general solution.


In this next step, I would like to include pretty-printing for std::tuple<Args...>, using variadic templates (so this is strictly C++11). For std::pair<S,T>, I simply say

std::ostream & operator<<(std::ostream & o, const std::pair<S,T> & p)
{
  return o << "(" << p.first << ", " << p.second << ")";
}

What is the analogous construction for printing a tuple?

I've tried various bits of template argument stack unpacking, passing indices around and using SFINAE to discover when I'm at the last element, but with no success. I shan't burden you with my broken code; the problem description is hopefully straight-forward enough. Essentially, I'd like the following behaviour:

auto a = std::make_tuple(5, "Hello", -0.1);
std::cout << a << std::endl; // prints: (5, "Hello", -0.1)

Bonus points for including the same level of generality (char/wchar_t, pair delimiters) as the the previous question!

Community
  • 1
  • 1
Kerrek SB
  • 428,875
  • 83
  • 813
  • 1,025
  • Has someone put any of the code here into a library? Or even an .hpp-with-everything-in which one could grab and use? – einpoklum Jul 16 '15 at 18:53
  • @einpoklum: Maybe [cxx-prettyprint](https://louisdx.github.io/cxx-prettyprint/)? That's what I needed that code for. – Kerrek SB Jul 16 '15 at 19:11
  • 1
    Great question, and +1 for "I shan't burden you with my broken code", although I am surprised it seems to have actually succeede in fending off the mindless "what have you tried" hordes. – Don Hatch Apr 19 '20 at 00:15

12 Answers12

79

Yay, indices~

namespace aux{
template<std::size_t...> struct seq{};

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

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

template<class Ch, class Tr, class Tuple, std::size_t... Is>
void print_tuple(std::basic_ostream<Ch,Tr>& os, Tuple const& t, seq<Is...>){
  using swallow = int[];
  (void)swallow{0, (void(os << (Is == 0? "" : ", ") << std::get<Is>(t)), 0)...};
}
} // aux::

template<class Ch, class Tr, class... Args>
auto operator<<(std::basic_ostream<Ch, Tr>& os, std::tuple<Args...> const& t)
    -> std::basic_ostream<Ch, Tr>&
{
  os << "(";
  aux::print_tuple(os, t, aux::gen_seq<sizeof...(Args)>());
  return os << ")";
}

Live example on Ideone.


For the delimiter stuff, just add these partial specializations:

// Delimiters for tuple
template<class... Args>
struct delimiters<std::tuple<Args...>, char> {
  static const delimiters_values<char> values;
};

template<class... Args>
const delimiters_values<char> delimiters<std::tuple<Args...>, char>::values = { "(", ", ", ")" };

template<class... Args>
struct delimiters<std::tuple<Args...>, wchar_t> {
  static const delimiters_values<wchar_t> values;
};

template<class... Args>
const delimiters_values<wchar_t> delimiters<std::tuple<Args...>, wchar_t>::values = { L"(", L", ", L")" };

and change the operator<< and print_tuple accordingly:

template<class Ch, class Tr, class... Args>
auto operator<<(std::basic_ostream<Ch, Tr>& os, std::tuple<Args...> const& t)
    -> std::basic_ostream<Ch, Tr>&
{
  typedef std::tuple<Args...> tuple_t;
  if(delimiters<tuple_t, Ch>::values.prefix != 0)
    os << delimiters<tuple_t,char>::values.prefix;

  print_tuple(os, t, aux::gen_seq<sizeof...(Args)>());

  if(delimiters<tuple_t, Ch>::values.postfix != 0)
    os << delimiters<tuple_t,char>::values.postfix;

  return os;
}

And

template<class Ch, class Tr, class Tuple, std::size_t... Is>
void print_tuple(std::basic_ostream<Ch, Tr>& os, Tuple const& t, seq<Is...>){
  using swallow = int[];
  char const* delim = delimiters<Tuple, Ch>::values.delimiter;
  if(!delim) delim = "";
  (void)swallow{0, (void(os << (Is == 0? "" : delim) << std::get<Is>(t)), 0)...};
}
David Rodríguez - dribeas
  • 192,922
  • 20
  • 275
  • 473
Xeo
  • 123,374
  • 44
  • 277
  • 381
  • @Kerrek: I'm currently testing & fixing myself, I get weird output on Ideone though. – Xeo Jun 05 '11 at 21:03
  • I think you're also confusing streams and strings. You're writing something akin to "std::cout << std::cout". In other words, `TuplePrinter` doesn't have an `operator< – Kerrek SB Jun 05 '11 at 21:13
  • @Kerrek: Yeah, I did some pretty strange stuff.. New version is edited, and works like requested. :P – Xeo Jun 05 '11 at 21:16
  • Grand, now it works! I'll now try to include this into the pretty printer project from the previous question. Thanks a bunch! – Kerrek SB Jun 05 '11 at 21:24
  • @Kerrek: To add the delimiters from the previous question, you just need another partial spec on tuples. Included that now. The rest (`basic_ostream` etc) should be easy from here on. – Xeo Jun 05 '11 at 21:25
  • Maybe doesn't work so well with empty tuples, but that's easy enough to fix with another specialization or a size check in the top-level operator<<...> – Nate Kohl Jun 05 '11 at 21:38
  • I wasn't going to deal with empty tuples, I'm happy enough that it works for oneples ;-) Hey, could one of you non-GNU people check if the template aliases in the pretty printer project work? Those would make defining custom delimiters a lot easier. – Kerrek SB Jun 05 '11 at 21:42
  • @Kerrek: You can define template aliases in C++03 too, as seen [here](http://ideone.com/oKwBU). :) – Xeo Jun 05 '11 at 21:53
  • Haha, jehova :-) Well, if you look at the prettyprinter.h code, I want a convenient way for users to define those delimiter classes for TChar = {char, wchar_t}, so I basically want to provide an "sdelims" and "wsdelims" shortcut, without making stuff more verbose. C++0x template aliases sound like the way to go, but I'm open to suggestions. Mind you, my sample case already contains a demo of custom delimiters, so the goal here is to be strictly more elegant. – Kerrek SB Jun 05 '11 at 21:58
  • Can't we replace `struct TuplePrinter` with just templated functions when using C++0x/C++11? – Nordlöw Sep 24 '11 at 15:42
  • @Nordlöw: No, we still can't easily, because functions *still* can't be partially specialized, but see my update on ways to circumvent that, even in C++03. – Xeo Feb 24 '12 at 17:06
  • If you don't have variadic templates, you can use class Tuple instead of class... Args and std::tuple_size::value instead of sizeof...(Args). – Thomas Mar 19 '13 at 22:25
  • 1
    @Thomas: You can't just use `class Tuple` for the `operator< – Xeo Mar 19 '13 at 22:56
  • Dang, my compilation hadn't completed when I posted. Guess I need to port boost/tuple_io.hpp to std::tuple. – Thomas Mar 21 '13 at 17:37
  • @Xeo: The code has two problems: a) `operator<&` instead of `std::ostream&` and b) it does not work with empty tuples, i.e., `std::tuple<>`. – Daniel Frey Mar 30 '13 at 02:03
  • @DanielFrey: The return type was a fragment from refactoring, thanks for catching that. And yeah, empty tuple is currently broken, but can easily be fixed. In fact, I should probably rewrite this thing to just use the indices trick. – Xeo Mar 30 '13 at 02:06
  • @Xeo: Using indices won't be easy here, but try it. Main problem will be order of evaluation. – Daniel Frey Mar 30 '13 at 02:08
  • 1
    @DanielFrey: That's a solved problem, list-initialization guarantees left-to-right order: `swallow{(os << get(t))...};`. – Xeo Mar 30 '13 at 02:11
  • @Xeo: Thanks to your hint to use initializer-lists to guarantee the order of execution, I have an elegant version with indices ready. Shall I modify your answer or write a separate one? – Daniel Frey Mar 30 '13 at 07:31
  • 6
    @Xeo I borrowed your swallow for [cppreference](http://en.cppreference.com/w/cpp/utility/integer_sequence), if you don't mind. – Cubbi May 17 '13 at 17:51
  • Some nitpicking... You don't want to define operators in the wrong namespace and you cannot define this one in the right namespace (`::std`). It would be better as a function that is called (possibly qualified) to avoid lookup failing to locate the operator. – David Rodríguez - dribeas Dec 02 '14 at 15:45
  • @DavidRodríguez-dribeas Really, you should have a namespace `print_pretty` with an expression template `pretty` with an overloaded `< – Yakk - Adam Nevraumont May 26 '15 at 14:40
24

In C++17 we can accomplish this with a little less code by taking advantage of Fold expressions, particularly a unary left fold:

template<class TupType, size_t... I>
void print(const TupType& _tup, std::index_sequence<I...>)
{
    std::cout << "(";
    (..., (std::cout << (I == 0? "" : ", ") << std::get<I>(_tup)));
    std::cout << ")\n";
}

template<class... T>
void print (const std::tuple<T...>& _tup)
{
    print(_tup, std::make_index_sequence<sizeof...(T)>());
}

Live Demo outputs:

(5, Hello, -0.1)

given

auto a = std::make_tuple(5, "Hello", -0.1);
print(a);

Explanation

Our unary left fold is of the form

... op pack

where op in our scenario is the comma operator, and pack is the expression containing our tuple in an unexpanded context like:

(..., (std::cout << std::get<I>(myTuple))

So if I have a tuple like so:

auto myTuple = std::make_tuple(5, "Hello", -0.1);

And a std::integer_sequence whose values are specified by a non-type template (see above code)

size_t... I

Then the expression

(..., (std::cout << std::get<I>(myTuple))

Gets expanded into

((std::cout << std::get<0>(myTuple)), (std::cout << std::get<1>(myTuple))), (std::cout << std::get<2>(myTuple));

Which will print

5Hello-0.1

Which is gross, so we need to do some more trickery to add a comma separator to be printed first unless it's the first element.

To accomplish that, we modify the pack portion of the fold expression to print " ," if the current index I is not the first, hence the (I == 0? "" : ", ") portion*:

(..., (std::cout << (I == 0? "" : ", ") << std::get<I>(_tup)));

And now we'll get

5, Hello, -0.1

Which looks nicer (Note: I wanted similar output as this answer)

*Note: You could do the comma separation in a variety of ways than what I ended up with. I initially added commas conditionally after instead of before by testing against std::tuple_size<TupType>::value - 1, but that was too long, so I tested instead against sizeof...(I) - 1, but in the end I copied Xeo and we ended up with what I've got.

Community
  • 1
  • 1
AndyG
  • 35,661
  • 8
  • 94
  • 126
19

I got this working fine in C++11 (gcc 4.7). There are I am sure some pitfalls I have not considered but I think the code is easy to read and and not complicated. The only thing that may be strange is the "guard" struct tuple_printer that ensure that we terminate when the last element is reached. The other strange thing may be sizeof...(Types) that return the number of types in Types type pack. It is used to determine the index of the last element (size...(Types) - 1).

template<typename Type, unsigned N, unsigned Last>
struct tuple_printer {

    static void print(std::ostream& out, const Type& value) {
        out << std::get<N>(value) << ", ";
        tuple_printer<Type, N + 1, Last>::print(out, value);
    }
};

template<typename Type, unsigned N>
struct tuple_printer<Type, N, N> {

    static void print(std::ostream& out, const Type& value) {
        out << std::get<N>(value);
    }

};

template<typename... Types>
std::ostream& operator<<(std::ostream& out, const std::tuple<Types...>& value) {
    out << "(";
    tuple_printer<std::tuple<Types...>, 0, sizeof...(Types) - 1>::print(out, value);
    out << ")";
    return out;
}
Tony Olsson
  • 229
  • 2
  • 2
  • 1
    Yeah, that looks sensible - perhaps with another specialization for the empty tuple, for completeness. – Kerrek SB Jul 04 '13 at 15:24
  • @KerrekSB, There isn't a simple way to print tuples in c++?, in python function implicitly returns a tuple and you can simply print them, in c++ in order to return the multiple variables from a function I need to pack them using `std::make_tuple()`. but at the time of printing it in `main()`, it throws a bunch of errors!, Any suggestions on simpler way to print the tuples? – Anu Jan 14 '19 at 22:12
17

I'm surprised the implementation on cppreference has not already been posted here, so I'll do it for posterity. It's hidden in the doc for std::tuple_cat so it's not easy to find. It uses a guard struct like some of the other solutions here, but I think theirs is ultimately simpler and easier-to-follow.

#include <iostream>
#include <tuple>
#include <string>

// helper function to print a tuple of any size
template<class Tuple, std::size_t N>
struct TuplePrinter {
    static void print(const Tuple& t) 
    {
        TuplePrinter<Tuple, N-1>::print(t);
        std::cout << ", " << std::get<N-1>(t);
    }
};

template<class Tuple>
struct TuplePrinter<Tuple, 1> {
    static void print(const Tuple& t) 
    {
        std::cout << std::get<0>(t);
    }
};

template<class... Args>
void print(const std::tuple<Args...>& t) 
{
    std::cout << "(";
    TuplePrinter<decltype(t), sizeof...(Args)>::print(t);
    std::cout << ")\n";
}
// end helper function

And a test:

int main()
{
    std::tuple<int, std::string, float> t1(10, "Test", 3.14);
    int n = 7;
    auto t2 = std::tuple_cat(t1, std::make_pair("Foo", "bar"), t1, std::tie(n));
    n = 10;
    print(t2);
}

Output:

(10, Test, 3.14, Foo, bar, 10, Test, 3.14, 10)

Live Demo

AndyG
  • 35,661
  • 8
  • 94
  • 126
4

Based on AndyG code, for C++17

#include <iostream>
#include <tuple>

template<class TupType, size_t... I>
std::ostream& tuple_print(std::ostream& os,
                          const TupType& _tup, std::index_sequence<I...>)
{
    os << "(";
    (..., (os << (I == 0 ? "" : ", ") << std::get<I>(_tup)));
    os << ")";
    return os;
}

template<class... T>
std::ostream& operator<< (std::ostream& os, const std::tuple<T...>& _tup)
{
    return tuple_print(os, _tup, std::make_index_sequence<sizeof...(T)>());
}

int main()
{
    std::cout << "deep tuple: " << std::make_tuple("Hello",
                  0.1, std::make_tuple(1,2,3,"four",5.5), 'Z')
              << std::endl;
    return 0;
}

with output:

deep tuple: (Hello, 0.1, (1, 2, 3, four, 5.5), Z)
4

Leveraging on std::apply (C++17) we can drop the std::index_sequence and define a single function:

#include <tuple>
#include <iostream>

template<class Ch, class Tr, class... Args>
auto& operator<<(std::basic_ostream<Ch, Tr>& os, std::tuple<Args...> const& t) {
  std::apply([&os](auto&&... args) {((os << args << " "), ...);}, t);
  return os;
}

Or, slightly embellished with the help of a stringstream:

#include <tuple>
#include <iostream>
#include <sstream>

template<class Ch, class Tr, class... Args>
auto& operator<<(std::basic_ostream<Ch, Tr>& os, std::tuple<Args...> const& t) {
  std::basic_stringstream<Ch, Tr> ss;
  ss << "[ ";
  std::apply([&ss](auto&&... args) {((ss << args << ", "), ...);}, t);
  ss.seekp(-2, ss.cur);
  ss << " ]";
  return os << ss.str();
}
DarioP
  • 5,010
  • 1
  • 28
  • 48
3

Based upon example on The C++ Programming Language By Bjarne Stroustrup, page 817:

#include <tuple>
#include <iostream>
#include <string>
#include <type_traits>
template<size_t N>
struct print_tuple{
    template<typename... T>static typename std::enable_if<(N<sizeof...(T))>::type
    print(std::ostream& os, const std::tuple<T...>& t) {
        char quote = (std::is_convertible<decltype(std::get<N>(t)), std::string>::value) ? '"' : 0;
        os << ", " << quote << std::get<N>(t) << quote;
        print_tuple<N+1>::print(os,t);
        }
    template<typename... T>static typename std::enable_if<!(N<sizeof...(T))>::type
    print(std::ostream&, const std::tuple<T...>&) {
        }
    };
std::ostream& operator<< (std::ostream& os, const std::tuple<>&) {
    return os << "()";
    }
template<typename T0, typename ...T> std::ostream&
operator<<(std::ostream& os, const std::tuple<T0, T...>& t){
    char quote = (std::is_convertible<T0, std::string>::value) ? '"' : 0;
    os << '(' << quote << std::get<0>(t) << quote;
    print_tuple<1>::print(os,t);
    return os << ')';
    }

int main(){
    std::tuple<> a;
    auto b = std::make_tuple("One meatball");
    std::tuple<int,double,std::string> c(1,1.2,"Tail!");
    std::cout << a << std::endl;
    std::cout << b << std::endl;
    std::cout << c << std::endl;
    }

Output:

()
("One meatball")
(1, 1.2, "Tail!")
CW Holeman II
  • 4,009
  • 7
  • 37
  • 60
1

Another one, similar to @Tony Olsson's, including a specialization for the empty tuple, as suggested by @Kerrek SB.

#include <tuple>
#include <iostream>

template<class Ch, class Tr, size_t I, typename... TS>
struct tuple_printer
{
    static void print(std::basic_ostream<Ch,Tr> & out, const std::tuple<TS...> & t)
    {
        tuple_printer<Ch, Tr, I-1, TS...>::print(out, t);
        if (I < sizeof...(TS))
            out << ",";
        out << std::get<I>(t);
    }
};
template<class Ch, class Tr, typename... TS>
struct tuple_printer<Ch, Tr, 0, TS...>
{
    static void print(std::basic_ostream<Ch,Tr> & out, const std::tuple<TS...> & t)
    {
        out << std::get<0>(t);
    }
};
template<class Ch, class Tr, typename... TS>
struct tuple_printer<Ch, Tr, -1, TS...>
{
    static void print(std::basic_ostream<Ch,Tr> & out, const std::tuple<TS...> & t)
    {}
};
template<class Ch, class Tr, typename... TS>
std::ostream & operator<<(std::basic_ostream<Ch,Tr> & out, const std::tuple<TS...> & t)
{
    out << "(";
    tuple_printer<Ch, Tr, sizeof...(TS) - 1, TS...>::print(out, t);
    return out << ")";
}
Gabriel
  • 2,390
  • 4
  • 27
  • 41
1

I like DarioP's answer, but stringstream uses heap. This can be avoided:

template <class... Args>
std::ostream& operator<<(std::ostream& os, std::tuple<Args...> const& t) {
  os << "(";
  bool first = true;
  std::apply([&os, &first](auto&&... args) {
    auto print = [&] (auto&& val) {
      if (!first)
        os << ",";
      (os << " " << val);
      first = false;
    };
    (print(args), ...);
  }, t);
  os << " )";
  return os;
}
user2445507
  • 425
  • 3
  • 12
0

One thing I dislike about the previous answers that use fold expressions is that they use index sequences or flags to keep track of the first element, which removes much of the benefit of nice clean fold expressions.

Here is an example that does not need indexing, but achieves a similar result. (Not as sophisticated as some of the others, but more could be added.)

The technique is to use what the fold already gives you: a special case for one element. I.e., one element fold just expands to elem[0], then 2 elements is elem[0] + elem[1], where + is some operation. What we want is for one element to write just that element to the stream, and for more elements, do the same, but join each one with an additional write of ", ". So mapping this on to the c++ fold, we want each element to be the action of writing some object to the stream. We want our + operation to be to intersperse two writes with a ", " write. So first transform our tuple sequence into a sequence of write actions, CommaJoiner I have called it, then for that action add an operator+ to join two actions in the way we want, adding a ", " in between:

#include <tuple>
#include <iostream>

template <typename T>
struct CommaJoiner
{
    T thunk;
    explicit CommaJoiner(const T& t) : thunk(t) {}

    template <typename S>
    auto operator+(CommaJoiner<S> const& b) const
    {
        auto joinedThunk = [a=this->thunk, b=b.thunk] (std::ostream& os) {
            a(os);
            os << ", ";
            b(os);
        };
        return CommaJoiner<decltype(joinedThunk)>{joinedThunk};
    }

    void operator()(std::ostream& os) const
    {
        thunk(os);
    }

};

template <typename ...Ts>
std::ostream& operator<<(std::ostream& os, std::tuple<Ts...> tup)
{
    std::apply([&](auto ...ts) {
        return (... + CommaJoiner{[=](auto&os) {os << ts;}});}, tup)(os);

    return os;
}

int main() {
    auto tup = std::make_tuple(1, 2.0, "Hello");
    std::cout << tup << std::endl;
}

A cursory glance at godbolt suggests that this compiles quite well too, all the thunks calls being flattened.

This will need a second overload to deal with an empty tuple however.

tahsmith
  • 1,463
  • 15
  • 22
0

I see answers using std::index_sequence with C++17, however, that's not the road I'd go personally. I'd rather go for recursion and constexpr if:

#include <tuple>
#include <iostream>

template<std::size_t I, class... Ts>
void doPrintTuple(const std::tuple<Ts...>& tuples) {
    if constexpr (I == sizeof...(Ts)) {
        std::cout << ')';
    }
    else {
        std::cout << std::get<I>(tuples);
        if constexpr (I + 1 != sizeof...(Ts)) {
            std::cout << ", ";
        }
        doPrintTuple<I + 1>(tuples);
    }
}

template<class... Ts>
void printTuple(const std::tuple<Ts...>& tuples) {
    std::cout << '(';
    doPrintTuple<0>(tuples);
}

int main() {
    auto tup = std::make_tuple(1, "hello", 4.5);
    printTuple(tup);
}

Output:

(1, hello, 4.5)
Marc Dirven
  • 149
  • 1
  • 13
-1

Here's some code I recently made for printing a tuple.

#include <iostream>
#include <tuple>

using namespace std;

template<typename... Ts>
ostream& operator<<(ostream& output, const tuple<Ts...> t) {
    output << '(';
    apply([&](auto&&... args) {
        ((cout << args << ", "), ...);
    }, t);
    output << "\b\b";
    output << ')';
    return output;
}

Using your example case:

auto a = std::make_tuple(5, "Hello", -0.1); 
cout << a << '\n'; // (5, Hello, -0.1)
xKaihatsu
  • 122
  • 9