I'm somewhat new to the C++ language. I'm writing a utility class for logging to file. It works beautifully except that now I would like to enhance it by making it more convenient to use (e.g. pass stringstreams to a log function).
This is what I've been trying and it hasn't worked.
definition:
void LogStream( std::stringstream i_Log ){
m_FileHandle << i_Log << std::endl;
}
call:
m_LogObject->LogStream( "MKLBSearchEngine::Search( " << x << ", " << i_Filter << " ) - No Results Found" );
There are several problems with your solution. The first is that you're
passing stringstream
by value, and it doesn't support copy. You need
by reference. The second is that at the call site, the return value of
the operator<<
overloads is ostream&
, not stringstream
, and since
stringstream
isn't a base class of ostream&
(it's the other way
round), you can't initialize the stringstream
(or the stringstream&
)
with it. And finally, there's no operator<<
which takes a
stringstream
as the right hand parameter, so the statement in the
LogStream
function can't work. Finally, this is going to be somewhat
awkward for the user anyway. A log of operator<<
are non-members,
with an ostream&
non-const reference as first argument, so you can't
call them with a temporary as the left argument. (In your example call,
of course, you forgot to create the std::ostringstream
anyway; it
won't compiler because there is no overload of <<
which takes a char
const[]
or a char const*
as its left hand operand.)
There are work-arounds for almost all of these problems. Something like:
void LogStream( std::ostream& text )
{
std::ostringstream& s = dynamic_cast<std::ostringstream&>(text);
m_FileHandle << s.str() << std::endl;
}
handles all of the problems except the last; the last has to be handled by the client, something like:
m_LogObject->LogStream( std::ostringstream().flush() << "..." << x );
(The call to std::ostream::flush()
returns a non-const reference to
the stream, which can be used to initialize further std::ostream&
.
And while you can't initialize a non-const reference with a temporary,
you can call a non-const member function on it.)
The awkwardness of this for the client code makes me generally prefer a
more complex solution. I define a special LogStreamer
class,
something like:
class LogStreamer
{
boost::shared_ptr< std::ostream > m_collector;
std::ostream* m_dest;
public:
LogStreamer( std::ostream& dest )
, m_collector( new std::ostringstream )
, m_dest( &dest )
{
}
~LogStreamer()
{
if ( m_collector.unique() ) {
*m_dest << m_collector->str() << std::endl;
}
}
template <typename T>
LogStreamer& operator<<( T const& value )
{
*m_collector << value;
return *this;
}
};
and
LogStreamer LogStream() { return LogStreamer( m_FileHandle ); }
The client code can then write:
m_LogObject->LogStream() << "..." << x;
In my own code: the log object is always a singleton, the call is
through a macro, which passes __FILE__
and __LINE__
to the LogStream()
function, and the final target ostream is a special streambuf with a
special function, called by LogStream()
, which takes a filename and a
line number, outputs them, along with the time stamp, at the start of
the next line output, and indents all other lines. A filtering
streambuf with something like:
class LogFilter : public std::streambuf
{
std::streambuf* m_finalDest;
std::string m_currentHeader;
bool m_isAtStartOfLine;
protected:
virtual int overflow( int ch )
{
if ( m_isAtStartOfLine ) {
m_finalDest->sputn( m_currentHeader.data(), m_currentHeader.size() );
m_currentHeader = " ";
}
m_isAtStartOfLine = (ch == '\n');
return m_finalDest->sputc( ch );
}
virtual int sync()
{
return m_finalDest->sync();
}
public:
LogFilter( std::streambuf* dest )
: m_finalDest( dest )
, m_currentHeader( "" )
, m_isAtStartOfLine( true )
{
}
void startEntry( char const* filename, int lineNumber )
{
std::ostringstream header;
header << now() << ": " << filename << " (" << lineNumber << "): ";
m_currentHeader = header.str();
}
};
(The function now()
, of course, returns a std::string
with the
timestamp. Or a struct tm
, and you've written a <<
for tm
.)
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