I would like to be able to do:
foo(stringstream()<<"number = " << 500);
EDIT: single line solution is crucial since this is for logging purposes. These will be all around the code.
inside foo will print the string to screen or something of the sort.
now since stringstream’s operator<< returns ostream&, foo’s signature must be:
foo(ostream& o);
but how can I convert ostream& to string? (or char*).
Different approaches to achieving this use case are welcome as well.
The obvious solution is to use
dynamic_castinfoo. But the givencode still won’t work. (Your example will compile, but it won’t do what
you think it should.) The expression
std::ostringstream()is atemporary, 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 codewill compile, but it won’t do what you expect.
You can work around this problem, using something like:
std::ostream::flush()returns a non-const reference, so there are nofurther 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 templatedmember
operator<<which forwards to the containedstd::ostringstream. Your functionfoowould take aconstreference to this—or what I offen do is have the destructor call
foodirectly, so that the client code doesn’t even have to worry aboutit; it does something like:
The function
log()returns an instance of the wrapper class (but seebelow), 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::ostringstreamisn’tcopyable, it won’t even compile. The solution here is to put all of the
actual logic, including the instance of
std::ostringstreamand thedestructor logic calling
fooin a separate implementation class, havethe public wrapper have a
boost::shared_ptrto it, and forward. Orjust reimplement a bit of the shared pointer logic in your class:
Note that it’s easy to extend this to support optional logging; just
provide a constructor for the LogWrapper which sets
collectortoNULL, and test for this in theoperator<<.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
fooin that case. Logically, I’d hope that the only exception youmight get is
std::bad_alloc, but there will always be a user whowrites something like:
where the
+is a user defined overload which throws.