| 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>(arg: args.at(n: 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>(arg: args.at(n: 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(rhs: os); |
| 255 | oss << args.at(n: 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(n: truncate); |
| 265 | } |
| 266 | os << data; |
| 267 | } else { |
| 268 | os << args.at(n: 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(format: "int: %d\n" , t: 10); |
| 283 | print(format: "int: %0#8X\n" , t: 0xA56E); |
| 284 | print(format: "double: %g\n" , t: 3.14159265358979323846); |
| 285 | print(format: "double: %f\n" , t: 3.14159265358979323846); |
| 286 | print(format: "double: %+20.9e\n" , t: 3.14159265358979323846); |
| 287 | print(format: "double: %0+20.9g\n" , t: 3.14159265358979323846); |
| 288 | print(format: "double: %*.*g\n" , t: 20, t: 5, t: 3.14159265358979323846); |
| 289 | print(format: "string: %.10s\n" , t: "Hello World!" ); |
| 290 | print(format: "double: %2$*.*g int: %1$d\n" , t: 10, t: 20, t: 5, t: 3.14159265358979323846); |
| 291 | } |
| 292 | |
| 293 | //] |
| 294 | |