Connecting Tech Pros Worldwide Forums | Help | Site Map

ostream,stringstream and char*

Protazy
Guest
 
Posts: n/a
#1: Sep 1 '06
Dear All,
I have the following piece of code:

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

//And this prints some numbers (address of the pointer...)
f(std::ostringstream() << "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::ostringstream() << test);

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

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

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

//and again, it works this way
std::ostringstream 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


Jens Theisen
Guest
 
Posts: n/a
#2: Sep 1 '06

re: ostream,stringstream and char*


Protazy wrote:
Quote:
void f(const std::ostream &str)
{
std::ostringstream *pStr = (std::ostringstream*)(&str);
std::cout << pStr->str();
}
You probably know that you shouldn't downcast and cast away constness
without good reason.
Quote:
//And this prints some numbers (address of the pointer...)
f(std::ostringstream() << "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::ostringstream. 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
Protazy
Guest
 
Posts: n/a
#3: Sep 1 '06

re: ostream,stringstream and char*



Jens Theisen wrote:
Quote:
Protazy wrote:
Quote:
void f(const std::ostream &str)
{
std::ostringstream *pStr = (std::ostringstream*)(&str);
std::cout << pStr->str();
}
>
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.
Quote:
>
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::ostringstream. 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

Jens Theisen
Guest
 
Posts: n/a
#4: Sep 2 '06

re: ostream,stringstream and char*


Protazy wrote:
Quote:
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
Protazy
Guest
 
Posts: n/a
#5: Sep 2 '06

re: ostream,stringstream and char*



Jens Theisen wrote:
Quote:
Protazy wrote:
Quote:
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?
>
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::ostringstream() << 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::ostringstream()?

Quote:
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

Jens Theisen
Guest
 
Posts: n/a
#6: Sep 2 '06

re: ostream,stringstream and char*


Protazy wrote:
Quote:
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).
Quote:
What worries me is the bahaviour of:
f(std::ostringstream() << 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::ostringstream()?
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::ostringstream() 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.
Quote:
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::ostringstream 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).
Quote:
The variable should last long enough to be passed to the function. Or am I
missing something here?
Yes, it lasts long enough.

Jens
Protazy
Guest
 
Posts: n/a
#7: Sep 2 '06

re: ostream,stringstream and char*



Jens Theisen wrote:
Quote:
>
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;-)
Quote:
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.
In the final version I wanted to do a dynamic_cast<and check the return
value, so it would be safer.
Quote:
>
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

Closed Thread