$include_dir="/home/hyper-archives/boost-commit/include"; include("$include_dir/msg-header.inc") ?>
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
 //]