Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r80900 - in sandbox/type_erasure/libs/type_erasure: doc example
From: steven_at_[hidden]
Date: 2012-10-07 21:01:58


Author: steven_watanabe
Date: 2012-10-07 21:01:56 EDT (Sun, 07 Oct 2012)
New Revision: 80900
URL: http://svn.boost.org/trac/boost/changeset/80900

Log:
Finish printf expample.
Added:
   sandbox/type_erasure/libs/type_erasure/example/printf.cpp
      - copied, changed from r80601, /sandbox/type_erasure/libs/type_erasure/example/intro.cpp
Removed:
   sandbox/type_erasure/libs/type_erasure/example/intro.cpp
Text files modified:
   sandbox/type_erasure/libs/type_erasure/doc/type_erasure.qbk | 5
   sandbox/type_erasure/libs/type_erasure/example/Jamfile.jam | 2
   sandbox/type_erasure/libs/type_erasure/example/printf.cpp | 312 ++++++++++++++++++++++++++++++++-------
   3 files changed, 260 insertions(+), 59 deletions(-)

Modified: sandbox/type_erasure/libs/type_erasure/doc/type_erasure.qbk
==============================================================================
--- sandbox/type_erasure/libs/type_erasure/doc/type_erasure.qbk (original)
+++ sandbox/type_erasure/libs/type_erasure/doc/type_erasure.qbk 2012-10-07 21:01:56 EDT (Sun, 07 Oct 2012)
@@ -205,6 +205,11 @@
 [print_sequence]
 [endsect]
 
+[section:print_sequence A type-safe printf]
+[import ../example/printf.cpp]
+[printf]
+[endsect]
+
 [endsect]
 
 [section:conceptdef Concept Definitions]

Modified: sandbox/type_erasure/libs/type_erasure/example/Jamfile.jam
==============================================================================
--- sandbox/type_erasure/libs/type_erasure/example/Jamfile.jam (original)
+++ sandbox/type_erasure/libs/type_erasure/example/Jamfile.jam 2012-10-07 21:01:56 EDT (Sun, 07 Oct 2012)
@@ -18,6 +18,6 @@
 compile compose.cpp ;
 compile overload.cpp ;
 compile associated.cpp ;
-compile intro.cpp ;
 
 run print_sequence.cpp ;
+run printf.cpp ;

Deleted: sandbox/type_erasure/libs/type_erasure/example/intro.cpp
==============================================================================
--- sandbox/type_erasure/libs/type_erasure/example/intro.cpp 2012-10-07 21:01:56 EDT (Sun, 07 Oct 2012)
+++ (empty file)
@@ -1,97 +0,0 @@
-// Boost.TypeErasure library
-//
-// Copyright 2011 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$
-
-#include <boost/type_erasure/builtin.hpp>
-#include <boost/type_erasure/operators.hpp>
-#include <boost/type_erasure/iterator.hpp>
-#include <boost/type_erasure/callable.hpp>
-#include <boost/type_erasure/any.hpp>
-#include <boost/type_erasure/relaxed_match.hpp>
-#include <boost/mpl/vector.hpp>
-#include <iostream>
-#include <vector>
-
-namespace mpl = boost::mpl;
-using namespace boost::type_erasure;
-
-//[intro
-/*`
- Here is a simple example using the library to implement
- a type safe printf.
- */
-typedef any<
- mpl::vector<
- copy_constructible<>,
- ostreamable<>
- >
-> any_printable;
-
-typedef std::vector<any_printable> print_storage;
-
-void print_impl(const char * format, const print_storage& args) {
- int idx = 0;
- while(char ch = *format++) {
- if (ch == '%') {
- switch(*format++) {
- case '%': std::cout << '%'; break;
- case 'd': std::cout << std::dec << args.at(idx++); break;
- case 'x': std::cout << std::hex << args.at(idx++); break;
- case '\0': return;
- }
- } else {
- std::cout << ch;
- }
- }
-}
-
-template<class... T>
-void print(const char * format, const T&... t)
-{
- print_storage args = { any_printable(t)... };
- print_impl(format, args);
-}
-/*`
- The top level function just stores all the arguments
- in a vector and forwards to the real implementation.
- `any_printable<>`, as its name suggests, is like
- Boost.Any, but also supports streaming to `std::cout`.
- */
-/*`
- Boost.TypeErasure generalizes a technique used by
- several other Boost libraries.
- */
-typedef any<
- mpl::vector<
- copy_constructible<>,
- typeid_<>,
- relaxed_match
- >
-> boost_any; // equivalent to boost::any
-
-typedef any<
- mpl::vector<
- copy_constructible<>,
- callable<void(int)>,
- typeid_<>,
- relaxed_match
- >
-> boost_function; // equivalent to boost::function<void(int)>
-
-typedef any<
- mpl::vector<
- forward_iterator<>,
- same_type<forward_iterator<>::value_type, int>,
- relaxed_match
- >
-> any_iterator;
-// equivalent to
-// boost::any_range<int, boost::forward_traversal_tag,
-// int&, std::ptrdiff_t>::iterator
-//]

Copied: sandbox/type_erasure/libs/type_erasure/example/printf.cpp (from r80601, /sandbox/type_erasure/libs/type_erasure/example/intro.cpp)
==============================================================================
--- /sandbox/type_erasure/libs/type_erasure/example/intro.cpp (original)
+++ sandbox/type_erasure/libs/type_erasure/example/printf.cpp 2012-10-07 21:01:56 EDT (Sun, 07 Oct 2012)
@@ -1,6 +1,6 @@
 // Boost.TypeErasure library
 //
-// Copyright 2011 Steven Watanabe
+// Copyright 2012 Steven Watanabe
 //
 // Distributed under the Boost Software License Version 1.0. (See
 // accompanying file LICENSE_1_0.txt or copy at
@@ -8,90 +8,286 @@
 //
 // $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/iterator.hpp>
-#include <boost/type_erasure/callable.hpp>
+#include <boost/type_erasure/any_cast.hpp>
 #include <boost/type_erasure/any.hpp>
-#include <boost/type_erasure/relaxed_match.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;
 
-//[intro
-/*`
- Here is a simple example using the library to implement
- a type safe printf.
- */
+// We capture the arguments by reference and require nothing
+// except that each one must provide a stream insertion operator.
 typedef any<
     mpl::vector<
- copy_constructible<>,
+ typeid_<>,
         ostreamable<>
- >
+ >,
+ const _self&
> any_printable;
-
 typedef std::vector<any_printable> print_storage;
 
-void print_impl(const char * format, const print_storage& args) {
+// 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 writted 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 asterik 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 asterik 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 '%': std::cout << '%'; break;
- case 'd': std::cout << std::dec << args.at(idx++); break;
- case 'x': std::cout << std::hex << args.at(idx++); break;
- case '\0': return;
+ 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;
         }
     }
 }
 
-template<class... T>
-void print(const char * format, const T&... t)
-{
- print_storage args = { any_printable(t)... };
- print_impl(format, args);
+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", (const char *)"Hello World!");
+ print("double: %2$*.*g int: %1$d\n", 10, 20, 5, 3.14159265358979323846);
 }
-/*`
- The top level function just stores all the arguments
- in a vector and forwards to the real implementation.
- `any_printable<>`, as its name suggests, is like
- Boost.Any, but also supports streaming to `std::cout`.
- */
-/*`
- Boost.TypeErasure generalizes a technique used by
- several other Boost libraries.
- */
-typedef any<
- mpl::vector<
- copy_constructible<>,
- typeid_<>,
- relaxed_match
- >
-> boost_any; // equivalent to boost::any
 
-typedef any<
- mpl::vector<
- copy_constructible<>,
- callable<void(int)>,
- typeid_<>,
- relaxed_match
- >
-> boost_function; // equivalent to boost::function<void(int)>
-
-typedef any<
- mpl::vector<
- forward_iterator<>,
- same_type<forward_iterator<>::value_type, int>,
- relaxed_match
- >
-> any_iterator;
-// equivalent to
-// boost::any_range<int, boost::forward_traversal_tag,
-// int&, std::ptrdiff_t>::iterator
 //]


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk