ostringstream derived problem

I

iu2

Hi all, can someone help me with this?

I'm trying to shorthen some logging function in our project, i.e.,
instead of

LogString("...);

use a more convenient

log() << "String here " << 12.33 << " and a number";

I create this syntax like this:

struct log : public ostringstream {
virtual ~log() { cout << "The log contains: " << str().c_str() <<
endl;}
};

The trick I'm trying to do is that log() creates an object of type
log, then it fills it using the ostringstream's parent '<<' and right
after the statement finishes the destructor is called and the message
is printed onto the screen.
The problem is that it doesn't:
It prints something like "The log contains: " 4129000
But if the first element after "log() << " is not a string, e.g., a
number, dec, hex or whatever, the scheme succeeds.

Why does this happen? I've got a feeling that the destructor might be
called before the first "<< " but I don't understand why.

Thanks
 
J

John Harrison

iu2 said:
Hi all, can someone help me with this?

I'm trying to shorthen some logging function in our project, i.e.,
instead of

LogString("...);

use a more convenient

log() << "String here " << 12.33 << " and a number";

I create this syntax like this:

struct log : public ostringstream {
virtual ~log() { cout << "The log contains: " << str().c_str() <<
endl;}
};

The trick I'm trying to do is that log() creates an object of type
log, then it fills it using the ostringstream's parent '<<' and right
after the statement finishes the destructor is called and the message
is printed onto the screen.
The problem is that it doesn't:
It prints something like "The log contains: " 4129000
But if the first element after "log() << " is not a string, e.g., a
number, dec, hex or whatever, the scheme succeeds.

Why does this happen? I've got a feeling that the destructor might be
called before the first "<< " but I don't understand why.

Thanks

Very subtle. I can't tell you the answer, although I can give you a few
pointers.

Firstly it's nothing to do with destructors. Destructors for temporaries
are called at the end of the containing expression.

The number you are seeing is the address of the string "String here ".
Remember that there are several sersions of operator<< some of them are
member functions and some are global functions. The one you want to be
called is this one, which prints a string

ostream& ostream::eek:perator<<(const char*)

the one that is being called is this one, which prints a pointer address

ostream& operator<<(ostream&, const void*)

In other words your expression is being interpretted like this

operator<<(log(), "String here ") << 12.33 << " and a number";

when you would prefer to have it intepreted like this

log().operator<<("String here ") << 12.33 << " and a number";

Both these expression are legal, and if you write either one you get the
expected output (pointer in the first case, string in the second
case). Also if you write this


log myLog;
myLog << "String here " << 12.33 << " and a number";

you get the desired string output.

So the question is why does having a temporary of a derived class on the
left hand side of operator<< make the compiler ignore member function of
the base class?

I have no idea what the answer is. I gave up on this kind of question a
long time ago. A wise man said (I think it was Bjarne Stroustrup) 'stay
away from the obscure corners of the language'. That would be my advice.

I think it's perfectly acceptable to use macros for logging.

#define LOG(X) std::cout << "The log contains: " << X << std::endl

LOG("String here " << 12.33 << " and a number")

That would also be my advice.

john
 
J

John Harrison

I think it's perfectly acceptable to use macros for logging.

#define LOG(X) std::cout << "The log contains: " << X << std::endl

LOG("String here " << 12.33 << " and a number")

Small tpyo, missing semi-colon

LOG("String here " << 12.33 << " and a number");

john
 
K

Kai-Uwe Bux

iu2 said:
Hi all, can someone help me with this?

I'm trying to shorthen some logging function in our project, i.e.,
instead of

LogString("...);

use a more convenient

log() << "String here " << 12.33 << " and a number";

I create this syntax like this:

struct log : public ostringstream {
virtual ~log() { cout << "The log contains: " << str().c_str() <<
endl;}
};

The trick I'm trying to do is that log() creates an object of type
log, then it fills it using the ostringstream's parent '<<' and right
after the statement finishes the destructor is called and the message
is printed onto the screen.
The problem is that it doesn't:
It prints something like "The log contains: " 4129000
But if the first element after "log() << " is not a string, e.g., a
number, dec, hex or whatever, the scheme succeeds.

Why does this happen? I've got a feeling that the destructor might be
called before the first "<< " but I don't understand why.

The problem is that in C++ temporaries do not bind to non-const references.
The line

log() << "String here " << 12.33 << " and a number";

creates a temporary log-object. When the compiler interprets the following
operator<<, it has to chose between the freestanding versions

ostream& operator<< ( ostream &, whatever)

and the member functions in ostringstream. The freestanding versions cannot
match since the temporary log() will not bind to the non-const stream&
parameter. Thus, the compiler only considered member functions. The best
match is

operator<<( void* )

and thus the char* argument is converted to a void* and then all bets are
off as to what happens.

I learned this here, too:

http://groups.google.com/group/comp...f75cae8d67da33c?lnk=gst&q=Bux+tricky+&rnum=2#


Best

Kai-Uwe Bux
 
K

Kai-Uwe Bux

John said:
Very subtle. I can't tell you the answer, although I can give you a few
pointers.

Firstly it's nothing to do with destructors. Destructors for temporaries
are called at the end of the containing expression.

The number you are seeing is the address of the string "String here ".
Remember that there are several sersions of operator<< some of them are
member functions and some are global functions. The one you want to be
called is this one, which prints a string

ostream& ostream::eek:perator<<(const char*)

the one that is being called is this one, which prints a pointer address

ostream& operator<<(ostream&, const void*)

In other words your expression is being interpretted like this

operator<<(log(), "String here ") << 12.33 << " and a number";

when you would prefer to have it intepreted like this

log().operator<<("String here ") << 12.33 << " and a number";

Both these expression are legal,

Have you tested whether your compiler accepts both? I get an error for

operator<<(log(), "String here ")

and so should you. log() is a temporary and that does not bind as the first
parameter in operator<<().

and if you write either one you get the
expected output (pointer in the first case, string in the second
case).

I think your expectations are swapped. You get the void* conversion when the
member function is called.

Also if you write this


log myLog;
myLog << "String here " << 12.33 << " and a number";

you get the desired string output.

So the question is why does having a temporary of a derived class on the
left hand side of operator<< make the compiler ignore member function of
the base class?

Actually, a temporary forces the compiler to use the member function since
the freestanding version does not match.
I have no idea what the answer is. I gave up on this kind of question a
long time ago. A wise man said (I think it was Bjarne Stroustrup) 'stay
away from the obscure corners of the language'. That would be my advice.

I think it's perfectly acceptable to use macros for logging.

#define LOG(X) std::cout << "The log contains: " << X << std::endl

LOG("String here " << 12.33 << " and a number")

That would also be my advice.

One could also provide a better matching member function:

#include <sstream>
#include <iostream>
#include <ostream>

struct log
: public std::eek:stringstream
{

std::eek:stream& operator<< ( char const * msg ) {
*this << std::string( msg );
return ( *this );
}

virtual ~log() {
std::cout
<< "The log contains: "
<< this->str().c_str()
<< std::endl;
}

};


int main ( void ) {
log() << "String here ";
}


Best

Kai-Uwe Bux
 
J

John Harrison

Have you tested whether your compiler accepts both? I get an error for

operator<<(log(), "String here ")

and so should you. log() is a temporary and that does not bind as the first
parameter in operator<<().

That's a good point. I did test it, but clearly not with extensions
disabled.
I think your expectations are swapped. You get the void* conversion when the
member function is called.

That's what I said, I think.
Actually, a temporary forces the compiler to use the member function since
the freestanding version does not match.


One could also provide a better matching member function:

Good idea.

john
 
J

John Harrison

John said:
That's a good point. I did test it, but clearly not with extensions
disabled.

Maybe if I had tested it with extensions disabled the penny would have
dropped. Lesson learned I hope.

john
 
J

James Kanze

Hi all, can someone help me with this?
I'm trying to shorthen some logging function in our project, i.e.,
instead of

use a more convenient
log() << "String here " << 12.33 << " and a number";
I create this syntax like this:
struct log : public ostringstream {
virtual ~log() { cout << "The log contains: " << str().c_str() <<
endl;}
};
The trick I'm trying to do is that log() creates an object of type
log, then it fills it using the ostringstream's parent '<<' and right
after the statement finishes the destructor is called and the message
is printed onto the screen.

There are a couple of subtle issues involved here.
The problem is that it doesn't:
It prints something like "The log contains: " 4129000
But if the first element after "log() << " is not a string, e.g., a
number, dec, hex or whatever, the scheme succeeds.
Why does this happen? I've got a feeling that the destructor might be
called before the first "<< " but I don't understand why.

The destructor is called at the appropriate time; a much more
subtle issue is involved. Basically, some of the functions of
ostream are members, .e.g.:

ostream& ostream::eek:perator<<( int ) ;
ostream& ostream::eek:perator<<( void * ) ;

and others are free functions:

ostream& operator<<( ostream&, char const* ) ;

You're expression log() is a temporary: an rvalue, in the terms
of the standard. And temporaries of class type have a very
peculiar rule: you can call member functions on them, including
non-const member functions, but you cannot bind them to a
non-const reference. Given something like "log() << x", the
compiler first creates a list of callable functions, then
applies overload resolution. Once you've gotten over the
hurdle of the first function, you have an ostream&, not a
temporary log, by definition, a reference is an lvalue, and
since it is not const, all further operator << consider the full
set of visible operators.

In the classical IO streams, the operator << for char const* was
a member, and the "standard" work around was simply to start by
outputting a (possibly empty) string, e.g.:
log() << "" << myObjWithGlobalInjectionOperator ;
The standards committee changed the char const* operator to a
free function, and broke this idiom. There are currently two
solutions:

-- use the member function flush(), e.g.:

log().flush() << "String here " << ...

-- or provide the operators in your log class, as member
templates:

class log
{
public:
// ...
template< typename T >
log&
operator<<( T const& object )
{
// Here, I'm not const! so...
myCollector << object ;
return *this ;
}

private:
std::eek:stringstream myCollector ;
} ;

In practice, the second solution is a lot friendlier to your
users, but will require a bit more work on your part. (In
practice, you'll generally have to provide some specializations
for the operator<< as well, to ensure that overload resolution
will work correctly in the expected cases.)
 
I

iu2

The problem is that in C++ temporaries do not bind to non-const references.
The line

log() << "String here " << 12.33 << " and a number";

creates a temporary log-object. When the compiler interprets the following
operator<<, it has to chose between the freestanding versions

ostream& operator<< ( ostream &, whatever)

and the member functions in ostringstream. The freestanding versions cannot
match since the temporary log() will not bind to the non-const stream&
parameter. Thus, the compiler only considered member functions. The best
match is

operator<<( void* )

and thus the char* argument is converted to a void* and then all bets are
off as to what happens.

I learned this here, too:

http://groups.google.com/group/comp.lang.c++/browse_frm/thread/9f553d...

Best

Kai-Uwe Bux- Hide quoted text -

- Show quoted text -


I've read your reply and James Kanze's reply, tried both solutions and
they worked. But I still don't get something, and I'll appreciate your
help:
You said that the best match is operator<<( void* );
Why is that? Isn't there any operator<<(char *) or operator<<(const
char *) member?
 
J

James Kanze

On Jul 1, 9:01 am, Kai-Uwe Bux <[email protected]> wrote:

[...]
I've read your reply and James Kanze's reply, tried both solutions and
they worked. But I still don't get something, and I'll appreciate your
help:
You said that the best match is operator<<( void* );
Why is that? Isn't there any operator<<(char *) or operator<<(const
char *) member?

Not any more. (There was in the classical iostream.)

A binary operator can be overloaded as either a member with a
single parameter (the this being the left operand, and the
parameter the right), or a free function taking two parameters.
According to the standard, the overload of << for void const*
(and the numeric types) is a member, the overload of << for char
const* a free function.

Because the rules for calling a member function are different
than those for binding to the reference argument of a free
function, if you have a temporary as the left argument, only the
member functions will be considered.
 
I

iu2

Not any more. (There was in the classical iostream.)

A binary operator can be overloaded as either a member with a
single parameter (the this being the left operand, and the
parameter the right), or a free function taking two parameters.
According to the standard, the overload of << for void const*
(and the numeric types) is a member, the overload of << for char
const* a free function.

Because the rules for calling a member function are different
than those for binding to the reference argument of a free
function, if you have a temporary as the left argument, only the
member functions will be considered.

--
James Kanze (GABI Software) email:[email protected]
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Thanks, all is clear now.
 
I

iu2

...









Thanks, all is clear now.- Hide quoted text -

- Show quoted text -

Sorry, but there still one more thing...
Doing
log() << "" << "The string";
outputs "4129900TheString"
Why?
Same thing for the following (a temprorary ostringstream)
cout << ((ostringstream &)(ostringstream() << " Hello!" << "
world")).str().c_str() << endl;
which outputs
4219134 world

What does the first string output actually do? It seems that after
putputting the first string the binding of ostream to the free
operator <<(ostream &, const char *) occures.

I think the same thing happens with your first solution -
log().flush() << "the string"; Why does it work?
Thanks
 
J

James Kanze

Sorry, but there still one more thing...
Doing
log() << "" << "The string";
outputs "4129900TheString"
Why?

A priory, the first "" invokes the member operator<<( void
const* ), which outputs the actual address. Like all
operator<<, it returns a non-const reference to the stream, so
the second << can bind to the non-member function, which takes a
char const*, and outputs it as text.
Same thing for the following (a temprorary ostringstream)
cout << ((ostringstream &)(ostringstream() << " Hello!" << "
world")).str().c_str() << endl;
which outputs
4219134 world

As above.
What does the first string output actually do?

The first << uses the temporary as its right argument, which
means that it cannot bind to the non-member functions. The best
member function is operator<<( void const* ), so that is what
operator overloading calls. The second << uses the return value
of the first << as its right hand operand. Since this is an
ostream&, it has no problem binding with the free functions as
well as the members; the free function operator<<(
std::eek:stream&, char const* ) is a better match, so it gets
chosen.
It seems that after
putputting the first string the binding of ostream to the free
operator <<(ostream &, const char *) occures.
Exactly.

I think the same thing happens with your first solution -
log().flush() << "the string"; Why does it work?

Because the left operand is NOT the temporary, but the return
value of the preceding function (e.g. the member operator<<, or
ostream::flush()). Since the return value is a non-const
reference, it binds to the non-const reference of the free
function without any problems.

IMHO: given that some of the operator<< must be free functions
(those provided by the user for his types), it probably would
have been better to have made them all free functions, to avoid
such confusion. An even better solution would have been to make
the operator<< a member template, which the user specialized for
his own types, but the idiom was developped before member
templates, or even templates in general, existed, so this option
wasn't available.
 
I

iu2

...
The first << uses the temporary as its right argument, which
means that it cannot bind to the non-member functions. The best
member function is operator<<( void const* ), so that is what
operator overloading calls. The second << uses the return value
of the first << as its right hand operand. Since this is an
ostream&, it has no problem binding with the free functions as
well as the members; the free function operator<<(
std::eek:stream&, char const* ) is a better match, so it gets
chosen.

Thanks, that gave me this idea that works too (it is not convenient to
use, but I tried it to see if I understood this thing):
((ostream &)log()) << "The string";

iu2
 
J

James Kanze

Thanks, that gave me this idea that works too (it is not convenient to
use, but I tried it to see if I understood this thing):
((ostream &)log()) << "The string";

Which is the equivalent of:
const_cast< ostream& >( log() ) << ...
Yes, it should work. (I think. I don't have my copy of the
standard here, so there might be some special rule which
requires an lvalue here, but I don't think so.)

To tell the truth, in recent days, I've usually used an entirely
different approach: my own stream class, forwarding to a member
ostringstream (or not, depending), using template member
functions. I use a reference counting scheme so that copies
behave as if they were the orignal object, and final clean-up
only occurs in the destructor of the last copy. The non-copy
constructor grabs a lock, and signals the streambuf (a
forwarding streambuf) that this is the start of a record, and
the last destructor ensures that the record ends with a new line
(it may contain others), flushes to the real output, and frees
the lock. (I use macros for the actual log object, so that I
can automatically insert __FILE__ and __LINE__ as well.)
 

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,774
Messages
2,569,598
Members
45,147
Latest member
CarenSchni
Top