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.
std::stringstream::str() is the method you are looking for. std::stringstream is a more generic tool. You can use the more specialized class std::ostringstream for this specific job. If you are working with std::wstring type of strings, you must prefer std::wstringstream or std::wostringstream instead.
A stringstream associates a string object with a stream allowing you to read from the string as if it were a stream (like cin). To use stringstream, we need to include sstream header file. The stringstream class is extremely useful in parsing input.
How to Perform Extraction or Read Operation in StringStream in C++ Like the insertion, we can also perform extraction on StringStream in C++, like the cin >> operator. We can again do this by using the >> operator or the str() function.
std::stringstream::str The first form (1) returns a string object with a copy of the current contents of the stream. The second form (2) sets s as the contents of the stream, discarding any previous contents.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With