Boost logo

Boost :

From: Karl Nelson (kenelson_at_[hidden])
Date: 2002-01-29 19:03:46


>
> Yes, this would work, also. I'm assuming that format_expr can be
> initialized with a format string plus any number of manipulators. I don't
> see what having a separate object buys you, though.

Format is a function which both "compiles" the format to a list structure
(format_expr) and then stuffs the format_expr with arguments.
One advantage of this is that you don't have to specify the char width
in doing that.

Ie.
   basic_format_expr<char> format(const char*, args .... );
   basic_format_expr<wchar_t> format(const wchar_t*, args .... );

Second the "format compile" only gets done once when reused.
I am basing this on the concepts of regular expressions where the
expression must have an expensive compile to an internal representation,
but using that representation should be cheap.

Format actually becomes very simple in that it looks like.

  template <class T_char, class T_trait=char_traits<T_char>,
            class T_arg1, class T_arg2>
  basic_format_expr<T_char,T_trait> format(const T_char* str,
        const T_arg1& a1, const T_arg2& a2)
    {
       basic_format_expr<T_char,T_trait> fe(str);
       fe.rewind();
       fe.push(a1);
       fe.push(a2);
       fe.done(); // throws incorrect_arguments
       return fe;
    }

> Actually, where the
> real synergy would lie is in applying the manipulators to the ostream, which
> currently is possible. By introducing the format layer (regardless of the
> existence of format_expr), we are really taking a step back. If formatting
> were native to ostream, we could leverage the existing manipulator
> persistence (or reuse, if you prefer).

My approach is to merge all of the flags from the formating string
together. Thus rather then use manipulators which you must apply in
repeatedly you simply apply all at once.

 
> > > It depends on the persistence semantics that are in force.
> > I wouldn't
> > > recommend using the semantics you assume for this example.
> > The manipulator
> > > should apply to all subsequent arguments, regardless of
> > their placeholder
> > > position. Then the example would come out fine.
> >
> > Still a hard call. You would have to know that hex should
> > be sticky and apply it to every stringstream after that point.
>
> Specifying whether a manipulator should be sticky (I've been using
> persistent (vs. local)) should be syntactically simple, for example
> "format("[1]")(sticky)[manip(local, param1)]", possibly with some of the
> more common manipulators redone so that
> "format("[1]")(sticky)[local(param1)]" is possible. It's up the programmer
> to define his own scope for the manipulator: it's just a matter of how he
> manages the lifetime of the format object.

If you are using comma list and group concepts, stickyness is totally
out of place. Especially if you have a "format" specifier which
states the base for every argument. Thus if you want the next
argument to be hex you simply state that...

  cout << format("%s %x %x %d", i, j, k, l);

Rather than
 
  cout << format("[1] [2] [3] [4]") % i % hex % j % k % dec % l;

Which of those two would you grasp more quickly? Which is more
compact?

> > Isn't placing something into a stream automatically a method of char
> > conversion? All that the type specifiers do is govern how
> > the conversion
> > is done. Unfortunately, to do a "good" job of defining a universal
> > format string is strictly limited to what you can do with ostream and
> > and what you can do with the post conversion string.
>
> There is a type->string conversion, yes. I was saying that there shouldn't
> be a type1->type2->string conversion, which I thought was your point also.
> I agree that any specifiers in the placeholder just specify details of the
> type->string conversion.

Agree. That is why "%s" is to be a "format specifier" not a "type
specifier" as in C. You are saying "string literary" should be used
for any conversion of type->string.

>
> > > However, it doesn't make sense to gratuitously include
> > such. The problem is
> > > that if there is a mismatch, as you are proposing char and
> > %d would be,
> > > format has to do something about it. It can throw or
> > silently fail, where
> > > "silently fail" means display in the default rendering for
> > that type. Each
> > > option is wrong in some cases. Why make the code author
> > respecify the type
> > > that the compiler already knows perfectly well?
> >
> > It doesn't know how the user wants it printed. What base,
> > what precision,
> > should it be scientific or fixed?
>
> Not exactly. Short of instructions from the user to the contrary, format is
> free to assume that the user wants whatever is specified by the current
> manipulator context. The context is the result of hard-coded defaults
> overridden by persistent manipulators overridden by local manipulators. The
> placeholder only needs a format specifier if the user wants to add one last
> override.

Having persistence in a format is also bad in that it could wrap...
Ie.

   format_expr f("[1] = [2]");
   cout << format(f) % 16 % hex % 16;
   cout << format(f) % 16 % hex % 16;

If you have a model of sticky then the second line would potentially
print "10 = 10".

While under what I am writting you would write the above as

   format_expr f("%d = %x");
   cout << format(f, 16, 16);
   cout << format(f, 16, 16);

--Karl


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