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

Doxygen and C++: <i>not</i> a marriage made in heaven

19 August, 200529 July, 2007

I briefly interrupt your scheduled browsing… Doxygen is a documentation system for C++, C, Java, Objective-C, Python, IDL (Corba and Microsoft flavors) and to some extent PHP, C#, and D. So it says on the website, and so it is. It’s also a Good Thing, most people would assume. Doxygen…

Read More

Functional Fills with Vecto

25 March, 200825 March, 2008

I’ve been wanting to do exclusive-or functional fill in vecto for a while, so tonight I delved in. I added a pixel function to the graphics state and kept the default as the normal alpha-blending it was already doing. The pixel function signature is a bit clumsy, but it was…

Read More

Lameness Explained

9 December, 20159 December, 2015

OK, more than one person wanted explanations of The C++ <random> Lame List, so here are some of my thoughts, if only to save people searching elsewhere. Calling rand() is lame because it’s an LCG with horrible randomness properties, and we can do better. And if you’re not calling rand(),…

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