ostringstream output to C-string

Discussion in 'C++' started by Fab, Mar 24, 2008.

  1. Fab

    Fab Guest

    Hi C++ experts,
    I'm using std::eek:stringstream in my program to format a string. In my
    unit tests I use it quite extensively to generate expected results
    that I then compare with a predefined string.

    The problem is that this code does something wrong:

    std::eek:stringstream os;
    os << "Hello world";
    const char * const produced = os.str().c_str();
    if (strcmp(produced, "Hello world") == 0) {...}

    In particular, the 'produced' buffer is invalid.
    On Linux and Mac OS X the program run and any access to 'produced'
    buffer, does not report any error, but if you run a test program with
    valgrind, Guard Malloc or any other memory checker tools, all the
    tools report some problems.

    The workaround to this problem is to generate a temporary variable to
    store a reference to the the std::string produced by os.str(). In this
    case everything works as expected:
    std::eek:stringstream os;
    os << "Hello world";
    const std::string & tempString(os.str());
    const char * const produced = os.str().c_str();
    if (strcmp(produced, "Hello world") == 0) {...}

    Is it because os.str().c_str() generates a temporary std::string to
    store the produced string, and c_str() returns a pointer to this temp
    object? Shouldn't temp object get disposed when exiting the scope?
    Does it mean that this temp string's scope is only the 'produced'
    assignment and after that is no longer valid?

    What's your opinion on that?
    F.
    Fab, Mar 24, 2008
    #1
    1. Advertising

  2. Fab wrote:
    > I'm using std::eek:stringstream in my program to format a string. In my
    > unit tests I use it quite extensively to generate expected results
    > that I then compare with a predefined string.
    >
    > The problem is that this code does something wrong:
    >
    > std::eek:stringstream os;
    > os << "Hello world";
    > const char * const produced = os.str().c_str();
    > if (strcmp(produced, "Hello world") == 0) {...}
    >
    > In particular, the 'produced' buffer is invalid.
    > On Linux and Mac OS X the program run and any access to 'produced'
    > buffer, does not report any error, but if you run a test program with
    > valgrind, Guard Malloc or any other memory checker tools, all the
    > tools report some problems.
    >
    > The workaround to this problem is to generate a temporary variable to
    > store a reference to the the std::string produced by os.str(). In this
    > case everything works as expected:
    > std::eek:stringstream os;
    > os << "Hello world";
    > const std::string & tempString(os.str());
    > const char * const produced = os.str().c_str();
    > if (strcmp(produced, "Hello world") == 0) {...}


    This is nothing different from the other code, from the language
    point of view. The expression that used to initialise 'produced'
    can still create another temporary, which is going to be destroyed
    at the end of initialising 'produced', thus making 'procuded'
    an invalid pointer. You just get lucky here and not the first
    time around.

    What you want to do, perhaps is

    const char* const procuded = tempString.c_str();

    Then you're using the actual temporary object that 'tempString'
    is bound to, and which will survive as long as 'tempString' does.

    > Is it because os.str().c_str() generates a temporary std::string to
    > store the produced string, and c_str() returns a pointer to this temp
    > object?


    Yes, pretty much.

    > Shouldn't temp object get disposed when exiting the scope?


    Not sure how to answer this question (about the scope) because I
    don't understand what you're asking. The temp object created in
    the expression used to evaluate 'produced' survives only until
    the 'produced' gets fully initialised.

    > Does it mean that this temp string's scope is only the 'produced'
    > assignment and after that is no longer valid?


    It's not assignment, it's initialisation. And, yes, it's destroyed
    right after 'produced' is initialised.

    > What's your opinion on that?


    No opinion. It's all according to the written rules.

    V
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Mar 24, 2008
    #2
    1. Advertising

  3. Fab schrieb:
    > Hi C++ experts,
    > I'm using std::eek:stringstream in my program to format a string. In my
    > unit tests I use it quite extensively to generate expected results
    > that I then compare with a predefined string.
    >
    > The problem is that this code does something wrong:
    >
    > std::eek:stringstream os;
    > os << "Hello world";
    > const char * const produced = os.str().c_str();
    > if (strcmp(produced, "Hello world") == 0) {...}
    >
    > In particular, the 'produced' buffer is invalid.
    > On Linux and Mac OS X the program run and any access to 'produced'
    > buffer, does not report any error, but if you run a test program with
    > valgrind, Guard Malloc or any other memory checker tools, all the
    > tools report some problems.
    >
    > The workaround to this problem is to generate a temporary variable to
    > store a reference to the the std::string produced by os.str(). In this
    > case everything works as expected:
    > std::eek:stringstream os;
    > os << "Hello world";
    > const std::string & tempString(os.str());
    > const char * const produced = os.str().c_str();
    > if (strcmp(produced, "Hello world") == 0) {...}
    >
    > Is it because os.str().c_str() generates a temporary std::string to
    > store the produced string, and c_str() returns a pointer to this temp
    > object? Shouldn't temp object get disposed when exiting the scope?


    Temporary objects are destroyed at the end of the statement. So your
    'produced' variable is initialized, then the temporary string is destroyed,
    and the pointer to the buffer containing the string becomes invalid.

    > What's your opinion on that?


    Don't use c_str(), compare the string directly:

    if (os.str() == "Hello world") { /*...*/ }

    --
    Thomas
    http://www.netmeister.org/news/learn2quote.html
    ++++++++++[>+++++<-]>++.--.
    Thomas J. Gritzan, Mar 24, 2008
    #3
  4. Fab

    Fab Guest

    Well, thanks to all for the answer... and yes, as Victor pointed out,
    my example was wrong (I managed to copy the wrong part of the code,
    and the C-string assignment is supposed to use the 'tempString'
    std::string.
    Ok, so, temporary objects are destroyed after the initialization.
    But in this code:
    const std::string & tempString(os.str());
    tempString is a reference to a temp object too, right? Why now the
    temp object is still valid? Does it mean the compiler do not destroy
    it because it knows there is still one reference to it? Or simply
    'tempString' *IS* the temp object?

    So, in these two cases:
    const std::string & tempString(os.str());
    and
    const std::string tempString(os.str());

    the second tempString is a copy of the temp object (that perhaps share
    the same buffer if std::string uses reference counting), and the first
    is the temp object itself?

    Fab

    On Mar 24, 11:40 am, "Thomas J. Gritzan" <>
    wrote:
    > Fab schrieb:
    >
    >
    >
    > > Hi C++ experts,
    > > I'm using std::eek:stringstream in my program to format a string. In my
    > > unit tests I use it quite extensively to generate expected results
    > > that I then compare with a predefined string.

    >
    > > The problem is that this code does something wrong:

    >
    > >     std::eek:stringstream os;
    > >     os << "Hello world";
    > >     const char * const produced = os.str().c_str();
    > >     if (strcmp(produced, "Hello world") == 0) {...}

    >
    > > In particular, the 'produced' buffer is invalid.
    > > On Linux and Mac OS X the program run and any access to 'produced'
    > > buffer, does not report any error, but if you run a test program with
    > > valgrind, Guard Malloc or any other memory checker tools, all the
    > > tools report some problems.

    >
    > > The workaround to this problem is to generate a temporary variable to
    > > store a reference to the the std::string produced by os.str(). In this
    > > case everything works as expected:
    > >     std::eek:stringstream os;
    > >     os << "Hello world";
    > >     const std::string & tempString(os.str());
    > >     const char * const produced = os.str().c_str();
    > >     if (strcmp(produced, "Hello world") == 0) {...}

    >
    > > Is it because os.str().c_str() generates a temporary std::string to
    > > store the produced string, and c_str() returns a pointer to this temp
    > > object? Shouldn't temp object get disposed when exiting the scope?

    >
    > Temporary objects are destroyed at the end of the statement. So your
    > 'produced' variable is initialized, then the temporary string is destroyed,
    > and the pointer to the buffer containing the string becomes invalid.
    >
    > > What's your opinion on that?

    >
    > Don't use c_str(), compare the string directly:
    >
    > if (os.str() == "Hello world") { /*...*/ }
    >
    > --
    > Thomashttp://www.netmeister.org/news/learn2quote.html
    > ++++++++++[>+++++<-]>++.--.
    Fab, Mar 24, 2008
    #4
  5. Fab wrote:
    > Well, thanks to all for the answer... and yes, as Victor pointed out,
    > my example was wrong (I managed to copy the wrong part of the code,
    > and the C-string assignment is supposed to use the 'tempString'
    > std::string.
    > Ok, so, temporary objects are destroyed after the initialization.
    > But in this code:
    > const std::string & tempString(os.str());
    > tempString is a reference to a temp object too, right? Why now the
    > temp object is still valid? Does it mean the compiler do not destroy
    > it because it knows there is still one reference to it? Or simply
    > 'tempString' *IS* the temp object?


    It's an exception to the rule.
    The C++ standard says, that a temporary object, that is bound to a
    reference, is not destroyed as long as the reference is in scope.

    So by binding the temporary object to the reference, you coupled the
    lifetime of both.

    > So, in these two cases:
    > const std::string & tempString(os.str());
    > and
    > const std::string tempString(os.str());
    >
    > the second tempString is a copy of the temp object (that perhaps share
    > the same buffer if std::string uses reference counting), and the first
    > is the temp object itself?


    Yes, the second tempString is initilized (copy constructed) from the
    temporary string returned by os.str(), unless the compiler uses RVO (return
    value optimization) to remove this copy.

    >

    [fullquote removed]

    Please don't toppost, and please don't quote signatures. Read the link in
    my signature to learn how to quote.

    --
    Thomas
    http://www.netmeister.org/news/learn2quote.html
    There are no bugs, and they have been fixed!
    Thomas J. Gritzan, Mar 24, 2008
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Victor Bazarov
    Replies:
    0
    Views:
    830
    Victor Bazarov
    Jun 25, 2003
  2. Thomas Lenz
    Replies:
    7
    Views:
    371
    Jim Langston
    Oct 23, 2007
  3. Bala2508
    Replies:
    28
    Views:
    1,138
    Jim Langston
    Nov 3, 2007
  4. Replies:
    2
    Views:
    398
    James Kanze
    Jan 17, 2008
  5. Replies:
    12
    Views:
    970
    Jerry Coffin
    Jun 2, 2008
Loading...

Share This Page