CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 10 of 10
  1. #1
    Join Date
    Jun 2006
    Posts
    42

    Odd behaviour in overloaded std::stringstream

    Hi,

    I'm having a problem with overloading std::stringstream.
    What I'm trying to achieve is a stringstream that is instantiated only as a temporary and outputs its content on destruction.

    Here is the code:

    Code:
    #include <iostream>
    #include <sstream>
    
    class stupidostream : public std::ostringstream {
    public:
            stupidostream() : std::ostringstream() { }
            stupidostream(const stupidostream& reference) : std::ostringstream() { }
            virtual ~stupidostream() { std::cout << "\""<< this->str() << "\"" << std::endl; }
    };
    
    
    stupidostream getStream()
    {
            return stupidostream();
    }
    
    
    int main()
    {
            getStream() << "Hello" << " World";
    }
    What happens is: getStream creates a new stupidostream (which in theory is copied but the compiler should optimise that away).
    Then two strings are inserted into the stream. At the ; the stream loses scope, its destructor gets called and the content is displayed.

    The expected behaviour of the above therefore is that "Hello World" is displayed, but instead it says "0x80494e8 World" or some other memory address. This means the character pointer is interpreted as an address instead of a c-string.
    Since I didn't overload the << operator, the first shift returns a reference to regular std::ostringstream (or one of its base classes) and " World" is inserted properly.
    But why? If I insert a std::string or a number first, everything works as expected.
    If I overwrite operator<<(const char*), everything works as expected, but in the above example, the compiler chooses the wrong overloaded operator<<.
    This happens in both gcc and visual c++, so it has to be a problem in my code...

  2. #2
    Join Date
    Oct 2002
    Location
    Austria
    Posts
    1,284

    Re: Odd behaviour in overloaded std::stringstream

    I don't think that it is easily possible to do what you want.
    in order to have a proper copy constructor of your class it would have to look like this
    Code:
    stupidostream( const stupidostream& reference):std::ostringstream(reference){  }
    and that is not possible because the the copy constructor of std::ios_base is private.
    In other words streams are not copyable.
    Kurt

  3. #3
    Join Date
    Apr 1999
    Posts
    27,449

    Re: Odd behaviour in overloaded std::stringstream

    Zuk is correct, streams are not copyable even though the compiler allowed it (by chance)

    Also, I think this is relevant:

    You're deriving from a template class. Once you do that, some members of the base class (determined by dependent/non-dependent name rules) are not available to you unless you specifically call them. Therefore, your derived class must explicitly call the ostringstream operator <<, and not rely that the compiler will call it for you.

    Your error is more than likely related to this FAQ:

    http://www.parashift.com/c++-faq-lit...html#faq-35.20

    Regards,

    Paul McKenzie

  4. #4
    Join Date
    Jun 2006
    Posts
    42

    Re: Odd behaviour in overloaded std::stringstream

    Thank you for your responses.

    The fact that std::ostringstream is not copyable is not a problem, because
    a) I do not require the copy of stupidostream to be a proper copy of the original because it is empty at the time of the copy, so I can use the default constructor anyway. In the actual code I will "assert" that, but for the sake of simplicity I left it out.
    b) the copy should usually be optimised away by the compiler in the call to getStream.

    However, the dependent/non-dependent thing sounds reasonable, but I'm not sure I get it.
    basic_ostringstream<> itself derives from basic_ostream<> and if we call operator<< on a ostringstream, the correct function from basic_ostream is called. Why does it work there?

  5. #5
    Join Date
    Aug 2005
    Location
    LI, NY
    Posts
    576

    Re: Odd behaviour in overloaded std::stringstream

    Quote Originally Posted by Paul McKenzie
    You're deriving from a template class. Once you do that, some members of the base class (determined by dependent/non-dependent name rules) are not available to you unless you specifically call them. Therefore, your derived class must explicitly call the ostringstream operator <<, and not rely that the compiler will call it for you.
    I don't see any unqualified calls to base class methods within the derived class. Anyway, there wouldn't be a problem because the type of the base class is not dependent on a template parameter of the derived class.
    - Alon

  6. #6
    Join Date
    Apr 1999
    Posts
    27,449

    Re: Odd behaviour in overloaded std::stringstream

    Quote Originally Posted by Tannin
    The fact that std::ostringstream is not copyable is not a problem,
    It is. There is no guarantee that the code will even compile, therefore the program is ill-formed and the behaviour is undefined. The compiler by chance just happens to let you get away with it past the compilation stage, but nothing stops another compiler from rejecting your code outright with errors stating that streams are not copyable.
    a) I do not require the copy of stupidostream to be a proper copy of the original because it is empty at the time of the copy, so I can use the default constructor anyway. In the actual code I will "assert" that, but for the sake of simplicity I left it out.
    b) the copy should usually be optimised away by the compiler in the call to getStream.
    Once you try to justify usage of undefined behaviour, you're starting out on the wrong foot. Your code may not even compile on another compiler, and you are relying on specific behaviour of a specific compiler, which could change on the next version of the compiler.

    Bottom line -- streams are not copyable, as there is no standard copy constructor or operator = for streams (i.e. you won't find these functions defined or explained in any C++ reference manual concerning standard stream classes).

    What does it mean to "copy a stream"? This is why it isn't defined by the standard. Therefore you don't know what you're getting when you copy a stream, or even if the compiler will allow it.
    However, the dependent/non-dependent thing sounds reasonable, but I'm not sure I get it.
    basic_ostringstream<> itself derives from basic_ostream<> and if we call operator<< on a ostringstream, the correct function from basic_ostream is called. Why does it work there?
    I speculated that this could be the cause, but now that copying streams leads to undefined behaviour, why it does what it does is a moot point.

    Regards,

    Paul McKenzie
    Last edited by Paul McKenzie; May 18th, 2008 at 10:39 AM.

  7. #7
    Join Date
    Aug 2005
    Location
    LI, NY
    Posts
    576

    Re: Odd behaviour in overloaded std::stringstream

    Quote Originally Posted by Paul McKenzie
    It is. There is no guarantee that the code will even compile, therefore the program is ill-formed and the behaviour is undefined. The compiler by chance just happens to let you get away with it past the compilation stage, but nothing stops another compiler from rejecting your code outright with errors stating that streams are not copyable.
    Once you try to justify usage of undefined behaviour, you're starting out on the wrong foot. Your code may not even compile on another compiler, and you are relying on specific behaviour of a specific compiler, which could change on the next version of the compiler.

    Bottom line -- streams are not copyable, as there is no standard copy constructor or operator = for streams (i.e. you won't find these functions defined or explained in any C++ reference manual concerning standard stream classes).

    What does it mean to "copy a stream"? This is why it isn't defined by the standard. Therefore you don't know what you're getting when you copy a stream, or even if the compiler will allow it.
    The code however is not invoking the copy constructor or assignment operator of any standard stream. Could problems arise from the fact that standard library implementors can assume that nothing derived from ios_base (standard or not) is copyable?

    Here's the same problem without copying:
    Code:
    #include <iostream>
    #include <sstream>
    
    class stupidostream : public std::ostringstream {
    public:
            virtual ~stupidostream() { std::cout << "\"" << this->str() << "\"" << std::endl; }
    };
    
    
    int main()
    {
    	stupidostream() << "hello" << "world";
    }
    Does this have to do with some weird rule about temporaries bound to references, or overload resolution, or some other part of the standard I haven't the foggiest about?
    - Alon

  8. #8
    Join Date
    Apr 1999
    Posts
    27,449

    Re: Odd behaviour in overloaded std::stringstream

    Quote Originally Posted by Hermit
    Here's the same problem without copying:
    Code:
    #include <iostream>
    #include <sstream>
    
    class stupidostream : public std::ostringstream {
    public:
            virtual ~stupidostream() { std::cout << "\"" << this->str() << "\"" << std::endl; }
    };
    
    int main()
    {
    	stupidostream() << "hello" << "world";
    }
    After debugging this (Visual Studio 2003), there is a standalone function that takes a std::ostringstream and char * as the arguments. This is what's called when using ostringstream straight up (when invoking the "hello").

    When using stupidostream(), there is an overloaded << that takes a void pointer (there is no char *pointer version of the overload).

    So if I were to reason this out, in the case of std::ostringstream, the compiler finds the standalone function as a better match (causing the correct output to occur, i.e. "hello world").

    When using stupidostream(), the best match is the member basic_ostream::operator << (void *p), which causes the hex to print.
    Try the following program:
    Code:
    #include <iostream>
    #include <sstream>
    
    class stupidostream : public std::ostringstream {
    public:
        virtual ~stupidostream() { std::cout << "\"" << this->str() << "\"" << std::endl; }
    };
    
    int main()
    {
        stupidostream() << "hello\n";;
        std::ostringstream s;
        s << "hello";;
        std::cout << s.str();
    }
    Regards,

    Paul McKenzie

  9. #9
    Join Date
    Aug 2005
    Location
    LI, NY
    Posts
    576

    Re: Odd behaviour in overloaded std::stringstream

    Quote Originally Posted by Paul McKenzie
    So if I were to reason this out, in the case of std::ostringstream, the compiler finds the standalone function as a better match (causing the correct output to occur, i.e. "hello world").

    When using stupidostream(), the best match is the member basic_ostream::operator << (void *p), which causes the hex to print.
    I suspected it had something to do with this.

    So I think I now understand the exact cause. The standalone operator<< takes a non-const reference for the left operand. Because of this, it is not a suitable match if the left operand is a temporary, and basic_ostream::operator << (void *p) is the next best thing as far as the compiler is concerned.

    Why do I get the feeling I'm more interested in this than the OP? :)
    - Alon

  10. #10
    Join Date
    Jun 2006
    Posts
    42

    Re: Odd behaviour in overloaded std::stringstream

    Nooo, sorry I didn't post earlier, but I didn't want to post before I compare what Paul posted with what linux does:

    disassembling the code g++ produces I can verify that it calls
    Code:
    std::basic_ostream<char, std::char_traits<char> >::operator<<(void const*)
    first for "hello" and then
    Code:
    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
    for "world"

    This makes sense, temporaries are always const according to this: http://www.codeguru.com/cpp/tic/tic0094.shtml (search for Temporaries).

    That means, as far as I understand, although my code would be fine IF the temporary was not const, the standard doesn't allow it. :(

    The only question I have left: Why can I even call "std::basic_ostream<char, std::char_traits<char> >::operator<<(void const*)"? The function is not const (its writing to the stream after all) so why is the method a valid match?
    Why doesn't the compiler just say: "Idiot, you can't shift into a const!"? Instead it turns the stream into non-const?

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured