Boost logo

Boost :

From: James Kanze (kanze_at_[hidden])
Date: 2002-01-30 17:40:54


Samuel Krempp <krempp_at_[hidden]> writes:

|> On Sun, 2002-01-27 at 17:59, James Kanze wrote:
|> > |> The printf syntax explicitly mentions the type and formatting
|> > |> options for each parameter. C++'s better type checking
|> > |> eliminates the need for the explicit typing, and the ability
|> > |> to add manipulator objects eliminates the need for explicit
|> > |> formatting.

|> > The first isn't quite true, and won't be as long as there are
|> > implicit conversions. Probably the most frequent error in using
|> > printf was to pass a constant 0 to something like a "%6.2f". A
|> > C/C++ user expects this to be allowed, since I can normally use an
|> > integral constant where ever a floating point is needed, and a
|> > good formatting function will know that a floating point is
|> > expected, and do the conversion.

|> already, format behaves better than printf, since it does print 0 in
|> some way, though not as a float number but as an integral one.

It's several orders of magnatude better than printf; I can agree to
that.

|> If I understand you correctly, you suggest converting the arguments
|> into the type specified by the format-string, for instance here,
|> convert 0 to float before formatting it.
|> That's one way to extend the meaning of the type-specification in the
|> context of Boost.Format (where arguments's types are known).

|> But it does not adapt well to user types :

I know. It's a partial solution, but I don't have a better one.

It will sort of work for user defined numeric types, provided they
follow the same formatting conventions as the standard types. Thus, if
a BigDecimal class uses the standard ios::fmtflags for it's formatting,
the 0 will be formatted as a double, but the results will be exactly the
same as if it had been formatted as a BigDecimal, using the same
format specification.

Where it breaks down is for types that aren't in some way integers or
floating point, but which can reasonably take an integral value.

At one point in my C days, I worked on an extention to printf to support
user defined types; the programmer registered a formatting function
for one or more keystrings. (The extension was that %<...> was a legal
format specifier, and the text between the <...> was the keystring,
which caused the user defined formatting function to be called. And the
code wasn't portable; it depended on the way our compiler passed
varargs.) I suppose that something like that could be used here as
well; the function would attempt to convert 0 to the required type. It
would take some really tricky templates to make this typesafe, however,
if it can be done at all.

|> it's okay for a float-like user-type with conversion to float, but
|> not if it's a pair of floats, a rational number, etc. For those
|> user-types, you'd have to use a special type-specificator (for
|> instance "%z"), to mean "don't convert the argument". And then in
|> this case you have no way to specify formatting options usually
|> carried in the type specificator (floatfields : fixed / scientific /
|> default, basefields, uppercase, ..)

My expectations are that user defined types define an operator<< for
ostream&, as usual, and that is it. The default implementation template
formatting function sets the fmtflags to the appropriate values, and
outputs the user defined type to the stream, recovering the text from
the stream.

As I say, this works if the user defined type is a scalar which respects
the standard formatting conventions. The integer gets converted to a
double, and formatted as such, but the results are the same string as if
it had been converted to the user defined type. It fails for
non-scalars, like complex.

|> With my approach, format still gives the same result as printf in
|> printf situations (float x; format("%6.2f") % x; ),

I'd say that that was a minimum requirement:-).

|> but treats the type-scpeficator merely as a set of formatting
|> options, so that it can apply to any type.

Right. That's exactly what my format does as well.

|> It does not enforce the type-specification as you wish it would,
|> e.g. :
|> int n;
|> cout << format("%f") % n;
|> will act like :
|> cout << fixed << n;
|> not like :
|> cout << fixed << float(n);

OK. I'll admit that the implicit conversion is far from necessary. In
my case, I needed to specialize on int anyway, in order to support
things like "%*.*f"; since I was already there, it was trivial to add a
quick check on the format specifier, and convert if necessary.

I think it's a nice added feature, but certainly not a necessity.

|> but it makes it applicable to any type, without discrimination.
|> Indeed, treating usertypes and standard types in the same way works
|> fine when you translate printf's type specification into
|> stream-state modifications, e.g :
|> %x => hex
|> %X => hex and uppercase
|> %e => scientific
|> %E => scientific and uppercase
|> %f => fixed
|> %g => unset all floatfield bits
|> etc.. (see the html doc for the full list)

I fear it will be at least two weeks before I can find the time to look
at everything in detail. I was simply informed by email that a
discussion was going on here about format, and thought that since I had
actually implemented such a class, and have been using it for about six
or seven years, that I could make some sort of contribution. I
sincerely regret that I didn't know about it earlier (when the
contribution would certainly have been more useful); overall, the volume
of the boost mailing list is too much for me to follow it regularly.

At any rate, my format class is available on the web (www.gabi-soft.de);
the header file contains all of the documentation, including the exact
mappings of the printf formatting to stream-state modifiers. (It starts
with several hundred lines of comments. They were originally in French,
but I've appended an English translation afterwards.)

|> It is carefully designed to have the same result if the types of the
|> arguments are the right ones, and extends nicely to any type.

|> In my opinion, it's better to place the conversion out of the
|> format-string, e.g. :

|> int n;
|> cout << format("%f") % float(n);

|> Though, both approaches could be implemented at the same time, by
|> using a special flag to choose between them (e.g. "%d" => set
|> formatting options suited for ouput of a decimal number, and cast to
|> int, V.S "%Nd" => set options, and do Not cast to int) but it is
|> complicating the description of the syntax too much.

Agreed. It's just a convenience feature. If it works, fine, but we
shouldn't allow it to make anything else more complex.

|> BTW, with your proposal, I guess the easiest and most performant
|> implementation is to cast each argument into built_in types as
|> specified in the format-string, and then forward those arguments,
|> and the format-string, to a snprintf function. (we just have to care
|> about buffer overflow issues. which is much simpler than handling
|> formatting..)

No. The main reason why I didn't considere this in my case is that
snprintf wasn't available when I first wrote the class, and sprintf is
just too dangerous. But handling the i18n issues isn't trivial with
this solution either. (Obviously, the constructor to format really
takes two arguments, the second of which is a locale, which defaults to
locale(). I've not looked at your code, but I presume that this is also
the case.)

|> > Would it be too much to ask why boost is trying to re-invent the
|> > wheel. The extended printf format defined by X/Open works quite
|> > well, thank you, and unless someone can point out some fatal flaw
|> > with it, I would argue for sticking with it.

|> as a matter of factn boost.format does stick with it Whatever syntax
|> is finally adopted, there will be a way to use format in a
|> printf-compatible mode. (which excludes "%1" stuff) In fact, there
|> may very well be no other mode, I'm still hesitating whether or not
|> provide an extra 'short-notation' mode.

|> For times when you need formatting options on less than 1/10 of the
|> arguments, I thought it would be comfortable to type simply "%1",
|> instead of "%1$s"; and for the argument on which you need formatting
|> options, use a printf-directive inside brackets, like
|> "%{3$6.2g}". Or some other way to get the best from both worlds. I
|> guees I'll eventually ask for votes on this matter. But in all
|> cases, format's syntax won't reinvent printf's syntax.

OK. I think I rather prefer your alternative to the X/Open printf
extension. On the other hand, all my code (and all my customers' code)
currently uses the X/Open printf extensions, so sticking with them would
avoid their having to learn yet another way.

-- 
James Kanze                                mailto:kanze_at_[hidden]
Conseils en informatique orientée objet/
                    Beratung in objektorientierter Datenverarbeitung
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany Tel. +49(0)179 2607481

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk