Type-safe 'printf-like' format
class"Le pourquoi du comment" ( - "the why of the how")
Format is a new library. One of its goal is to provide a replacement for
printf, that means format can parse a format-string designed for printf,
apply it to the given arguments, and produce the same result as printf
would have.
With this constraint, there were roughly 3 possible choices for the syntax
of the format-string :
format(" %s at %s with %s\n") % x % y % z;
we can hope it wont confuse people that much.
An other fear about operators, is precedence problems. What if I someday
write format("%s") % x+y
instead of format("%s") % (x+y) ??
It will make a mistake at compile-time, so the error will be immediately
detected.
indeed, this line calls tmp = operator%( format("%s"), x)
and then operator+(tmp, y)
tmp will be a format object, for which no implicit conversion is defined,
and thus the call to operator+ will fail. (except if you define such an
operator, of course). So you can safely assume precedence mistakes will be
noticed at compilation.
On the other hand, the function approach has a true inconvenience. It needs
to define lots of template function like :
and even if we define those for N up to 500, that is still a limitation, that C's printf does not have.template <class T1, class T2, .., class TN> string format(string s, const T1& x1, .... , const T1& xN);
Anyhow, if we actually chose the formal function call templates system, it
would only be able to print Classes T for which there is an
Because allowing both const and non const produces a combinatorics explosion - if we go up to 10 arguments, we need 2^10 functions.operator<< ( stream, const T&)
In conclusion, using a dedicated binary operator is the simplest, most robust, and least restrictive mechanism to pass arguments when you can't know the number of arguments at compile-time.
has the same structure asformat(fstr) % x1 % x2 % x3;
which does not have any precedence problem. The only drawback, is it's harder for the eye to catch what is done in this line, than when we are using operators. calling .with(..), it looks just like any other line of code. So it may be a better solution, depending on tastes. The extra characters, and overall cluttered aspect of the line of code using 'with(..)' were enough for me to opt for a true operator.format(fstr).with( x1 ).with( x2 ).with( x3 );
cout << format("%s %s ") << x;
cout << y ; // uh-oh, format is not really a stream manipulator
cout << format("%s %s ") % x % y << endl;
is treated exaclt like :
cout << ( format("%s %s ") % x % y ) << endl;
So using %, the life of a format object does not interfere
with the surrounding stream context. This is the simplest possible
behaviour, and thus the user is able to continue using the stream after
the format object.
cout << format("%s %s ") << x << y << endl;
is understood as :
( ( ( cout << format("%s %s ") ) << x ) << y ) << endl;
Several alternative implementations chose
operator<<, and there is only one way to make it work :returns a proxy, encapsulating both the final destination (cout) and the format-string informationoperator<<( ostream&, format const&)
I examined several possible implementations, and none is completely
satsifying.
E.g. : In order to catch users mistake, it makes sense to raise
exceptions when the user passes too many arguments. But in this
context, supplementary arguments are most certainly aimed at the final
destination. There are several choices here :
cout << format("%s %s ") << x << y << endf << endl;
You can define endf to be a function that returns the
final destination stored inside the proxy. Then it's okay, after
endf the user is calling << on cout again.
cout << format("%s %s \n") << x << y << flush ;
Then, the solution is to overload the operator<<
for manipulators. This way You don't need endf, but outputting a
non-manipulator item right after the format arguments is a mistake.
cout << format("%s %s %s") %x %y %z << "And avg is" << format("%s\n") %avg;
compared to :
cout << format("%s %s %s") << x << y << z << endf <<"And avg is" << format("%s\n") << avg;
"<<" misleadingly puts the arguments at the same
level as any object passed to the stream.
operator() has the merit of being the natural way to send an argument
into a function. And some think that operator[] 's meaning apply well to
the usage in format.
They're as good as operator% technically, but quite ugly. (that's a matter
of taste)
And deepd down, using operator% for passing arguments that were referred to
by "%" in the format string seems much more natural to me than using those
operators.
Revised 02 December, 2006
Copyright © 2001 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)