ostringstream output to C-string

F

Fab

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.
 
V

Victor Bazarov

Fab said:
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
 
T

Thomas J. Gritzan

Fab said:
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") { /*...*/ }
 
F

Fab

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
 
T

Thomas J. Gritzan

Fab said:
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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top