]>
git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/type_erasure/example/printf.cpp
1 // Boost.TypeErasure library
3 // Copyright 2012 Steven Watanabe
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)
13 (For the source of this example see
14 [@boost:/libs/type_erasure/example/printf.cpp printf.cpp])
16 This example uses the library to implement a type safe printf.
18 [note This example uses C++11 features. You'll need a
19 recent compiler for it to work.]
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>
34 namespace mpl
= boost::mpl
;
35 using namespace boost::type_erasure
;
36 using namespace boost::io
;
38 // We capture the arguments by reference and require nothing
39 // except that each one must provide a stream insertion operator.
47 typedef std::vector
<any_printable
> print_storage
;
49 // Forward declaration of the implementation function
50 void print_impl(std::ostream
& os
, const char * format
, const print_storage
& args
);
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:
58 // '%' [ argument-number '$' ] flags * [ width ] [ '.' precision ] [ type-code ] format-specifier
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
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.
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.
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.
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.
93 // This is ignored, but provided for compatibility with C printf.
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
109 void print(std::ostream
& os
, const char * format
, const T
&... t
)
111 // capture the arguments
112 print_storage args
= { any_printable(t
)... };
113 // and forward to the real implementation
114 print_impl(os
, format
, args
);
117 // This overload of print with no explicit stream writes to std::cout.
119 void print(const char * format
, const T
&... t
)
121 print(std::cout
, format
, t
...);
124 // The implementation from here on can be separately compiled.
126 // utility function to parse an integer
127 int parse_int(const char *& format
) {
129 while(char ch
= *format
) {
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');
135 default: return result
;
142 // printf implementation
143 void print_impl(std::ostream
& os
, const char * format
, const print_storage
& args
) {
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
++) {
150 if (*format
== '%') { os
<< '%'; continue; }
152 ios_flags_saver
savef(os
);
153 ios_precision_saver
savep(os
);
154 ios_fill_saver
savefill(os
);
157 bool pad_space
= false;
158 bool pad_zero
= false;
160 // parse argument index
161 if (*format
!= '0') {
162 int i
= parse_int(format
);
170 has_positional
= true;
171 goto parse_precision
;
174 has_positional
= true;
177 has_positional
= true;
180 // Parse format modifiers
181 while((ch
= *format
)) {
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
;
195 if (*format
== '*') {
197 width
= any_cast
<int>(args
.at(idx
++));
199 width
= parse_int(format
);
201 os
<< std::setw(width
);
204 if (*format
== '.') {
206 if (*format
== '*') {
208 precision
= any_cast
<int>(args
.at(idx
++));
210 precision
= parse_int(format
);
212 os
<< std::setprecision(precision
);
215 // parse (and ignore) the type modifier
217 case 'h': ++format
; if(*format
== 'h') ++format
; break;
218 case 'l': ++format
; if(*format
== 'l') ++format
; break;
227 std::size_t truncate
= 0;
229 // parse the format code
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;
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");
245 if (pad_zero
&& !(os
.flags() & std::ios_base::left
)) {
246 os
<< std::setfill('0') << std::internal
;
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
;
255 oss
<< args
.at(idx
++);
256 std::string data
= oss
.str();
259 if (data
.empty() || (data
[0] != '+' && data
[0] != '-' && data
[0] != ' ')) {
263 if (truncate
!= 0 && data
.size() > truncate
) {
264 data
.resize(truncate
);
268 os
<< args
.at(idx
++);
271 // we can't have both positional and indexed arguments in
272 // the format string.
273 assert(has_positional
^ has_indexed
);
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);