]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/type_erasure/example/printf.cpp
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / boost / libs / type_erasure / example / printf.cpp
CommitLineData
7c673cae
FG
1// Boost.TypeErasure library
2//
3// Copyright 2012 Steven Watanabe
4//
5// Distributed under the Boost Software License Version 1.0. (See
6// accompanying file LICENSE_1_0.txt or copy at
7// http://www.boost.org/LICENSE_1_0.txt)
8//
9// $Id$
10
11//[printf
12/*`
13 (For the source of this example see
14 [@boost:/libs/type_erasure/example/printf.cpp printf.cpp])
15
16 This example uses the library to implement a type safe printf.
17
18 [note This example uses C++11 features. You'll need a
19 recent compiler for it to work.]
20 */
21
22#include <boost/type_erasure/builtin.hpp>
23#include <boost/type_erasure/operators.hpp>
24#include <boost/type_erasure/any_cast.hpp>
25#include <boost/type_erasure/any.hpp>
26#include <boost/mpl/vector.hpp>
27#include <boost/io/ios_state.hpp>
28#include <iostream>
29#include <sstream>
30#include <iomanip>
31#include <vector>
32#include <string>
33
34namespace mpl = boost::mpl;
35using namespace boost::type_erasure;
36using namespace boost::io;
37
38// We capture the arguments by reference and require nothing
39// except that each one must provide a stream insertion operator.
40typedef any<
41 mpl::vector<
42 typeid_<>,
43 ostreamable<>
44 >,
45 const _self&
46> any_printable;
47typedef std::vector<any_printable> print_storage;
48
49// Forward declaration of the implementation function
50void print_impl(std::ostream& os, const char * format, const print_storage& args);
51
52// print
53//
54// Writes values to a stream like the classic C printf function. The
55// arguments are formatted based on specifiers in the format string,
56// which match the pattern:
57//
58// '%' [ argument-number '$' ] flags * [ width ] [ '.' precision ] [ type-code ] format-specifier
59//
60// Other characters in the format string are written to the stream unchanged.
61// In addition the sequence, "%%" can be used to print a literal '%' character.
62// Each component is explained in detail below
63//
64// argument-number:
65// The value must be between 1 and sizeof... T. It indicates the
66// index of the argument to be formatted. If no index is specified
67// the arguments will be processed sequentially. If an index is
68// specified for one argument, then it must be specified for every argument.
69//
70// flags:
71// Consists of zero or more of the following:
72// '-': Left justify the argument
73// '+': Print a plus sign for positive integers
74// '0': Use leading 0's to pad instead of filling with spaces.
75// ' ': If the value doesn't begin with a sign, prepend a space
76// '#': Print 0x or 0 for hexadecimal and octal numbers.
77//
78// width:
79// Indicates the minimum width to print. This can be either
80// an integer or a '*'. an asterisk means to read the next
81// argument (which must have type int) as the width.
82//
83// precision:
84// For numeric arguments, indicates the number of digits to print. For
85// strings (%s) the precision indicates the maximum number of characters
86// to print. Longer strings will be truncated. As with width
87// this can be either an integer or a '*'. an asterisk means
88// to read the next argument (which must have type int) as
89// the width. If both the width and the precision are specified
90// as '*', the width is read first.
91//
92// type-code:
93// This is ignored, but provided for compatibility with C printf.
94//
95// format-specifier:
96// Must be one of the following characters:
97// d, i, u: The argument is formatted as a decimal integer
98// o: The argument is formatted as an octal integer
99// x, X: The argument is formatted as a hexadecimal integer
100// p: The argument is formatted as a pointer
101// f: The argument is formatted as a fixed point decimal
102// e, E: The argument is formatted in exponential notation
103// g, G: The argument is formatted as either fixed point or using
104// scientific notation depending on its magnitude
105// c: The argument is formatted as a character
106// s: The argument is formatted as a string
107//
108template<class... T>
109void print(std::ostream& os, const char * format, const T&... t)
110{
111 // capture the arguments
112 print_storage args = { any_printable(t)... };
113 // and forward to the real implementation
114 print_impl(os, format, args);
115}
116
117// This overload of print with no explicit stream writes to std::cout.
118template<class... T>
119void print(const char * format, const T&... t)
120{
121 print(std::cout, format, t...);
122}
123
124// The implementation from here on can be separately compiled.
125
126// utility function to parse an integer
127int parse_int(const char *& format) {
128 int result = 0;
129 while(char ch = *format) {
130 switch(ch) {
131 case '0': case '1': case '2': case '3': case '4':
132 case '5': case '6': case '7': case '8': case '9':
133 result = result * 10 + (ch - '0');
134 break;
135 default: return result;
136 }
137 ++format;
138 }
139 return result;
140}
141
142// printf implementation
143void print_impl(std::ostream& os, const char * format, const print_storage& args) {
144 int idx = 0;
145 ios_flags_saver savef_outer(os, std::ios_base::dec);
146 bool has_positional = false;
147 bool has_indexed = false;
148 while(char ch = *format++) {
149 if (ch == '%') {
150 if (*format == '%') { os << '%'; continue; }
151
152 ios_flags_saver savef(os);
153 ios_precision_saver savep(os);
154 ios_fill_saver savefill(os);
155
156 int precision = 0;
157 bool pad_space = false;
158 bool pad_zero = false;
159
160 // parse argument index
161 if (*format != '0') {
162 int i = parse_int(format);
163 if (i != 0) {
164 if(*format == '$') {
165 idx = i - 1;
166 has_indexed = true;
167 ++format;
168 } else {
169 os << std::setw(i);
170 has_positional = true;
171 goto parse_precision;
172 }
173 } else {
174 has_positional = true;
175 }
176 } else {
177 has_positional = true;
178 }
179
180 // Parse format modifiers
181 while((ch = *format)) {
182 switch(ch) {
183 case '-': os << std::left; break;
184 case '+': os << std::showpos; break;
185 case '0': pad_zero = true; break;
186 case ' ': pad_space = true; break;
187 case '#': os << std::showpoint << std::showbase; break;
188 default: goto parse_width;
189 }
190 ++format;
191 }
192
193 parse_width:
194 int width;
195 if (*format == '*') {
196 ++format;
197 width = any_cast<int>(args.at(idx++));
198 } else {
199 width = parse_int(format);
200 }
201 os << std::setw(width);
202
203 parse_precision:
204 if (*format == '.') {
205 ++format;
206 if (*format == '*') {
207 ++format;
208 precision = any_cast<int>(args.at(idx++));
209 } else {
210 precision = parse_int(format);
211 }
212 os << std::setprecision(precision);
213 }
214
215 // parse (and ignore) the type modifier
216 switch(*format) {
217 case 'h': ++format; if(*format == 'h') ++format; break;
218 case 'l': ++format; if(*format == 'l') ++format; break;
219 case 'j':
220 case 'L':
221 case 'q':
222 case 't':
223 case 'z':
224 ++format; break;
225 }
226
227 std::size_t truncate = 0;
228
229 // parse the format code
230 switch(*format++) {
231 case 'd': case 'i': case 'u': os << std::dec; break;
232 case 'o': os << std::oct; break;
233 case 'p': case 'x': os << std::hex; break;
234 case 'X': os << std::uppercase << std::hex; break;
235 case 'f': os << std::fixed; break;
236 case 'e': os << std::scientific; break;
237 case 'E': os << std::uppercase << std::scientific; break;
238 case 'g': break;
239 case 'G': os << std::uppercase; break;
240 case 'c': case 'C': break;
241 case 's': case 'S': truncate = precision; os << std::setprecision(6); break;
242 default: assert(!"Bad format string");
243 }
244
245 if (pad_zero && !(os.flags() & std::ios_base::left)) {
246 os << std::setfill('0') << std::internal;
247 pad_space = false;
248 }
249
250 if (truncate != 0 || pad_space) {
251 // These can't be handled by std::setw. Write to a stringstream and
252 // pad/truncate manually.
253 std::ostringstream oss;
254 oss.copyfmt(os);
255 oss << args.at(idx++);
256 std::string data = oss.str();
257
258 if (pad_space) {
259 if (data.empty() || (data[0] != '+' && data[0] != '-' && data[0] != ' ')) {
260 os << ' ';
261 }
262 }
263 if (truncate != 0 && data.size() > truncate) {
264 data.resize(truncate);
265 }
266 os << data;
267 } else {
268 os << args.at(idx++);
269 }
270
271 // we can't have both positional and indexed arguments in
272 // the format string.
273 assert(has_positional ^ has_indexed);
274
275 } else {
276 std::cout << ch;
277 }
278 }
279}
280
281int main() {
282 print("int: %d\n", 10);
283 print("int: %0#8X\n", 0xA56E);
284 print("double: %g\n", 3.14159265358979323846);
285 print("double: %f\n", 3.14159265358979323846);
286 print("double: %+20.9e\n", 3.14159265358979323846);
287 print("double: %0+20.9g\n", 3.14159265358979323846);
288 print("double: %*.*g\n", 20, 5, 3.14159265358979323846);
289 print("string: %.10s\n", "Hello World!");
290 print("double: %2$*.*g int: %1$d\n", 10, 20, 5, 3.14159265358979323846);
291}
292
293//]