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 1)

elbeno, 1 February, 201530 June, 2015

Part 1 Part 2 Part 3 Part 4 Part 5 Postscript

I thought I’d have a go at writing some code that could print things. A pretty-printer, if you like. What I want to be able to do is this:

 // Print x correctly, where x is ANY type.
cout << prettyprint(x) << endl;

Obviously this is going to involve templates since the type of x may vary.

There are a few things that immediately spring to mind:

  • Maybe x is already printable, that is, operator<< might work "out of the box".
  • If x is a container, we want to do something with that and print the elements.
  • We probably want to print elements of pair and tuple also.
  • The value of some things is not printable, or at least not sensible to print. For example, functions.

The basic plan that came to mind is this: prettyprint is a function template, which returns an object that wraps the x and has operator<< defined. This class will itself be a template, and the template instantiation we choose will be determined by what sort of thing x is.

This suggest that the very first thing we can write looks something like this:

template 
struct stringifier
{
  explicit stringifier(const T&) {}

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

template 
inline std::ostream& operator<<(std::ostream& s, 
                                const stringifier& t)
{
  return t.output(s);
}

template 
inline stringifier prettyprint(T&& t)
{
  return stringifier(std::forward(t));
}

So far so good. Now we can print "<unknown>" for any type. We have a place to start specializing things.

A quick poke around C++ type support and some experiments soon fleshed out the plan:

  1. There are at least 6 types of callable things: functions, member functions, std::functions, bind expressions, lambdas, and objects that support operator() (function objects). All of these are "unprintable". Lambdas are a kind of function object; I don't know whether they can be meaningfully distinguished or whether that is desirable.
  2. nullptr is its own type and unprintable.
  3. enum class values are not printable out-of-the-box; they will require a cast to std::underlying_type.
  4. pairs should be easy to deal with, but tuples will require some effort. They are designed for static access, not iteration.

So now I have 4 broad categories I want to represent for printing: already-outputtable, unprintable, callable, and containers/pairs/tuples. I decided to make tag types for each specializable thing so as to be able to select on them.

struct is_iterable_tag {};
struct is_pair_tag {};
struct is_tuple_tag {};
struct is_callable_tag {};
struct is_outputtable_tag {};
struct is_enum_tag {};
struct is_unprintable_tag {};

As luck would have it, several of these already have built-in support for detecting:

template
using is_unprintable = typename std::conditional<
  std::is_union::value ||
  std::is_class::value ||
  std::is_null_pointer::value,
  std::true_type, std::false_type>::type;

// similarly we can use: 
// std::is_enum::value to detect enum or enum class

Detecting pairs and tuples is also easy, using a standard specialization pattern:

template 
struct is_pair : public std::false_type {};

template 
struct is_pair> : public std::true_type {};

template 
struct is_tuple : public std::false_type {};

template 
struct is_tuple> : public std::true_type {};

A good setup so far. We can detect concrete types. Next, how to detect whether something already supports operator<<.

C++ Programming

Post navigation

Previous post
Next post

Related Posts

Even more on ellipses and splines

15 March, 200815 March, 2008

Honestly, they’re incredibly interesting. Anyway, I’ll skip straight to the pièce de résistance: This is a 4-curve cubic Bézier spline modulated onto an ellipse. The ellipse [a=4, b=3] is at an angle of π/4. C1 continuity of the complete curve is preserved. The flickr set tells the story of how…

Read More

Experimenting with (cubic) Bézier subdivision

27 February, 20081 March, 2008

As you know, a Bézier curve (here meaning specifically a cubic Bézier) is often used for drawing all kinds of things in vector graphics. It has the nice property that the endpoints and control points form a bounding box, and deCasteljau’s algorithm is a nice numerically stable way of evaluating…

Read More

Sneak preview

19 February, 200828 February, 2008

This is a sneak peek at my current personal project. A bit of computerised heraldry. (“Sable a [ordinary] ermine.” Left to right, top to bottom: chief, fess, pale, cross, bend, bend sinister, chevron, saltire. Yeah, I know; charges on a bend should be bendwise. It’s a work in progress.)

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