123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- // Boost.TypeErasure library
- //
- // Copyright 2012 Steven Watanabe
- //
- // Distributed under the Boost Software License Version 1.0. (See
- // accompanying file LICENSE_1_0.txt or copy at
- // http://www.boost.org/LICENSE_1_0.txt)
- //
- // $Id$
- //[printf
- /*`
- (For the source of this example see
- [@boost:/libs/type_erasure/example/printf.cpp printf.cpp])
- This example uses the library to implement a type safe printf.
- [note This example uses C++11 features. You'll need a
- recent compiler for it to work.]
- */
- #include <boost/type_erasure/builtin.hpp>
- #include <boost/type_erasure/operators.hpp>
- #include <boost/type_erasure/any_cast.hpp>
- #include <boost/type_erasure/any.hpp>
- #include <boost/mpl/vector.hpp>
- #include <boost/io/ios_state.hpp>
- #include <iostream>
- #include <sstream>
- #include <iomanip>
- #include <vector>
- #include <string>
- namespace mpl = boost::mpl;
- using namespace boost::type_erasure;
- using namespace boost::io;
- // We capture the arguments by reference and require nothing
- // except that each one must provide a stream insertion operator.
- typedef any<
- mpl::vector<
- typeid_<>,
- ostreamable<>
- >,
- const _self&
- > any_printable;
- typedef std::vector<any_printable> print_storage;
- // Forward declaration of the implementation function
- void print_impl(std::ostream& os, const char * format, const print_storage& args);
- // print
- //
- // Writes values to a stream like the classic C printf function. The
- // arguments are formatted based on specifiers in the format string,
- // which match the pattern:
- //
- // '%' [ argument-number '$' ] flags * [ width ] [ '.' precision ] [ type-code ] format-specifier
- //
- // Other characters in the format string are written to the stream unchanged.
- // In addition the sequence, "%%" can be used to print a literal '%' character.
- // Each component is explained in detail below
- //
- // argument-number:
- // The value must be between 1 and sizeof... T. It indicates the
- // index of the argument to be formatted. If no index is specified
- // the arguments will be processed sequentially. If an index is
- // specified for one argument, then it must be specified for every argument.
- //
- // flags:
- // Consists of zero or more of the following:
- // '-': Left justify the argument
- // '+': Print a plus sign for positive integers
- // '0': Use leading 0's to pad instead of filling with spaces.
- // ' ': If the value doesn't begin with a sign, prepend a space
- // '#': Print 0x or 0 for hexadecimal and octal numbers.
- //
- // width:
- // Indicates the minimum width to print. This can be either
- // an integer or a '*'. an asterisk means to read the next
- // argument (which must have type int) as the width.
- //
- // precision:
- // For numeric arguments, indicates the number of digits to print. For
- // strings (%s) the precision indicates the maximum number of characters
- // to print. Longer strings will be truncated. As with width
- // this can be either an integer or a '*'. an asterisk means
- // to read the next argument (which must have type int) as
- // the width. If both the width and the precision are specified
- // as '*', the width is read first.
- //
- // type-code:
- // This is ignored, but provided for compatibility with C printf.
- //
- // format-specifier:
- // Must be one of the following characters:
- // d, i, u: The argument is formatted as a decimal integer
- // o: The argument is formatted as an octal integer
- // x, X: The argument is formatted as a hexadecimal integer
- // p: The argument is formatted as a pointer
- // f: The argument is formatted as a fixed point decimal
- // e, E: The argument is formatted in exponential notation
- // g, G: The argument is formatted as either fixed point or using
- // scientific notation depending on its magnitude
- // c: The argument is formatted as a character
- // s: The argument is formatted as a string
- //
- template<class... T>
- void print(std::ostream& os, const char * format, const T&... t)
- {
- // capture the arguments
- print_storage args = { any_printable(t)... };
- // and forward to the real implementation
- print_impl(os, format, args);
- }
- // This overload of print with no explicit stream writes to std::cout.
- template<class... T>
- void print(const char * format, const T&... t)
- {
- print(std::cout, format, t...);
- }
- // The implementation from here on can be separately compiled.
- // utility function to parse an integer
- int parse_int(const char *& format) {
- int result = 0;
- while(char ch = *format) {
- switch(ch) {
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- result = result * 10 + (ch - '0');
- break;
- default: return result;
- }
- ++format;
- }
- return result;
- }
- // printf implementation
- void print_impl(std::ostream& os, const char * format, const print_storage& args) {
- int idx = 0;
- ios_flags_saver savef_outer(os, std::ios_base::dec);
- bool has_positional = false;
- bool has_indexed = false;
- while(char ch = *format++) {
- if (ch == '%') {
- if (*format == '%') { os << '%'; continue; }
- ios_flags_saver savef(os);
- ios_precision_saver savep(os);
- ios_fill_saver savefill(os);
-
- int precision = 0;
- bool pad_space = false;
- bool pad_zero = false;
- // parse argument index
- if (*format != '0') {
- int i = parse_int(format);
- if (i != 0) {
- if(*format == '$') {
- idx = i - 1;
- has_indexed = true;
- ++format;
- } else {
- os << std::setw(i);
- has_positional = true;
- goto parse_precision;
- }
- } else {
- has_positional = true;
- }
- } else {
- has_positional = true;
- }
- // Parse format modifiers
- while((ch = *format)) {
- switch(ch) {
- case '-': os << std::left; break;
- case '+': os << std::showpos; break;
- case '0': pad_zero = true; break;
- case ' ': pad_space = true; break;
- case '#': os << std::showpoint << std::showbase; break;
- default: goto parse_width;
- }
- ++format;
- }
- parse_width:
- int width;
- if (*format == '*') {
- ++format;
- width = any_cast<int>(args.at(idx++));
- } else {
- width = parse_int(format);
- }
- os << std::setw(width);
- parse_precision:
- if (*format == '.') {
- ++format;
- if (*format == '*') {
- ++format;
- precision = any_cast<int>(args.at(idx++));
- } else {
- precision = parse_int(format);
- }
- os << std::setprecision(precision);
- }
- // parse (and ignore) the type modifier
- switch(*format) {
- case 'h': ++format; if(*format == 'h') ++format; break;
- case 'l': ++format; if(*format == 'l') ++format; break;
- case 'j':
- case 'L':
- case 'q':
- case 't':
- case 'z':
- ++format; break;
- }
- std::size_t truncate = 0;
- // parse the format code
- switch(*format++) {
- case 'd': case 'i': case 'u': os << std::dec; break;
- case 'o': os << std::oct; break;
- case 'p': case 'x': os << std::hex; break;
- case 'X': os << std::uppercase << std::hex; break;
- case 'f': os << std::fixed; break;
- case 'e': os << std::scientific; break;
- case 'E': os << std::uppercase << std::scientific; break;
- case 'g': break;
- case 'G': os << std::uppercase; break;
- case 'c': case 'C': break;
- case 's': case 'S': truncate = precision; os << std::setprecision(6); break;
- default: assert(!"Bad format string");
- }
- if (pad_zero && !(os.flags() & std::ios_base::left)) {
- os << std::setfill('0') << std::internal;
- pad_space = false;
- }
- if (truncate != 0 || pad_space) {
- // These can't be handled by std::setw. Write to a stringstream and
- // pad/truncate manually.
- std::ostringstream oss;
- oss.copyfmt(os);
- oss << args.at(idx++);
- std::string data = oss.str();
- if (pad_space) {
- if (data.empty() || (data[0] != '+' && data[0] != '-' && data[0] != ' ')) {
- os << ' ';
- }
- }
- if (truncate != 0 && data.size() > truncate) {
- data.resize(truncate);
- }
- os << data;
- } else {
- os << args.at(idx++);
- }
- // we can't have both positional and indexed arguments in
- // the format string.
- assert(has_positional ^ has_indexed);
- } else {
- std::cout << ch;
- }
- }
- }
- int main() {
- print("int: %d\n", 10);
- print("int: %0#8X\n", 0xA56E);
- print("double: %g\n", 3.14159265358979323846);
- print("double: %f\n", 3.14159265358979323846);
- print("double: %+20.9e\n", 3.14159265358979323846);
- print("double: %0+20.9g\n", 3.14159265358979323846);
- print("double: %*.*g\n", 20, 5, 3.14159265358979323846);
- print("string: %.10s\n", "Hello World!");
- print("double: %2$*.*g int: %1$d\n", 10, 20, 5, 3.14159265358979323846);
- }
- //]
|