...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
The <boost/format.hpp>
format
class provides printf-like formatting, in a type-safe manner which allows
output of user-defined types.
A format object is constructed from a format-string, and is then given
arguments through repeated calls to operator%.
Each of those arguments are then converted to strings, who are in turn
combined into one string, according to the format-string.
cout << boost::format("writing %1%, x=%2% : %3%-th try") % "toto" % 40.23 % 50; // prints "writing toto, x=40.230 : 50-th try"
or later on, as incout << format("%2% %1%") % 36 % 77;
you feed variables into the formatter.format fmter("%2% %1%"); fmter % 36; fmter % 77;
// fmter was previously created and fed arguments, it can print the result : cout << fmter ; // You can take the string result : string s = fmter.str(); // possibly several times : s = fmter.str( ); // You can also do all steps at once : cout << boost::format("%2% %1%") % 36 % 77; // using the str free function : string s2 = str( format("%2% %1%") % 36 % 77 );
using namespace std; using boost::format; using boost::io::group;
It prints : "11 22 333 22 11 \n"cout << format("%1% %2% %3% %2% %1% \n") % "11" % "22" % "333"; // 'simple' style.
It prints : "(x,y) = ( -23, +35) \n"cout << format("(x,y) = (%1$+5d,%2$+5d) \n") % -23 % 35; // Posix-Printf style
It prints : "writing toto, x=40.23 : 50-th step \n"cout << format("writing %s, x=%s : %d-th step \n") % "toto" % 40.23 % 50;
all those print : "(x,y) = ( -23, +35) \n"cout << format("(x,y) = (%+5d,%+5d) \n") % -23 % 35; cout << format("(x,y) = (%|+5|,%|+5|) \n") % -23 % 35; cout << format("(x,y) = (%1$+5d,%2$+5d) \n") % -23 % 35; cout << format("(x,y) = (%|1$+5|,%|2$+5|) \n") % -23 % 35;
Both print the same : "_ +101_ 101 \n"format fmter("_%1$+5d_ %1$d \n"); format fmter2("_%1%_ %1% \n"); fmter2.modify_item(1, group(showpos, setw(5)) ); cout << fmter % 101 ; cout << fmter2 % 101 ;
The manipulators are applied at each occurence of %1%, and thus it prints : "_ +101_ +101 \n"cout << format("_%1%_ %1% \n") % group(showpos, setw(5), 101);
For some std::vector names, surnames, and tel (see sample_new_features.cpp) it prints :for(unsigned int i=0; i < names.size(); ++i) cout << format("%1%, %2%, %|40t|%3%\n") % names[i] % surname[i] % tel[i];
Marc-François Michel, Durand, +33 (0) 123 456 789 Jean, de Lattre de Tassigny, +33 (0) 987 654 321
The program sample_formats.cpp demonstrates simple
uses of format.
sample_new_features.cpp
illustrates the few formatting features that were added to printf's syntax
such as simple positional directives, centered alignment, and
'tabulations'.
sample_advanced.cpp
demonstrates uses of advanced features, like reusing, and modifying, format
objects, etc..
And sample_userType.cpp shows the behaviour of the format library on user-defined types.
boost::format( format-string ) % arg1 % arg2 % ... % argN
The format-string contains text in which special directives will
be replaced by strings resulting from the formatting of the given
arguments.
The legacy syntax in the C and C++ worlds is the one used by printf, and
thus format can use directly printf format-strings, and produce the same
result (in almost all cases. see Incompatibilities with printf for details)
This core syntax was extended, to allow new features, but also to adapt to
the C++ streams context. Thus, format accepts several forms of directives
in format-strings :
The printf format specifications supported by Boost.format follows the
Unix98 Open-group
printf precise syntax, rather than the standard C printf, which does
not support positional arguments. (Common flags have the same meaning in
both, so it should not be a headache for anybody)
Note that it is an error to use positional format specifications
(e.g. %3$+d) mixed with non-positional ones (e.g. %+d)
in the same format string.
In the Open-group specification, referring to the same argument several
times (e.g. "%1$d %1$d") has undefined behaviour. Boost.format's
behaviour in such cases is to allow each argument to be reffered to any
number of times. The only constraint is that it expects exactly P
arguments, P being the maximum argument number used in the format
string. (e.g., for "%1$d %10$d", P == 10 ).
Supplying more, or less, than P arguments raises an exception.
(unless it was set otherwise, see exceptions)
A specification spec has the form : [ N$ ] [
flags ] [ width ] [ . precision ]
type-char
Fields insided square brackets are optional. Each of those fields are
explained one by one in the following list :
Flag Meaning effect on internal stream '-' left alignment N/A (applied later on the string) '=' centered alignment N/A (applied later on the string)
- note : added feature, not in printf -'_' internal alignment sets internal alignment
- note : added feature, not in printf -'+' show sign even for positive numbers sets showpos '#' show numerical base, and decimal point sets showbase and showpoint '0' pad with 0's (inserted after sign or base indicator) if not left-aligned, calls setfill('0') and sets internal
Extra actions are taken after stream conversion to handle user-defined output.' ' if the string does not begin with + or -, insert a space before the converted string N/A (applied later on the string)
Different to printf's behaviour : it is not affected by internal alignment
Type-Char Meaning effect on stream p or x hexadecimal output sets hex o octal output sets oct e scientific float format sets floatfield bits to scientific f fixed float format sets floatfield bits to fixed g general -default- float format unset all floatfield bits X, E or G same effect as their lowercase counterparts, but using uppercase letters for number outputs. (exponents, hex digits, ..) same effects as 'x', 'e', or 'g', plus uppercase d, i or u decimal type output sets basefield bits to dec s or S string output precision specification is unset, and its value goes to an internal field for later 'truncation'. (see precision explanation above) c or C 1-character output only the first character of the conversion string is used. % print the character % N/A
Note that the 'n' type specification is ignored (and so is the
corresponding argument), because it does not fit in this context.
Also, printf 'l', 'L', or 'h' modifiers (to indicate wide, long or
short types) are supported (and simply have no effect on the internal
stream).
printf(s, x1, x2);
cout << format(s) % x1 % x2;
But because some printf format specifications don't translate well into
stream formatting options, there are a few notable imperfections in the way
Boost.format emulates printf.
In any case, the format class should quietly ignore the unsupported
options, so that printf format-strings are always accepted by format and
produce almost the same output as printf.
format formatter("%+5d"); cout << formatter % x; unsigned int n = formatter.size();
All flags which are translated into modification to the stream state act recursively within user-defined types. ( the flags remain active, and so does the desired format option, for each of the '<<' operations that might be called by the user-defined class)
e.g., with a Rational class, we would have something like :Rational ratio(16,9); cerr << format("%#x \n") % ratio; // -> "0x10/0x9 \n"
It's a different story for other formatting options. For example, setting width applies to the final output produced by the object, not to each of its internal outputs, and that's fortunate :
cerr << format("%-8d") % ratio; // -> "16/9 " and not "16 /9 " cerr << format("%=8d") % ratio; // -> " 16/9 " and not " 16 / 9 "
But so does the 0 and ' ' options (contrarily to '+' which is directly
translated to the stream state by showpos. But no such flags exist
for the zero and space printf options)
and that is less natural :
It is possible to obtain a better behaviour by carefully designing the Rational's operator<< to handle the stream's width, alignment and showpos paramaters by itself. This is demonstrated in sample_userType.cpp.cerr << format("%+08d \n") % ratio; // -> "+00016/9" cerr << format("% 08d \n") % ratio; // -> "000 16/9"
The internal stream state of format is saved before and restored
after output of an argument; therefore, the modifiers are not sticky and
affect only the argument they are applied to.
The default state for streams, as stated by the standard, is : precision 6,
width 0, right alignment, and decimal flag set.
The state of the internal format stream can be changed by manipulators passed along with the argument; via the group function, like that :
cout << format("%1% %2% %1%\n") % group(hex, showbase, 40) % 50; // prints "0x28 50 0x28\n"
When passing N items inside a 'group' Boost.format needs to process
manipulators diferently from regular argument, and thus using group is
subject to the following constraints :
Such manipulators are passed to the streams right before the following argument, at every occurence. Note that formatting options specified within the format string are overridden by stream state modifiers passed this way. For instance in the following code, the hex manipulator has priority over the d type-specification in the format-string which would set decimal output :
cout << format("%1$d %2% %1%\n") % group(hex, showbase, 40) % 50; // prints "0x28 50 0x28\n"
Boost.format enforces a number of rules on the usage of format objects.
The format-string must obeys the syntax described above, the user must
supply exactly the right number of arguments before outputting to the final
destination, and if using modify_item or bind_arg, items and arguments
index must not be out of range.
When format detects that one of these rules is not satisfied, it raises a
corresponding exception, so that the mistakes don't go unnoticed and
unhandled.
But the user can change this behaviour to fit his needs, and select which
types of errors may raise exceptions using the following functions :
unsigned char exceptions(unsigned char newexcept); // query and set unsigned char exceptions() const; // just query
The user can compute the argument newexcept by combining the following atoms using binary arithmetic :
For instance, if you don't want Boost.format to detect bad number of arguments, you can define a specific wrapper function for building format objects with the right exceptions settings :
It is then allowed to give more arguments than needed (they are simply ignored) :boost::format my_fmt(const std::string & f_string) { using namespace boost::io; format fmter(f_string); fmter.exceptions( all_error_bits ^ ( too_many_args_bit | too_few_args_bit ) ); return fmter; }
And if we ask for the result before all arguments are supplied, the corresponding part of the result is simply emptycout << my_fmt(" %1% %2% \n") % 1 % 2 % 3 % 4 % 5;
cout << my_fmt(" _%2%_ _%1%_ \n") % 1 ; // prints " __ _1_ \n"
The performance of boost::format for formatting a few builtin type arguments with reordering can be compared to that of Posix-printf, and of the equivalent stream manual operations to give a measure of the overhead incurred. The result may greatly depend on the compiler, standard library implementation, and the precise choice of format-string and arguments.
Since common stream implementations eventually call functions of the printf family for the actual formatting of numbers, in general printf will be noticeably faster than the direct stream operations And due to to the reordering overhead (allocations to store the pieces of string, stream initialisation at each item formatting, ..) the direct stream operations would be faster than boost::format, (one cas expect a ratio ranging from 2 to 5 or more)
When iterated formattings are a performance bottleneck, performance can be slightly increased by parsing the format string into a format object, and copying it at each formatting, in the following way.
const boost::format fmter(fstring); dest << boost::format(fmter) % arg1 % arg2 % arg3 ;
As an example of performance results, the author measured the time of execution of iterated formattings with 4 different methods
the test was compiled with g++-3.3.3 and the following timings were measured (in seconds, and ratios) :
string fstring="%3$0#6x %1$20.10E %2$g %3$0+5d \n"; double arg1=45.23; double arg2=12.34; int arg3=23; - release mode : printf : 2.13 nullStream : 3.43, = 1.61033 * printf boost::format copied : 6.77, = 3.1784 * printf , = 1.97376 * nullStream boost::format straight :10.67, = 5.00939 * printf , = 3.11079 * nullStream - debug mode : printf : 2.12 nullStream : 3.69, = 1.74057 * printf boost::format copied :10.02, = 4.72642 * printf , = 2.71545 * nullStream boost::format straight :17.03, = 8.03302 * printf , = 4.61518 * nullStream
namespace boost { template<class charT, class Traits=std::char_traits<charT> > class basic_format { public: typedef std::basic_string<charT, Traits> string_t; typedef typename string_t::size_type size_type; basic_format(const charT* str); basic_format(const charT* str, const std::locale & loc); basic_format(const string_t& s); basic_format(const string_t& s, const std::locale & loc); basic_format& operator= (const basic_format& x); void clear(); // reset buffers basic_format& parse(const string_t&); // clears and parse a new format string string_t str() const; size_type size() const; // pass arguments through those operators : template<class T> basic_format& operator%(T& x); template<class T> basic_format& operator%(const T& x); // dump buffers to ostream : friend std::basic_ostream<charT, Traits>& operator<< <> ( std::basic_ostream<charT, Traits>& , basic_format& ); // Choosing which errors will throw exceptions : unsigned char exceptions() const; unsigned char exceptions(unsigned char newexcept); // ............ this is just an extract ....... }; // basic_format typedef basic_format<char > format; typedef basic_format<wchar_t > wformat; // free function for ease of use : template<class charT, class Traits> std::basic_string<charT,Traits> str(const basic_format<charT,Traits>& f) { return f.str(); } } // namespace boost
This class's goal is to bring a better, C++, type-safe and type-extendable printf equivalent to be used with streams.
Precisely, format was designed to provide the following features :In the process of the design, many issues were faced, and some choices were made, that might not be intuitively right. But in each case they were taken for some reasons.
The author of Boost format is Samuel Krempp. He used ideas from Rüdiger Loos' format.hpp and Karl Nelson's formatting classes.
Revised 02 December, 2006
Copyright © 2002 Samuel Krempp
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)