The obvious solution is to use dynamic_cast
in foo
. But the given
code still won't work. (Your example will compile, but it won't do what
you think it should.) The expression std::ostringstream()
is a
temporary, you can't initialize a non-const reference with a temporary,
and the first argument of std::operator<<( std::ostream&, char const*)
is a non-const reference. (You can call a member function on a
temporary. Like std::ostream::operator<<( void const* )
. So the code
will compile, but it won't do what you expect.
You can work around this problem, using something like:
foo( std::ostringstream().flush() << "number = " << 500 );
std::ostream::flush()
returns a non-const reference, so there are no
further problems. And on a freshly created stream, it is a no-op.
Still, I think you'll agree that it isn't the most elegant or intuitive
solution.
What I usually do in such cases is create a wrapper class, which
contains it's own std::ostringstream
, and provides a templated
member operator<<
which forwards to the contained
std::ostringstream
. Your function foo
would take a const
reference to this—or what I offen do is have the destructor call
foo
directly, so that the client code doesn't even have to worry about
it; it does something like:
log() << "number = " << 500;
The function log()
returns an instance of the wrapper class (but see
below), and the (final) destructor of this class calls your function
foo
.
There is one slight problem with this. The return value may be copied,
and destructed immediately after the copy. Which will wreck havoc with
what I just explained; in fact, since std::ostringstream
isn't
copyable, it won't even compile. The solution here is to put all of the
actual logic, including the instance of std::ostringstream
and the
destructor logic calling foo
in a separate implementation class, have
the public wrapper have a boost::shared_ptr
to it, and forward. Or
just reimplement a bit of the shared pointer logic in your class:
class LogWrapper
{
std::ostringstream* collector;
int* useCount;
public:
LogWrapper()
: collector(new std::ostringstream)
, useCount(new int(1))
{
}
~LogWrapper()
{
-- *useCount;
if ( *useCount == 0 ) {
foo( collector->str() );
delete collector;
delete useCount;
}
}
template<typename T>
LogWrapper& operator<<( T const& value )
{
(*collector) << value;
return *this;
}
};
Note that it's easy to extend this to support optional logging; just
provide a constructor for the LogWrapper which sets collector
to
NULL
, and test for this in the operator<<
.
EDITED:
One other thing occurs to me: you'll probably want to check whether the
destructor is being called as a result of an exception, and not call
foo
in that case. Logically, I'd hope that the only exception you
might get is std::bad_alloc
, but there will always be a user who
writes something like:
log() << a + b;
where the +
is a user defined overload which throws.