I am mystified... need help with STL.

B

Ben Thomas

Hi all,

I'm having some trouble understanding the behavior of std::eek:stringstream.
(I'm using Visual Studio .Net & STL port 4.5.3). I'll appreciate if someone
can give me a little explanation of this behavior and how it is possible...

Here's my code

//////////////////////////
#include <stdio.h>
#include <ostream>

void func( const char* s )
{
printf( "func: [%u], [%s]\n", s, s );
}

void main( void )
{
std::eek:stringstream stream;
stream << "Hello World";
const char* s = stream.str().c_str();
printf( "main: [%u], [%s]\n", s, s );
func( stream.str().c_str() );
}
////////////////////////////////

And here's my result:

main: [3290280], []
func: [3290280], [Hello World]

Thankx a lot!
Ben Thomas.
 
K

Kevin Goodsell

Ben said:
Hi all,

I'm having some trouble understanding the behavior of std::eek:stringstream.
(I'm using Visual Studio .Net & STL port 4.5.3). I'll appreciate if someone
can give me a little explanation of this behavior and how it is possible...

Here's my code

//////////////////////////
#include <stdio.h>
#include <ostream>

What about said:
void func( const char* s )
{
printf( "func: [%u], [%s]\n", s, s );

This invokes undefined behavior. The %u format specifier is for unsigned
ints only. You are passing it a const char *. This is a good example of
why you should not use printf or any function or language construct that
defeats type-checking.
}

void main( void )

main returns int. void is not and never has been an acceptable return
type for main.
{
std::eek:stringstream stream;
stream << "Hello World";
const char* s = stream.str().c_str();

My best guess is that ostringstream::str() returns by value (strangely,
I can't find this information in either Stroustrup or Josuttis). That
means you get a temporary std::string, which you then invoke c_str() on.
However, the temporary goes out of scope after this statement completes,
and the pointer returned from c_str() is no longer valid. Try this instead:

std::string tmp = stream.str();
const char* s = tmp.c_str();
printf( "main: [%u], [%s]\n", s, s );

Undefined behavior again.
func( stream.str().c_str() );

In this case, the same thing happens except that the temporary does not
go out of scope until the function completes.

-Kevin
 
R

Ron Natalie

Kevin Goodsell said:
printf( "func: [%u], [%s]\n", s, s );

This invokes undefined behavior. The %u format specifier is for unsigned
ints only. You are passing it a const char *. This is a good example of
why you should not use printf or any function or language construct that
defeats type-checking.

Yes, if he wants to inspect the pointer, the way to do it is:
printf("func [%p],[%s]\n", (void*)s, s);
 
U

Unforgiven

Ron said:
Kevin Goodsell said:
printf( "func: [%u], [%s]\n", s, s );

This invokes undefined behavior. The %u format specifier is for
unsigned ints only. You are passing it a const char *. This is a
good example of why you should not use printf or any function or
language construct that defeats type-checking.

Yes, if he wants to inspect the pointer, the way to do it is:
printf("func [%p],[%s]\n", (void*)s, s);

To be a complete nitpicker, the C++ way to do it is:
std::cout << "main: [" << static_cast<const void*>(s) << "], [" << s <<
"]" << std::endl;

Or even more nitpicky, we should forget char* altogether, std::string should
be good enough for us C++ types:
std::cout << "main: [" << stream.str() << "]" << std::endl;

^_^
 
B

Ben Thomas

Thankx for the answer!

Okay, I have fixed the problem with displaying the pointers value and change
#include<ostream> with #include<sstream> and return 0 in the main function
and it's the same behavior! :)

The solution of a temp variable works! I too first though that it was a
scope problem, that's why I displayed the pointer value. Yet, they are the
same, so scope seems to be okay, only that it's empty in one call and not in
the other. Also, I don't know if there's a difference in scope between the
two kind of call... I have tryed to write a class that reproduce the same
kind of behavior but wasn't able to do so... maybe it's in the many level of
template with STL, but how can you make your class act differently between
this

const char* s = stream.str().c_str();

and this

func( stream.str().c_str() ); // void func( const char* s );

I just can't make sense of it... maybe the problem is elsewhere after all.
Ben Thomas.

Ron Natalie said:
printf( "func: [%u], [%s]\n", s, s );

This invokes undefined behavior. The %u format specifier is for unsigned
ints only. You are passing it a const char *. This is a good example of
why you should not use printf or any function or language construct that
defeats type-checking.

Yes, if he wants to inspect the pointer, the way to do it is:
printf("func [%p],[%s]\n", (void*)s, s);
 
K

Kevin Goodsell

Ben said:
Thankx for the answer!

Please don't top-post. Re-read section 5 of the FAQ for posting guidelines.

http://www.parashift.com/c++-faq-lite/
Okay, I have fixed the problem with displaying the pointers value and change
#include<ostream> with #include<sstream> and return 0 in the main function
and it's the same behavior! :)

The solution of a temp variable works! I too first though that it was a
scope problem, that's why I displayed the pointer value. Yet, they are the
same,

This proves absolutely nothing.
so scope seems to be okay,

Bad conclusion.
only that it's empty in one call and not in
the other.

It's undefined in one call, and not in the other.
Also, I don't know if there's a difference in scope between the
two kind of call... I have tryed to write a class that reproduce the same
kind of behavior but wasn't able to do so... maybe it's in the many level of
template with STL, but how can you make your class act differently between
this

const char* s = stream.str().c_str();

OK, I'll try to explain again (but I suspect you need to look up
"temporaries" or "temporary objects" in your C++ book).

stream.str() returns by value. This means that the result is copied into
a "temporary object". Now, the ruled for temporary objects state that
they go out of scope at the end of the full expression in which they are
created. A full expression is an expression that is not a sub-expression
of any larger expression, therefore the full expression in this
particular case is the entire statement, minus the semi-colon. That
means that the temporary std::string object containing the result of
stream::str() is GONE once this statement completes. It no longer exists.

Since the pointer returned from std::string::c_str() ceases to be valid
when the std::string is modified or destroyed, your 's' pointer stops
being valid as soon as you initialize it.
and this

func( stream.str().c_str() ); // void func( const char* s );

In this case, the full expression includes the function call. The
function call uses the pointer while it's still valid. As soon as the
function completes, the temporary std::string object goes out of scope
and the pointer is no longer valid - but that's OK this time because you
haven't saved it anywhere.
I just can't make sense of it... maybe the problem is elsewhere after all.

I'm quite sure this IS the problem. I've verified my assumption about
the return type of std::eek:stringstream::str() by checking the standard.
What you are doing is roughly equivalent to this:

#include <iostream>
#include <cstring>

using namespace std;

class Stupid
{
char *str;
public:
Stupid() : str(new char[20]) { strcpy(str, "this is stupid");
cout << "Constructing a Stupid object" << endl; }
~Stupid() { delete [] str;
cout << "Destroying a Stupid object"
" (and releasing memory)" << endl; }
char *GetPtr() { return str; }
};

int main()
{
char *s = Stupid().GetPtr();

cout << "The string pointed to by s is:\n" << s << endl;

return 0;
}

If you can't see why this is horribly broken, try running it. The object
dies and releases its memory before you even have a chance to use 's'.

-Kevin
 
P

Peter Kragh

So, Ben, if you really want your code to work, just make a few adjustments:

#include <stdio.h>
#include <sstream>
#include <string>

void func( const char* s )
{
printf( "func: [%p], [%s]\n", s, s );
}

void main( void )
{
std::eek:stringstream stream;
stream << "Hello World";
std::string s = stream.str();
printf( "main: [%p], [%s]\n", s.c_str(), s.c_str() );
func( stream.str().c_str() );
}

That way, you *copy* the temporary object to a new (non-temporary) one, and
it's valid when you call your func function.

Maybe this should help you understand std::eek:stringstream a little better :)

BR,
Peter Kragh
 
K

Kevin Goodsell

Peter said:
So, Ben, if you really want your code to work, just make a few adjustments:

#include <stdio.h>
Deprecated.

#include <sstream>
#include <string>

void func( const char* s )
{
printf( "func: [%p], [%s]\n", s, s );

This is an error, as has already been discussed in this thread. %p only
accepts void*.
}

void main( void )

main must return int. This is required by the C++ standard.
{
std::eek:stringstream stream;
stream << "Hello World";
std::string s = stream.str();
printf( "main: [%p], [%s]\n", s.c_str(), s.c_str() );

Same error.
func( stream.str().c_str() );
}

That way, you *copy* the temporary object to a new (non-temporary) one, and
it's valid when you call your func function.

Maybe this should help you understand std::eek:stringstream a little better :)

-Kevin
 

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

Forum statistics

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

Latest Threads

Top