]> git.proxmox.com Git - ceph.git/blob - 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
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
34 namespace mpl = boost::mpl;
35 using namespace boost::type_erasure;
36 using 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.
40 typedef any<
41 mpl::vector<
42 typeid_<>,
43 ostreamable<>
44 >,
45 const _self&
46 > any_printable;
47 typedef std::vector<any_printable> print_storage;
48
49 // Forward declaration of the implementation function
50 void 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 //
108 template<class... T>
109 void 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.
118 template<class... T>
119 void 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
127 int 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
143 void 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
281 int 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 //]