ostream,stringstream and char*

P

Protazy

Dear All,
I have the following piece of code:

---------------------------------------------
void f(const std::eek:stream &str)
{
std::eek:stringstream *pStr = (std::eek:stringstream*)(&str);
std::cout << pStr->str();
}
int main(int argc, char** argv)
{
//This works as expected
std::eek:stringstream str;
f(str << "Working example.\n"); //(1)

//And this prints some numbers (address of the pointer...)
f(std::eek:stringstream() << "This is not working\n"); //(2)

std::cout << std::endl;//new line

const char* const test = "TestVariable";

//this is the address of test:
printf("The address is: %p\n",test);

//and here we have the address again
f(std::eek:stringstream() << test);

std::cout << std::endl;//new line

//the address of test AND "some text" (?)
f(std::eek:stringstream() << test << "some text");//(3)

std::cout << std::endl;//new line

//and again, it works this way
std::eek:stringstream str2;
f(str2 << test);
}
-----------------------------------------------------

This is the output:
************************
Working example. <---(1)
0x446012 <---(2)
The address is: 0x446027
0x446027
0x446027some text <---(3)
TestVariable
************************

Can someone give me explanation why it is working for (1), and it is not
working for (2)?
Correct me if I'm wrong, but I thought that the bahaviour here would be
identical...
Also, the situation in (3) is strange for me.

I used cygwin (gcc 3.4.4) and Visual C++ 2003, for both compilers it looks
similar.

Thank you in advance for any help!

Best regards,
Protazy
 
J

Jens Theisen

Protazy said:
void f(const std::eek:stream &str)
{
std::eek:stringstream *pStr = (std::eek:stringstream*)(&str);
std::cout << pStr->str();
}

You probably know that you shouldn't downcast and cast away constness
without good reason.
//And this prints some numbers (address of the pointer...)
f(std::eek:stringstream() << "This is not working\n"); //(2)

It boils down to the following test code:

#include <iostream>

using namespace std;

struct x
{
friend void operator << (x & lhs, char const* c)
{
cout << "friend" << endl;
}

void operator << (const void * p)
{
cout << "member" << endl;
}
};

int main()
{
x x_;
x_ << "x";
x() << "x";
}

prints:

friend
member

The reason being that (though I'm not sure where that's specified in the
standard, someone who knows might be so nice to point it out) non-const
member functions bind to temporaries, as your temporary
std::eek:stringstream. Free functions, such as my friend, which take
non-const references don't, so overload resolution in this case takes an
unexpected function. The standard specifies which operators should be
defined as members and which should be free, so it should be the same
behaviour on any platform.

Jens
 
P

Protazy

Jens said:
You probably know that you shouldn't downcast and cast away constness
without good reason.

You're right, my fault. Thanks for pointing that out.
The reason being that (though I'm not sure where that's specified in the
standard, someone who knows might be so nice to point it out) non-const
member functions bind to temporaries, as your temporary
std::eek:stringstream. Free functions, such as my friend, which take
non-const references don't, so overload resolution in this case takes an
unexpected function.

I think that - thanks to your explanations - I got it now. The ostringstream
does not have "<<" member operator for char*, but it has an operator for
void*, which is used here. And that's the reason for such behaviour. Tricky;>

Many thanks, I learned something valuable today.

Regards,
Protazy
 
J

Jens Theisen

Protazy said:
The ostringstream
does not have "<<" member operator for char*, but it has an operator for
void*, which is used here.

Oh, it does have! In your first example,

o << "Hello";

did the right thing, didn't it?

It's just that it's not found when you use it on a _temporary_. But in
real code you really don't want to use a temporary ostringstream in the
first place.

Jens
 
P

Protazy

Jens said:
Oh, it does have! In your first example,

o << "Hello";

did the right thing, didn't it?

From what I understood, it is not a member operator used here. It's a friend
function. Right?
What worries me is the bahaviour of:
f(std::eek:stringstream() << test << "some text");
In the first "<<" the member operator is used, but for second "<<" friend
function is invoked. Is the variable produced by first "<<" "less temporary"
then the variable produced by std::eek:stringstream()?

It's just that it's not found when you use it on a _temporary_. But in
real code you really don't want to use a temporary ostringstream in the
first place.

Why? Is it dangerous? I only need it to extract string from it. I want to do
something like:
f(std::stringstrem() << "The value is " << 2 << " and some object is " <<
someobject);
The variable should last long enough to be passed to the function. Or am I
missing something here?

Thanks and regards,
Protazy
 
J

Jens Theisen

Protazy said:
From what I understood, it is not a member operator used here. It's a friend
function. Right?

Sorry, I overlooked the "member". Yes it's a friend (or a non-friend
free function, depending on the implementation).
What worries me is the bahaviour of:
f(std::eek:stringstream() << test << "some text");
In the first "<<" the member operator is used, but for second "<<" friend
function is invoked. Is the variable produced by first "<<" "less temporary"
then the variable produced by std::eek:stringstream()?

Yes, it's less temporary. :)

The return value of operator << in this context is ostream &, which is a
non-const reference. In particular, it's an lvalue expression, where
std::eek:stringstream() is a temporary, ie. an rvalue expression. And
though what's returned by the first << operator expression is still the
same temporary, the expression is now an lvalue expression. lvalues bind
to non-const references, rvalues don't.

In this case, it seems weird and arbirtary, but usually it's what you
want since it's usually a programming error to pass an object by
reference (non-const, to modify it), if it's going to be destroyed after
the end of the expression. The essence of what happened in your example
is that it got explicitly casted to an lvalue.
Why? Is it dangerous? I only need it to extract string from it. I want to do
something like:
f(std::stringstrem() << "The value is " << 2 << " and some object is " <<
someobject);

Operator << on ostringstreams returns ostring&, so you can't call str on
it, that's why you made your f take an ostream and cast it down. While
legal, I don't really like it: The function is lying about what's it's
really accepting in it's signature, and somone who doesn't realise the
hack might put another kind of ostream in it at some point; which will
probably crash the program.

A better way is wrapping it into your own helper stream class:

struct make_string
{
template< typename T >
make_string & operator <<(T const& t)
{
m_s << t;
return *this;
}

operator std::string ()
{
return m_s.str();
}

private:
std::eek:stringstream m_s;
};

And then use it like

void foo(std::string const& str)
{
std::cout << str;
}

int main()
{
foo(make_string() << "Hello " << 42);
}

There is a more sophisticated class like this in boost, supporting
expressions like format("this: %1% and that: %2%") % these % those
(maybe the syntax is slightly different).
The variable should last long enough to be passed to the function. Or am I
missing something here?

Yes, it lasts long enough.

Jens
 
P

Protazy

Jens said:
Yes, it's less temporary. :)

The return value of operator << in this context is ostream &, which is

Thanks for the explanations. And yes, it seems weird;-)
Operator << on ostringstreams returns ostring&, so you can't call str on
it, that's why you made your f take an ostream and cast it down. While
legal, I don't really like it: The function is lying about what's it's
really accepting in it's signature, and somone who doesn't realise the
hack might put another kind of ostream in it at some point; which will
probably crash the program.


A better way is wrapping it into your own helper stream class:

I also thought about it, I just wanted to know the reason for "strange"
ostringstream bahaviour. And now, thanks to you, I know:)

Many thanks again!

Best regards,
Protazy
 

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,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top