cxx-prettyprint: A C++ Container Pretty-Printer

Please visit the source code repository on GitHub.

Download: prettyprint.hpp (C++11), prettyprint98.hpp (C++98/03), or the zip ball or tar.gz ball.

Updates

Aug. 2014: Some refactoring of the C++11 code. Tuples and pairs are now containers, and the special logic has been moved into a dedicated template used for printing the container body. This removes several dodgy overloads from the namespace std. Some old code and comments have also been removed, and more demo cases added.

Nov. 2011: There is now a new wrapper bucket_print, which iterates only over one bucket of containers based on hash tables (such as the unordered_* containers in the standard library). See below for details.

Breaking news (July 11, 2011)

We are thrilled to announce that the indomitable Stephan T Lavavej has been inspired by this library to make an episode of Advanced STL about container pretty printing — thank you very much!

Watch “Advanced STL #6” now!

We will try to incorporate some of STL’s excellent ideas into this library, stay tuned. In the meantime, check out his own pretty printer code!

Updates: Following STL’s ideas, the library has now been improved to support static arrays directly without the need for a wrapper. Also, the library should now be usable in MS Visual Studio 2010.

Furthermore, there is now a version that works in C++98/03 compliant compilers. It uses TR1 tuples by default, but all dependence on TR1 libraries can be removed by defining NO_TR1.

Quick start

Download prettyprint.hpp and place it in your source directory. Add #include "prettyprint.hpp" to your source file. You are ready!

#include <iostream> #include <vector> #include "prettyprint.hpp" int main() { std::vector<int> foo; foo.push_back(1); foo.push_back(2); foo.push_back(3); std::cout << "My vector: " << foo << std::endl; }

Note: This library relies on C++11 features. With gcc, pass the option -std=c++11.

Description

cxx-prettyprint is a header-only library for C++ which provides printing via the <<-operator for any container class. Before we go into the details, let us quickly think about what we need to do:

The cxx-prettyprint library provides precisely this functionality. We are using the following default settings for the delimiters (but see below for how to customize):

These lexical conventions attempt to mimic popular mathematical notation for tuples, sets and vectors, respectively.

Example

Suppose we map integers to sets of strings.

typedef std::map<int, std::set<std::string>>; map_type; map_type foo = { { 1, std::set<std::string>{ std::string("cat"), std::string("dog"), std::string("doe") } }, { 3, std::set<std::string>{ std::string("goose"), std::string("moose"), std::string("ruse") } } }; std::cout << foo << std::endl;

The output would look like so:

[(1, {cat, doe, dog}), (3, {goose, moose, ruse})]

Raw C arrays

Raw C arrays are handled the same way as “proper” containers:

int arr[] = { 1, 4, 9, 16 }; std::cout << arr << std::endl;

However, if the array size is not known at compile time, a wrapper may be invoked via pretty_print_array(arr, size).

Hash containers

Certain containers based on hash tables offer an additional interface to the internal buckets of the hash table. This interface comes in the form of local iterators, accessed via begin(n) and end(n), where n indicates the bucket number. Examples of such containers are the unordered_* containers from the standard libray.

Individual buckets may be printed with the wrapper bucket_print, used as follows:

std::unordered_map<std::string, int> m; fill_map(m); // populate map for (unsigned int i = 0; i != m.bucket_count(); ++i) { std::cout << "Bucket " << i << ": " << bucket_print(m, i) << std::endl; }

Advanced usage: Customizing the delimiters

The delimiters that are used by the output operation are deduced from compile-time static class constants. In particular, if T denotes any container type, then the appropriate delimiters are defined by the following templated structure:

namespace pretty_print { template<typename TChar> struct delimiters_values { typedef TChar char_type; const TChar * prefix; const TChar * delimiter; const TChar * postfix; }; template<typename T, typename TChar> struct delimiters { typedef delimiters_values<TChar> type; static const type values; }; } // namespace

The templates are parametrized on TChar, the type of the character, which is char for std::cout and wchar_t for std::wcout. You must define delimiters<T, TChar>::values for your container type T. For example, the default delimiters for TChar = char are declared as follows:

template<typename T> struct delimiters<T, char> { static const delimiters_values<char> values; }; template<typename T> const delimiters_values<char> delimiters<T, char>::values = { "[", ", ", "]" };

Overriding delimiters

The most straight-forward way to override delimiters for a particular container is to specialize the delimiters struct accordingly. For example, if we want special delimiters for a vector of doubles, we specialize as follows:

template<> const pretty_print::delimiters_values<char> pretty_print::delimiters<std::vector<double>, char>::values = { "< ", " ; ", " >" };

An alternative solution is to provide your own delimiters class. That is a class with a public, static constant named values and of type pretty_print::delimiters_values<TChar>, where you must provide a concrete instance of TChar suitable for your output operation (usually char). With such a class, say MyDelims, you can invoke a wrapper class for any container, say v:

std::cout << pretty_print::custom_delims<MyDelims>(v) << std::endl;

(The wrapper class custom_delims uses type erasure to call the relevant internal helper instance of the type of v.) Your delimiter class could for example be declared as follows:

struct MyDelims { static const pretty_print::delimiters_values<char> values; }; const pretty_print::delimiters_values<char> MyDelims::values = { "<", "; ", ">" };

As a rule of thumb: Specialize pretty_print::delimiters if you want uniform delimiters for a particular container type, and use the pretty_print::custom_delims wrapper with your own delimiter class if you want to use a fixed set of delimiters for a variety of containers on a one-off basis.

Internals

The key component of the library is a combination of std::enable_if together with a typetrait is_container<T>. This type trait uses the SFINAE (substitution failure is not an error) model to determine whether a type T::const_iterator exists. If yes, operator<<() is overloaded for type T, and a loop from begin() to end() is performed, inserting the appropriate delimiters.

The custom_delims helper class uses type-erasure to take an arbitrary container object and instantiate the correct template of the internal helper class with the user-provided delimiter class.

Versions

There are two versions of the library: One for C++0x compliant compilers and MS Visual C++ 2010, called “prettyprint.hpp”, and one for C++98/03 compliant compilers called “prettyprint98.hpp”. The latter makes use of the TR1 libraries to support tuples, but this dependence (and support for tuples) can be disabled by defining NO_TR1.

Credits

This library is the product of many great contributions to my questions on StackOverflow and on Channel 9. Here is the original StackOverflow working topic, here is the follow-up on printing tuples, and finally the Channel 9 thread where it all came together. Credits go to Marcelo Cantos for the initial approach, to Sven Groot for an improved, self-contained solution which became the foundation for my code, and to Xeo for the tuple-printing code.

To do

Possible avenues for future improvements:

If you like to help out, just send me a message on GitHub, email me patches or suggestions, or send pull requests.

louisdx