Boost logo

Boost :

From: John Panzer (jpanzer_at_[hidden])
Date: 2000-03-09 14:32:35


"Moore, Paul" wrote:

> From: Dietmar Kuehl [mailto:dietmar_kuehl_at_[hidden]]
> > > Basically, the idea is to make a class derived from stringstream
> > [snip]
> >
> > Already at this point it is almost certain that it is not such a good
> > idea: You should not derive from 'std::ostream' or 'std::istream' or
> > any class derived thereof for a different purpose than convenient
> > construction and convenient access to a specialized stream buffer.
> > Instead of deriving from these classes, you normally want to create a
> > new stream buffer although most people don't seem to be aware of this
> > fact...
>
> I was aware that creating a new stream buffer was the "official" way -
> however, as all I was likely to do was to collect text into a string, which
> is precisely what a stringstream does, it seemed unreasonable to reimplement
> stringstream just for this...
>
> Probably I should have written a wrapper, using containment - something
> like:
>
> class MyClass {
> stringstream s;
> public:
> template <typename T> operator<<(T t) { s << t; return *this; }
> }
>
> This seems OK now that template members stand a chance of working... OTOH,
> if I really need a friend operator<< in there (I have a sneaking suspicion I
> might) then template friends are *definitely* a bit bleeding-edge for this
> to be reliable yet.
>
> > In a derived stream buffer you can direct the character wherever you
> > want to have them. For example, at
> > <http://www.fmi.uni-konstanz.de/~kuehl/iostream/> you can
> > find a stream buffer writing to a GUI window (in this case to a Motif
> > TextWidget but this can be adapted to other GUI systems quite easily).
> > Although your approach using a destructor to gather all the output works
> > for some situations, it does not work that smoothly in others and
> personally I
> > prefer to do only clean-up in destructors.
>
> Yes, I've seen your web pages - they were my first introduction to some of
> the wonderful things you can do with iostreams. The problem is that the
> Windows MessageBox() function which I'm interested in here is a one-off call
> - effectively, MessageBox(str) displays str in a window, and waits for the
> user to acknowledge before returning. The string MUST be complete when the
> call is made, otherwise you get multiple message boxes. I thought about
> displaying the box on a flush(), but I couldn't be sure that the iostream
> system wouldn't call flush() internally during the formatting (as flush is a
> buffering thing, and buffering is supposed to be transparent to the user).
>
> I could have a MsgStream type, with a special member function display(), but
> then I need to call as
>
> {
> MsgStream s;
> s << "Hello!";
> s.display();
> }
>
> This is unwieldy, as my intended use was as a debugging aid - sprinkle
> little
>
> Msg() << "Got to line " << __LINE__;
>
> statements through the code. For this usage, it really *needs* to be a
> one-liner. And using a manipulator is no good, as manipulators need to work
> on ostreams, not on derived classes... [Yes, I did think pretty hard about
> all this...]
>
> > > MyClass() << "This is some output, you can do numbers, too: "
> > > << hex() << 1234;
> >
> > Although the compiler I'm using swallows a similar statement without
> > complaint (using 'std::ostringstream()' rather than 'MyClass'; and the
> > 'hex()' replaced by 'std::hex'), I think this should not compile: The
> > temporary is bound to a non-const reference when passing it
> > as argument to 'std::opererator<< (std::ostream&, char const*)'.
>
> Ugh. That's a real issue. I hadn't thought of that problem, but I guess you
> are right. Sounds like it should be rejected.

Ouch. I'm doing something similar for logging, and I'm returning a temporary
object from a helper function, so I think I have the same issue:

   logger log(LogLevel loglevel);
   ...
   log(error) << "foo" << 42 << endl;

This works nicely, and lets me synchronize access to an output stream from
multiple threads (in this case, the logger constructor actually locks the output
stream and the destructor unlocks it.)

I wonder if something like this would help solve the
temporary-bound-to-non-const-reference problem, and as a separate topic, whether
it would be a good idea:

class logger {
    mutable ostream &realStream;
    ...
};

const logger log(...);
log(error) << "foo" << endl;

John Panzer


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