Re: ostringstream derived problem
On Jul 1, 7:43 am, iu2 <isra...@elbit.co.il> wrote:
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.
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::operator<<( int ) ;
ostream& ostream::operator<<( 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::ostringstream 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.)
--
James Kanze (Gabi Software) email: james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34