From: Thomas Maeder on
Andreas Magnusson <eamagnusson(a)gmail.com> writes:

> I've tried the following code both with two different version of MSVC
> (7.1 and 8) and GCC, all of them give the same (for me) strange
> result. First the code:
>
> class sstream_derived : public std::stringstream

The Standard C++ Library stream classes are not designed to serve as
(non-private) base classes.

What is the problem that you are trying to solve by deriving from
std::stringstream?


> {
> public:
> sstream_derived()
> {
> std::cout << "sstream_derived()" << std::endl;
> }
> sstream_derived(const sstream_derived &ssd)
> {
> std::cout << "sstream_derived(const sstream_derived &ssd)" <<
> std::endl;
> }

Stream objects are supposed to not being copyable, because it's very
hard to find meaningful semantics for this operation.


> virtual ~sstream_derived()
> {
> std::cout << "~sstream_derived: " << str() << std::endl;
> }
> };
>
> class test
> {
> public:
> sstream_derived get_stream()
> {
> return sstream_derived();
> }
> };
>
> int main()
> {
> test t;
> std::string name = "Andreas";
> int age = 30;
> //
> t.get_stream() << "Hello, my name is " << name << ", I'm " << age <<
> " years old!";

The type of the expression t.get_stream() is sstream_derived, and the
type of "Hello, my name is " char[19]. Both these types belong to the
global namespace.

When a matching operator<< is looked for, only members of
sstream_derived and operator<< non-members that belong to the global
namespace are considered; these include opeator<<(void *) (which is a
member) and many arithmetic operator<<s; the operator<< overload for
char const * is not considered since it is a non-member and belongs to
namespace std.


> t.get_stream() << 42 << "Hello, my name is " << name << ", I'm " <<
> age << " years old!";

The type of the expression t.get_stream() << 42 is std::ostream. This
means that for the expression

t.get_stream() << 42 << "Hello, my name is "

operator<< non-members in namespace std are also considered, including
the operator<< overload for char const *, which turns out to be the
best match.

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Andreas Magnusson on
On 6 Jan, 17:01, int...(a)gmail.com wrote:
> On 6 Jan, 02:34, Andreas Magnusson <eamagnus...(a)gmail.com> wrote:
>
>
>
> > I've tried the following code both with two different version of MSVC
> > (7.1 and 8) and GCC, all of them give the same (for me) strange
> > result. First the code:
> >
> > Anyone knows what's wrong?
>
> The problem has nothing to do with you inheriting from
> std::stringstream (though why you'd want to is still a mystery), and

Hehe, I'm mystery man. Nah, seriously I could just as well derive from
std::ostream and construct it with a std::stringbuf which basically is
what std::stringstream does. I just thought I'd save myself a new/
delete pair.

> everything with the fact that operator<<(const void*) is defined as a
> member in class std::basic_ostream, while operator<<(basic_ostream&,
> const char*) is a global function in namespace std. Note the signature

Thanks, that was the answer!

> of the latter - the first argument is a reference to non-const. Now,
> your get_stream() function returns an temporary object; and
> temporaries do not bind to such references. On the other hand, all
> member overloads for operator<< are perfectly valid for a temporary,
> and so the only one of those matching the argument - const char* - is
> selected, which is the const void* one.
>
> A better question is, why are you even trying to return a copy of a
> stream from a function?

Yeah, I agree. My idea was to output the complete stringstream in the
dtor of sstream_derived. Thus I need the dtor to run, preferably
before the end of the current block. By returning an rvalue I can get
the desired functionality, except of course for the small fact that
std::basic_ostream<> doesn't define a member overload for
operator<<(const char *), which I didn't know, now I do. That's also
the reason I want to derive from std::stringstream. I want all that
std::stringstream has but I want to define my own dtor. Now you now
all my secrets.

Thanks a lot for your help!

Andreas

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Andreas Magnusson on
On 6 Jan, 17:05, "Daniel Kr�gler" <daniel.krueg...(a)googlemail.com>
wrote:
> On 6 Jan., 00:34, Andreas Magnusson <eamagnus...(a)gmail.com> wrote:
>
> First, I expect to see at least the following include's here:
>
> #include <sstream>
> #include <ostream>
> #include <iostream>

Of course, the program wouldn't compile without them. I just thought
I'd save some space by excluding lines completely unrelated to my
question.

> It seems that something nasty happens here ;-)
>
> You are providing a faking copy c'tor, because
> the copy c'tor does not invoke the copy c'tor
> of it's base class. Of-course, this would not
> be possible, since the base class itself is
> not copyable.

Yeah, I know, I'm not using it but GCC wouldn't compile unless I had
it and I compiled the code with GCC to see whether my problem was MSVC
specific or not. It would've been nasty had I used it.

> Is this construction by design?

Yep. The magic is to do the output in the dtor.

> > class test
> > {
> > public:
> > sstream_derived get_stream()
> > {
> > return sstream_derived();
> > }
> > };
>
> The immediate source of your problem is
> test::get_stream, which returns an rvalue
> of sstream_derived. Now consider a shortend
> version of your problem code:
>
> test().get_stream() << "Hello, my name is ";
>
> You seem to expect that this line should
> output the character string "Hello, my name is "
> but this is not possible, because the corresponding
> inserter function need to be one of the the free
> function templates from header <ostream>:
>

[snip]

> template<class traits>
> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&,
> const char*);
>
> All of them expect an *lvalue* of basic_ostream<char,traits>,
> which is not what you provide.

Thanks, that's exactly what I was looking for. I have always thought
that std::basic_ostream<> has overloaded <<-operators for *all* built-
in types, but now I know it doesn't and I see why it would never work.

> In C++0x this workaround is not needed du to the newly
> introduced rvalue references and the movability of streams.

That sounds nice.

Thanks,
Andreas


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Daniel Krügler on
On 7 Jan., 22:30, Andreas Magnusson <eamagnus...(a)gmail.com> wrote:
> On 6 Jan, 17:05, "Daniel Kr�gler" <daniel.krueg...(a)googlemail.com>
> wrote:
> > First, I expect to see at least the following include's here:
>
> > #include <sstream>
> > #include <ostream>
> > #include <iostream>
>
> Of course, the program wouldn't compile without them. I just thought
> I'd save some space by excluding lines completely unrelated to my
> question.

I added the remark, because some astonishing effects indeed
*can* be based on missing include headers, still leading to
compilable code. One example is the following: In recent C++03
does *not* exist any explicit requirement that <iostream> already
includes everything contained in <ostream>. You might wonder
concerning this, but there are conforming implementations, which
doesn't. The reason for this is, that the IO classes have to fulfill
some hard to implement properties (e.g. even in the c'tor or d'tor
of global static instances of a class std::cout and friends
"have to work"), thus implementors usually have to use some
heavy hackery tricks (which are granted by the standard for them)
to realize these extraordinary requirements.[1]

So the following very simple program - which has some
similarities to yours - indeed can result in a very similar
observation as in your case:

#include <iostream>

int main() {
std::cout << "Hello World\n";
std::cout.flush();
}

Because on some implementations the inserter
for const char* is not declared here, the program
would also chose the member inserter for const void*.

Because of these subtilities with header dependencies
I usually check them and mention missing ones and
I strongly recommend to *always* provide a *complete*
running program - especially in cases of an error
analysis.

> > It seems that something nasty happens here ;-)
>
> > You are providing a faking copy c'tor, because
> > the copy c'tor does not invoke the copy c'tor
> > of it's base class. Of-course, this would not
> > be possible, since the base class itself is
> > not copyable.
>
> Yeah, I know, I'm not using it but GCC wouldn't compile unless I had
> it and I compiled the code with GCC to see whether my problem was MSVC
> specific or not. It would've been nasty had I used it.

Actually it is possible to simulate a *copy*
of a stream, see e.g. Langer/Krefts book
"Standard C++ IOStreams and Locales" for details.
In short you need to

- Clear the exception mask
- Copy the stream state (rdstate) via clear
- Assign the stream buffer (rdbuf)
- Invoke copyfmt

There still remain some subtle problems
doing so, so for this reason streams are
not copyable by design.

[1]: There are attempts to define at least some explicit header
dependencies for C++, see the still open issue:

http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#343


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]