Topic: Stream Manipulators


Author: gustav@morpheus.demon.co.uk (Paul Moore)
Date: 1997/04/21
Raw View
I'm just discovering what you can do with stream manipulators, and I'm
fairly impressed. But I've hit some problems, and I'm not sure whether
it's because what I am doing is not right, or whether my compiler is
failing to support the standard properly. First of all, the problem -

If I create a stringstream manipulator:

    #include <sstream>

    stringstream &display (stringstream &str)
    {
       // platform specific stuff to display
       // the contents of str in a GUI window

       // Is this the right way to reset a stream to empty?
       str.str("");

       return str;
    }

    static stringstream Msg;

and then use it in code as

    Msg << "Hello there!" << display;

nothing happens (not even a compile error or warning!). Should this
work?

I added an operator<< definition, in case the library stuff didn't
work for user-defined stringstream manipulators

    stringstream& operator<< (stringstream& s,
                              stringstream& (*f)(stringstream&))
    {
        (*f)(s);
        return s;
    }

but this didn't help...

I then changed the call to

    Msg << "Hello there!";
    Msg << display;

and this worked!

Do I have a bug in stringstream& operator<< (stringstream&, const
char*) not returning the right type? It looks like the result of
Msg << "Hello there!" is not being seen as a stringstream, and so my
manipulator is not being applied...

Is what I did in the first place right (in the sense of standard-
conforming)? If not, what should I do? I can't think that the last
version I have is what the standard requires.

If it is OK according to the standard, what bits of what is going on
is my compiler likely to be missing (ie, what are the most recent
features needed?)

I'll admit it - I'm using MSVC 4.2b (so, it's probably a compiler bug
:-() But I'm moving to MSVC 5 soon - that's supposed to be closer to the
standard - am I OK on that?

Thanks,
Paul Moore.
------------------------------------------------------------------------
Paul Moore                                   gustav@morpheus.demon.co.uk
------------------------------------------------------------------------
... Back Up My Hard Drive? I Can't Find The Reverse Switch!
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: kuehl@horn.informatik.uni-konstanz.de (Dietmar Kuehl)
Date: 1997/04/23
Raw View
Hi,
Paul Moore (gustav@morpheus.demon.co.uk) wrote:
: If I create a stringstream manipulator:

:     #include <sstream>
:     stringstream &display (stringstream &str)

A function with this signature is not a very useful manipulator: It
works only if the object it is applied to is known to be a
'stringstream'.  However, inserters, ie. 'operator<< ()' with an
'ostream' as first argument or corresponding members of 'ostream',
normally return an 'ostream' (corresponding for extractors).  Thus,
your 'display()' manipulator should have the signature

      ostream &display(ostream &out)

Since you rely on the the stream to be an 'ostringstream' (or a stream
derived thereof), this already highlights that there is a conflict: In
other terms, I don't think that you are taking the right approach to
displaying something in a GUI window (see below). Of course, you can
make sure that you really use an 'stringstream' (using casting) but
this is clearly ugly and there is a much better alternative.

:        // Is this the right way to reset a stream to empty?
:        str.str("");

Yes, I think this is the correct approach to clearing a 'stringstream'
(but it will turn out that it is not needed for this problem anyway).

:     static stringstream Msg;

A global object is close to never a good idea.

:     Msg << "Hello there!" << display;

: nothing happens (not even a compile error or warning!). Should this
: work?

It is surprising that this compiles! Apparently, there is a 'display()'
manipulator with the correct signature defined.  Otherwise, this should
give a compiler error because the type of 'Msg << "Hello there!"' is
'ostream&' (unless you have overloaded 'operator<< (stringstream &,
char const *)').

: I added an operator<< definition, in case the library stuff didn't
: work for user-defined stringstream manipulators

:     stringstream& operator<< (stringstream& s,
:                               stringstream& (*f)(stringstream&))
:     {
:         (*f)(s);
:         return s;
:     }

: but this didn't help...

It still makes no difference: An 'ostream&' does not match the type
'stringstream&'.

: I then changed the call to

:     Msg << "Hello there!";
:     Msg << display;

: and this worked!

Yes, because now the type matches.

: Do I have a bug in stringstream& operator<< (stringstream&, const
: char*) not returning the right type? It looks like the result of
: Msg << "Hello there!" is not being seen as a stringstream, and so my
: manipulator is not being applied...

Indeed, the type of the expression 'Msg << "Hello there!"' should be
'ostream&' but this is not a bug: The class 'ostringstream' (and
classes derived thereof) do not overload 'operator<<()'. All those
operators are inherited from 'ostream' or are non-member function
taking an 'ostream&' as first argument.  This is important because
otherwise you would have to write every inserter for every type with an
overloaded 'operator<<()' and for every "external representation", like
files, strings, etc. Writing to an 'ostream' instead assures that you
only have to write one inserter for every type (ie. you don't need to
define a new inserter for every external representation).

: Is what I did in the first place right (in the sense of standard-
: conforming)? If not, what should I do? I can't think that the last
: version I have is what the standard requires.

Well, your code seems to be standard conforming but you should take a
different approach to the whole problem:  You should create a new
external representation which writes directly to a GUI window instead
of using the detour through the 'string'. This is actually not that
hard:  All you have to do is to derive from 'streambuf' and override
some virtual functions. For some examples of how to do this, you might
have a look at
<http://www.informatik.uni-konstanz.de/~kuehl/c++/iostream/>.  There
you will find two new external representations one of which actually
writes to a GUI window (a Motif Text Widget).

: If it is OK according to the standard, what bits of what is going on
: is my compiler likely to be missing (ie, what are the most recent
: features needed?)

It is not your compiler which misses anything (at least not in this
respect). It is your design which tries to achive a goal for which a
different solution is envisioned by the designers of the IOStream
library.

: I'll admit it - I'm using MSVC 4.2b (so, it's probably a compiler bug
: :-() But I'm moving to MSVC 5 soon - that's supposed to be closer to the
: standard - am I OK on that?

No, it is no compiler bug and moving to a different compiler would not
solve this problem. It is a design bug. Moving to a different design
will solve the problem and proivde a solid solution to the problem of
writing to a GUI window using the IOStream interface.
--
<mailto:dietmar.kuehl@uni-konstanz.de>
<http://www.informatik.uni-konstanz.de/~kuehl/>
I am a realistic optimist - that's why I appear to be slightly pessimistic
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: gustav@morpheus.demon.co.uk (Paul Moore)
Date: 1997/04/23
Raw View
On 23 Apr 97 01:42:10 GMT, kuehl@horn.informatik.uni-konstanz.de
(Dietmar Kuehl) wrote:

>Hi,
>Paul Moore (gustav@morpheus.demon.co.uk) wrote:
>: If I create a stringstream manipulator:
>
>: Is what I did in the first place right (in the sense of standard-
>: conforming)? If not, what should I do? I can't think that the last
>: version I have is what the standard requires.
>
>Well, your code seems to be standard conforming but you should take a
>different approach to the whole problem:  You should create a new
>external representation which writes directly to a GUI window instead
>of using the detour through the 'string'.

Thanks for the useful comments. I've looked at your suggestions, and I
see what you're getting at (I'm surprised how easy it is to create a
derived stream type).

However, I oversimplified the problem, in a way. My display function
actually shows the message built so far in a message box, pausing the
program until the user presses OK. So, I need full control over *when*
the message is displayed - which is what my "display" function gives,
as it only executes the display code when I ask.

If I use a derived streambuf, wouldn't I put the display code in the
"flush" method? And if so, how would I make sure that the system
didn't call flush() except when I asked it to? Alternatively, is there
another method I should use rather than flush()?

By the way, I can think of other implementation methods - build the
message in a strambuf, as I do, then call a function show(streambuf&).
But in C I would write a function show(fmt, ...). In parallel with
printf(), I'm trying to change this style to something type-safe in
C++, hence my preference for the operator<< method. Hope this explains
a bit why I'm pushing at this approach, rather than going for a
different approach.

Paul Moore.

------------------------------------------------------------------------
Paul Moore                                   gustav@morpheus.demon.co.uk
------------------------------------------------------------------------
... Back Up My Hard Drive? I Can't Find The Reverse Switch!
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: kuehl@horn.informatik.uni-konstanz.de (Dietmar Kuehl)
Date: 1997/04/28
Raw View
Hi,
Paul Moore (gustav@morpheus.demon.co.uk) wrote:
: However, I oversimplified the problem, in a way. My display function
: actually shows the message built so far in a message box, pausing the
: program until the user presses OK. So, I need full control over *when*
: the message is displayed - which is what my "display" function gives,
: as it only executes the display code when I ask.

I would implement this behavior as a combination of a new 'streambuf'
(say 'GUIbuf') and a modifier: The 'GUIbuf' (potentially derived from
'stringbuf') would collect the characters until a method specific to
the 'GUIbuf', eg. 'display()', is called. This would then pop up the
window and wit for the user interaction.

To make the use of this method easier, you might introduce a 'display'
modifier, which will call the 'GUIbuf::display()' function if it
applied to an 'ostream' which refers to a 'GUIbuf'. The corresponding
implementation might look like this:

  ostream &display(ostream &out)
  {
    if (GUIbuf *sbuf = dynamic_cast<GUIbuf*>(out.rdbuf()))
      sbuf->display();
    return out;
  }

If your compiler does not yet support 'dynamic_cast', you may use a
simple cast and some other indication whether the 'streambuf' returned
from 'rdbuf()' is indeed a 'GUIbuf' (eg. a pointer stored via 'pword()'
when installing a 'GUIbuf').

: If I use a derived streambuf, wouldn't I put the display code in the
: "flush" method? And if so, how would I make sure that the system
: didn't call flush() except when I asked it to? Alternatively, is there
: another method I should use rather than flush()?

I would not put the code into the 'flush()' mehtod (ie. the 'sync()'
member of the corresponding 'streambuf'):  This function is too often
called, eg. by 'endl' or even automatically if 'ios::unitbuf' is set.

: By the way, I can think of other implementation methods - build the
: message in a strambuf, as I do, then call a function show(streambuf&).

Of course, you can use such an approach. However, I would prefer the
approach of using a new 'streambuf'.
--
<mailto:dietmar.kuehl@uni-konstanz.de>
<http://www.informatik.uni-konstanz.de/~kuehl/>
I am a realistic optimist - that's why I appear to be slightly pessimistic
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/04/28
Raw View
gustav@morpheus.demon.co.uk (Paul Moore) writes:

|>  On 23 Apr 97 01:42:10 GMT, kuehl@horn.informatik.uni-konstanz.de
|>  (Dietmar Kuehl) wrote:
|>
|>  >Hi,
|>  >Paul Moore (gustav@morpheus.demon.co.uk) wrote:
|>  >: If I create a stringstream manipulator:
|>  >
|>  >: Is what I did in the first place right (in the sense of standard-
|>  >: conforming)? If not, what should I do? I can't think that the last
|>  >: version I have is what the standard requires.
|>  >
|>  >Well, your code seems to be standard conforming but you should take a
|>  >different approach to the whole problem:  You should create a new
|>  >external representation which writes directly to a GUI window instead
|>  >of using the detour through the 'string'.
|>
|>  Thanks for the useful comments. I've looked at your suggestions, and I
|>  see what you're getting at (I'm surprised how easy it is to create a
|>  derived stream type).
|>
|>  However, I oversimplified the problem, in a way. My display function
|>  actually shows the message built so far in a message box, pausing the
|>  program until the user presses OK. So, I need full control over *when*
|>  the message is displayed - which is what my "display" function gives,
|>  as it only executes the display code when I ask.

In sum, you are not doing "streamed" output.

This can be done by deriving from streambuf; the simplest solution would
involve your streambuf containing (perhaps by reference) a stringbuf (or
a strstreambuf, for older compilers), and simply forwarding the output
to it until display was requested.

I would argue, however, that you are not "streaming", so the streambuf
idiom is not entirely appropriate.  Most of the time, for things like
this, I will have a display function (or class) taking either the
strstreambuf or the ostrstream as argument.  In effect, I disassociate
the creation of the message (which involves streaming, but without
display), and the display.

I don't think that this is an absolute, however, and I can understand
the other side, i.e.: people who do implement this as a form of a
streambuf.

|>  If I use a derived streambuf, wouldn't I put the display code in the
|>  "flush" method? And if so, how would I make sure that the system
|>  didn't call flush() except when I asked it to? Alternatively, is there
|>  another method I should use rather than flush()?

I don't think that it belongs in the flush (actually, streambuf::sync)
method.  You are right to imagine that flush can be called at other
times.  If you do use a streambuf, I would create a special method for
the display, distinct from flush.

Note that from a theoretical point of view, this is related to my
opinion that you are not streaming.  Flush should ensure that all data
already inserted are transmitted to their final destination, without any
comment with regards to later data.  Flush should be "transparent", in
the sense that whether it is called or not should not have a direct
effect on the behavior of the program, provided it is called at least
once after the last character has been written.  If I understand
correctly, in your case, your function will also declare that there will
not be any later data.  This is an entirely different semantic, and so
warrents a different function.

|>  By the way, I can think of other implementation methods - build the
|>  message in a strambuf, as I do, then call a function show(streambuf&).

Correct.  I would probably, in fact, associate this function with an
object controlling the window.

--
James Kanze      home:     kanze@gabi-soft.fr        +33 (0)1 39 55 85 62
                 office:   kanze@vx.cit.alcatel.fr   +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
     -- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]