Search code examples
c++c++11tuplesvariadic-templatespretty-print

Anatomy of pretty print tuple


A while ago, a solution to print out std::tuple was posted here. For the most part I get what's happening. I'm having trouble understanding whats happening in the print_tuple function though.

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

I don't get what's happening in the body of this function. As far as I can tell, it has something to do with unpacking Is. I get that the condition, Is == 0 is checking to see if we're at the head element.

So what's going on?


Solution

  • Let's go through an example with an arbitrary tuple, say:

    using Tuple = tuple<char, int, string>;
    

    Thus, the integer sequence that our function will be called with is:

    seq<0, 1, 2>
    

    And our pack expansion in the body is:

    (void)swallow{0, (void(os << (Is == 0? "" : ", ") << std::get<Is>(t)), 0)...};
    

    which, if we manually expand it the way the compiler would, becomes:

    (void)swallow{0,
                  (void(os << (0 == 0? "" : ", ") << std::get<0>(t)), 0),
                  (void(os << (1 == 0? "" : ", ") << std::get<1>(t)), 0),
                  (void(os << (2 == 0? "" : ", ") << std::get<2>(t)), 0)
                  };
    

    And then evaluating the branches:

    (void)swallow{0,
                  (void(os << "" << std::get<0>(t)), 0),
                  (void(os << ", " << std::get<1>(t)), 0),
                  (void(os << ", " << std::get<2>(t)), 0)
                  };
    

    Which is to say, we're constructing an integer array of 4 0s, with the side effect of printing out the contents of the tuple, comma-separated, making sure we don't start with an extra comma. The four expressions must be evaluated in order, so that guarantees that the tuple contents get printed in order.

    The initial (void) cast is just there to avoid the unused-variable warning that compilers should emit if you have all warnings turned on. The initial 0 in the array initialization handles the case where the tuple is empty.