-
Notifications
You must be signed in to change notification settings - Fork 73
/
Copy pathprettyprint.tex
191 lines (157 loc) · 7.71 KB
/
prettyprint.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
\documentclass[a4paper,11pt]{article}
\title{A Container Pretty-Printer for C++}
\author{Louis ``Kerrek SB'' Delacroix et al.\thanks{See the ``Acknowledgements'' for a full list of contributors,}}
\date{}
\usepackage[margin=3cm]{geometry}
\begin{document}
\maketitle
\begin{abstract}\noindent
We present a header-only library for C++ which enables formatted
output for arbitrary containers in the usual style like \texttt{std::cout
<< x << std::endl;}. The only requirements on the type \texttt{T} of
\texttt{x} is that it have a member type \texttt{T::const\_iterator} and
methods \texttt{T::begin()} and \texttt{T::end()}.
\end{abstract}
\section*{Introduction}
Templated containers are fundamental in C++ and occur ubiquitously. Yet
producing formatted output of containers requires either a loop ranging
over the container's elements, or an overload of \texttt{operator<<} for
the desired container. One frequently finds oneself writing the same
pattern of code time and time again. It would be handy if printing a
container would ``just work''. This is what the \texttt{prettyprint}
library achieves.
\paragraph{Expectations.} This library was designed to meet the following
generic expectations for formatted output of a container:
\begin{itemize}
\item The empty container should print as ``\texttt{[]}''.
\item A container with one element should print as ``\texttt{[3]}''.
\item A container with $n$ elements should print as ``\texttt{[4, -1, 11, 8]}''.
\item The container's elements should be printed by recursively invoking formatted output.
\end{itemize}
Formatted container output in this library has three parameters: The
opening delimiter, the closing delimiter, and the separator. All three
parameters should be customizable, but sensible defaults should be
provided so that formatted output works without any action on the
user's part. We chose to select the following defaults:
\begin{itemize}
\item The separator is always ``\texttt{, }''.
\item Pairs and tuples appear in round parentheses: ``\texttt{(a, b)}''.
\item Sets and (set-likes) appear in curly braces: ``\texttt{\{3, -7, 18\}}''.
\item All other containers appear in square brackets: ``\texttt{[2, 2, 0, 1]}''.
\end{itemize}
These defaults are inspired by the prevailing typographic tradition
of mathematical notation, and we hope that they are both intuitive and
\ae{}sthetically pleasing.
Note that key-value containers are not treated specially, since their value
type is simply \texttt{std::pair<key\_type, mapped\_type>}.
\section*{Acknowledgements}
Credits go to Marcelo Cantos for the initial approach, to Sven Groot
for an improved, self-contained solution which became the foundation
for this code, and to StackOverflow's Xeo for the tuple-printing code.
This library would not have been possible without the support of these
people.
\section*{Installation and Usage}
Simply make the header file \texttt{prettyprint.hpp} available and include
it in your code. Containers can be printed immediately:
\begin{verbatim}#include <vector>
#include <map>
#include <set>
#include <iostream>
#include "prettyprint.hpp"
int main()
{
std::vector<int> v;
std::set<double> s;
std::map<size_t, void*> m;
int a[10];
/* populate containers */
std::cout << "Vector: " << v << std::endl
<< "Set: " << s << std::endl
<< "Map: " << m << std::endl
<< "C-Array: " << pretty_print::array_wrapper(a) << std::endl;
}
\end{verbatim}
This will output something like the following:
\begin{verbatim}Vector: [1, 2, 3]
Set: {4.2, -1.3, NaN}
Map: [(15, CD01EF23), (9, 19283ABC), (25, D2A92158)]
C-Array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
\end{verbatim}
\paragraph{C-style arrays.} In the usage example above we introduced
a helper function called \texttt{pretty\_print::array\_wrapper} which
can be used to print C-style arrays in the same fashion as other
containers. It comes in two overloads, one for compile-time sized
arrays and one for dynamically sized arrays:
\begin{verbatim}
size_t n = get_number(), m = get_number();
int a[] = {1, 2, 4, 8}; // stack array of static size 4
int * b = new int[n]; // heap array of dynamic size n
int c[m]; // stack array of dynamic size m (VLA)
std::cout << pretty_print::array_wrapper(a) << std::endl // 1
<< pretty_print::array_wrapper(b, n) << std::endl // 2
<< pretty_print::array_wrapper(c, m) << std::endl // 2
;
\end{verbatim}
In case (1) the array size is deduced at compile time; in cases (2) and (3) the
array size has to be passed explicitly to the wrapper function.
\section*{Advanced Features: Customizing Delimiters}
\paragraph{Note.} All interna of the library live in the namespace \texttt{pretty\_print}.\bigskip
The only advanced feature of the library at this point is the ability to customize
the delimiters used during printing. There are two ways to do this:
\begin{itemize}
\item Create a (partial or explicit) specialisation of a helper class
for a specific container type and use the usual output syntax.
\item Create a helper class holding custom delimiters and invoking
a modified, slightly more verbose output function.
\end{itemize}
The two approaches meet orthogonal needs: If you have a container that is
used repeatedly and which always must appear with custom delimiters, use the
first approach and specialize the \texttt{delimiters} struct for your container
type. On the other hand, if you have a set of delimiters that you only want to
use in certain situations (but with any sort of container), create a helper class
to hold your delimiters and invoke output with the \texttt{custom\_delims} function
like this:\begin{center}\texttt{std::cout << pretty\_print::custom\_delims<MyDelims>(x) << std::endl;}\end{center}
\subsection*{Method 1: Specialize.} The default delimiter class
is: \begin{center}\texttt{template <typename T, typename TChar> struct
pretty\_print::delimiters;}\end{center} All delimiter classes are
templated on a character type \texttt{TChar}, which should match the
desired output stream parameter (i.e.\ \texttt{char} for
\texttt{ostream} and \texttt{wchar\_t} for \texttt{wostream}), and on
the container type \texttt{T}. The class has a static member constant
\texttt{values} of type \texttt{delimiters\_values<T, TChar>}, which itself is
defined like this:
\begin{verbatim}template<typename TChar> struct delimiters_values
{
typedef TChar char_type;
const TChar * prefix;
const TChar * delimiter;
const TChar * postfix;
};
\end{verbatim}
By providing an explicit or partial specialization of \texttt{delimiters},
the delimiter parameters can be changed globally for a specific container
type.
\paragraph{Example 1.} We specialize for \texttt{std::vector<double>}
to print ``\texttt{((1.2; -3.4; 5.6))}''.
\begin{verbatim}template <> const pretty_print::delimiters_values<char>
pretty_print::delimiters<std::vector<double>, char>::values = { "((", "; ", "))" };
\end{verbatim}
\paragraph{Example 2.} We specialize for all \texttt{std::list<T>} to print ``\texttt{a--b--c}''.
\begin{verbatim}template <typename T> const pretty_print::delimiters_values<char>
pretty_print::delimiters<std::list<T>, char>::values = { "", "--", "" };
\end{verbatim}
\subsection*{Method 2: Custom delimiter class.}
Instead of providing specializations for the default delimiter class, we can also
create our own delimiter class. It must provide a static member constant \texttt{values}
just like the default class. For example:
\begin{verbatim}struct HalfOpen
{
static const pretty_print::delimiters_values<wchar_t> values;
};
const pretty_print::delimiters_values<wchar_t>
HalfOpen::values = { L"[", L", L", ")" };
std::wcout << pretty_print::custom_delims<HalfOpen>(x) << std::endl;
\end{verbatim}
\section*{Implementation}
[To be written.]
\end{document}