Boost logo

Boost :

From: Karl Nelson (kenelson_at_[hidden])
Date: 2001-12-19 20:52:01


> On Wed, 2001-12-19 at 22:09, Karl Nelson wrote:
> > The implemention I sent of format had no problems with extra
> > arguments.
> >
> > cout << format("A %2$s %1$s") << s1 << s2<< endl;
>
> > For good reason. All extra agruments after the format
> > became regular operations.
>
> that's the problem for me : it won't notice user's errors.
> The uer could mistype the format-string, eg hit "%" twice : "%%2$s"
> I'd like the syntax to enforce the right number of arguments, no more no
> less.

Enforcing things at compile time can be done under either system.
It I could just as easily define my format stream to require
endf and warn. But that im my opinion wouldn't be a good thing.
Runtime exceptions for petty things like too many arguments are
an annoyance. After all it is just going to print the wrong
thing not run out of memory!

> My code raises exceptions in both cases.
> I know that yours does not notice excess of arguments,
> and I don't know what happens with a lack of arguments.
> Do you check for that inside the object's destructor ??
> (or rather, the proxy's destructor. the first
> cout << format("...") returns a temporary proxy)
>
> Thus, if somebody forgets one argument :
> cout << format("%2$s bla bla %1$s") << x;
> When the proxy dies while still waiting for an argument, you can detect
> that and raise an exception.

Why do that? My implementation just said your the boss and
print "bla bla x". If the strings are coming from a translations
database there are going to be errors. The program should die
from an exception just because of that.

> That's the most probable, and most disastrous mistake, so it's good that
> your ofrstream can catch it.
> But the other kind of user errors -too much arguments-, should be
> detected too, and that is not possible unless you force the user to use
> "endf" before continuing to use the stream.
>
> That's why I thought the endf was required, rather than optional.
> (in my opinion it should be required, so that excess of arguments can be
> detected)
>
>
> > became
> > operator<< (cout,"A ");
> > operator<< (cout, s1);
> > operator<< (cout," ");
> > operator<< (cout, s2);
> > operator<< (cout, endl);
>
>
> After reading Heintzelman messages, I thought you were claiming that
> your class calls only those operations, in the sample case.
> But you're using internal streams, and strings, just as I do,
> Actually you are only describing what will be eventually output to cout.
> (I'm explaining this in case other people misunderstood as I first did)

Right, though I am also capturing the flags and passing them to stream.

So
  cout << format("%2$d %1$d") << hex << 1 << 2 << 3;

means

  cout << savestate;
  cout << savestate << 2 << restorestate;
  cout << savestate << hex << 1 << restorestate;
  cout << restorestate;
  cout << 3;

It was a manipulator which starts an argument counting.
If used with a proxy, it can copy the initial state of the stream
to the first argument.

> > Further, you can force it off with endf
> > cout << format("A %s %s") << s1 << endf << s2;
> >
> > (extra argument lost)
>
> err, what exactly happens in this case ?
> Is s2 really lost ? (why not send it to cout?? that would be more
> natural)

Sorry I mean it isn't passed to the format at all. It goes
straight to ostream.

> Does the format agrees to output the partial result (second argument is
> missing), without raising an exception ??
> It would be bad behviour, very error prone.

I don't see it that way. Having exception handling for every
string (which is what it would require in an i18n program) is
unrealistic.

 
> > Extreme shortness in typing charactors is not a
> > very good reason for a design decision.
>
> You're right. But that was not a reason for me,
> just a bonus.
> against those who say
> "<< is better, it looks good. just like good old streams."
> I can say :
> "<< is better, it's shorter and makes the code easier to read."
   ^^
   %

How is making a stream like stream not stream like make code easier
to read? You have totally lost me.

 
> > > On the other hand, with operator% :
> > > 1. the code is smaller than with '<<' and, in my opinion, nicer.
> >
> > If it acts like stream make it look like a stream. The charactors
> > saved are not worth force the user to have more concepts. Format
> > should be considered a manipulator and as such should not change the
> > syntax.
>
> Do you really think it acts like a stream, and that a format object is
> like a manipulator ??
> I don't. It's much more complex than a manipulator, and does the
> formatting internally, not with the stream passed on the left.

If the stream had formating in the class (like hex) it would
act that way. Thus it is a manipulator. The stream has states,
this just glues more states on to it.

> I think using the same notation than stream only confuses everything,
> and could in the end mislead the user.
> I prefer have format do his job, with a notation which makes it easy to
> see where it starts and where it ends.
> cout << "Time in London : " << format("%1 h %2") % h % m
> << "\nTime in Paris : " << format("%1 h %2") % (h+offset) % m
> << endl;

  cout << format("Time in London : %1$s h $2$s\n") << h << m
       << format("Time in Paris : %1$s h $2$s\n") << h+offset << m;

It seems clear to me.

 
> I see format objects as "string makers", that you can output to an
> ostream.
> nasonov said he thinks they should be seen only as "string makers" (in
> substance), and thus he does not provide operator<<(stream, format).
> I think this operator is useful and I provide it as a shortcut to
> "take the string, send it to the stream", but somehow I agree with him.
> I like the fact that operato% clearly identifies what goes with the
> format object, and what goes with the stream. So it's compatible with
> the operator<<(stream, format) while keeping the "string maker"
> aspect.
> It's visually very clear to see what is happenning in the previous
> example.

If you want to force rules of presidence use what is given for
that...

   cout << (format(str) << 1 << 2) << endl;

The class could be designed to handle that.

> > > ( format("%1 %2") % 1 ) + 2 % <the_rest_of_the_line>
> > > So it tries to call operator+ on a format object, => the error is
> > > detected at compile time.
> >
> > What is wrong with 'cout << (format("%1 %2") << 1) ;' ?
>
> hum, the user forgot one of the 2 arguments (raise an exception, no ?)
> but I don't see the link with what I tried to say.

How is the execption to be handled? What good does it do to raise it?
Can you provide a reasonable code where raising an exception when
strings are being provided from an outside source will make
a critical difference to the coder?

 
> What I meant is : in case of problems due to the precedence of %, it
> won't pass compilation. (because it will call operator+(format, int) )
> More generally, it will call operator+(format, T)
> or operator-(format, T)
> none of which will be defined.

I don't doubt that it will be caught, only that is in necessary
for the user to make the mistake in the first place.

Std doesn't redefine concepts on one item to be different. If things
act the same they are the same. That is why all containers use
assign and not assign in one and set in another. I am saying you
are adding a concept unnecessarily.

> > The compiler should construct the format make its contents then
> > put it out when it hits the stream.
> >
> > Just like
> > basic_format<char, trait<char> > f("%1 %2");
> > f << "a" << "b";
> > cout << f;
> >
> > Should allow the format to be made ahead of time.
>
> My format does that. (with %, of course..)
>
> > (okay I am glossing over what should it do with
> > cout << hex << format("%d") << 16;
> > But the point is there we could implement something
> > much niced and more stream like.)
>
> ah ! That's a good example to show that formats dont act like a
> manipulator. Which is the best reason to use a notation that does not
> look like it were a manipulator.

But the one I wrote did. The state of the stream is
inherited. Thus gets interpreted as

  cout << hex << save << save << dec << 16 << restore << restore ;

> > cout << hex << format("%d") << 16;
> Who could guess that hex won't have an effect,a nd that it will print
> "16" instead of "10" ??

the %d should be a clue. :-)

> Compare with :
> cout << hex << format("%d") % 16;
>
>
> That's a good point for % rather than <<.
> In fact, in my opinion, it's the strongest point. the rest is mre
> subjective.
> While this one is essential : we should not use << because format is NOT
> a manipulator.

But is a maniplator.

> I would use << only if we could turn format into some kind of clever
> manipulator, which distributes the following arguments directly to the
> stream, in the correct order (setting some stream flags on the fly if
> asked to).
> That's the ideal Heintzelman talked about, and I doubt it is feasible
> (not yet looked what the 'bind' library does, though)
>
> That's not at all what our format classes currently do (mine as well as
> yours).
> It's string makers, not manipulators.

You copy the state from the stream to the proxy streams thus the
end result is to make it look like a manipulator.

--Karl


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