Skip to content
Why is a raven like a writing desk?

Thoughts both confusing and enlightening.

Why is a raven like a writing desk?

Thoughts both confusing and enlightening.

How to print anything in C++ (part 5)

elbeno, 2 February, 201530 June, 2015

Part 1 Part 2 Part 3 Part 4 Part 5 Postscript

So far, we can print containers, but what about arrays? And what about “pretty-printing” strings – perhaps we need to wrap them with quotes. Well, we know that with the existing code, both arrays and strings count as outputtable. Both std::string and char* (and its variously const friends) can be passed to operator<<, and so can arrays, because they decay to pointers.

So, we should be able to deal with this using common-or-garden specialization on stringifier_select. Specializing for std::string is easy:

template 
struct stringifier_select, 
                          is_outputtable_tag>
{
  using S = std::basic_string;
  explicit stringifier_select(const S& t) : m_t(t) {}

  std::ostream& output(std::ostream& s) const
  {
    return s << C('\"') << m_t << C('\"');
  }

  const S& m_t;
};

And specializing for the other char* related types is also as easy, albeit verbose because of dealing with char*, const char*, char* const and const char* const.

Specializing for arrays of char is just as easy. This time we have just the array size as a template argument. And once again, there is a specialization for arrays of const char that is identical.

template 
struct stringifier_select
{
  using S = char[N];
  explicit stringifier_select(const S& t) : m_t(t) {}

  std::ostream& output(std::ostream& s) const
  {
    return s << '\"' << m_t << '\"';
  }

  const S& m_t;
};

Specializing for arrays (other than of char) is also easy, and gives us a chance to abstract out the code that we used for the iterable printing. By happy circumstance (i.e. by design!), arrays support std::begin() and std::end(), so we can write the following:

template 
std::ostream& output_iterable(std::ostream& s, const T& t)
{
  s << iterable_opener()(t);
  auto b = std::begin(t);
  auto e = std::end(t);
  if (b != e)
    s << prettyprint(*b);
  std::for_each(++b, e,
                [&s, &t] (auto& e)
                { s << iterable_separator()(t)
                    << prettyprint(e); });
  return s << iterable_closer()(t);
}

template 
struct stringifier_select
{
  using S = T[N];
  explicit stringifier_select(const S& t) : m_t(t) {}

  std::ostream& output(std::ostream& s) const
  {
    return output_iterable(s, m_t);
  }

  const S& m_t;
};

And the code for printing iterable things changes likewise. Unlike the situation with char*, we don't need to deal with const and non-const separately because here, T itself is inferred to be const or not.

And that's pretty much it - just a couple more things to add. I mentioned enum classes back in part 1, and here's how we print out their values:

template 
struct stringifier_select
{
  explicit stringifier_select(T t) : m_t(t) {}

  std::ostream& output(std::ostream& s) const
  {
    return s << static_cast>(m_t);
  }

  T m_t;
};

Simple. Two final things to add: first, specialize for pretty-printing bool equivalently to using std::boolalpha; second, distinguish a few "unprintable" things and output something for them - classes without operator<<, unions, nullptr. The code that does this is very similar to what we've already seen.

So now, we can pretty-print all "normally" printable things, containers, pairs and tuples, callable things, certain unprintables that we can meaningfully label, and really unprintable things with a fallback. I think that'll do for the time being. It's been a fun journey, exploring TMP techniques, C++ type support, mapping over tuples, and the amazing void_t.

You can find all the resulting code from this exercise on github.

C++ Programming

Post navigation

Previous post
Next post

Related Posts

Why Reading Books Matters to a Programmer

29 April, 200829 April, 2008

The programming book market these days is small. Nothing like what it was eight years ago or so. And apparently, programmers don’t read books (any more). It’s mostly true. But of course, there are still books worth reading. I’m going to take as read the easy arguments: let’s assume that…

Read More

More on ellipses

1 March, 20081 March, 2008

I think I will use the same method as I do for Bézier curves to step along the circumference. Another generalisation I had to make from the circle code is with respect to the normal. For a circle of radius r, centred on the origin, and parameterised by angle θ,…

Read More

A Crossword for CppCon 2024

12 September, 202411 September, 2024

As well as being a C++ enthusiast, I’m a bit of a cruciverbalist. I do the Guardian cryptic every day (not in a particularly competitive time — I’d count sub-20 minutes as a good day), and sometimes others when I can. Like many Brits, I learned to do cryptic crosswords…

Read More

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

©2026 Why is a raven like a writing desk? | WordPress Theme by SuperbThemes