mirror of
https://github.com/boostorg/type_erasure.git
synced 2026-01-19 04:42:14 +00:00
294 lines
9.8 KiB
C++
294 lines
9.8 KiB
C++
// 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);
|
|
}
|
|
|
|
//]
|