I have seen many loggers using operator<<
to read log messages and write them on file:
template<typename T>
Logger& Logger::operator<<(Logger& l, const T& s) {
l.logStream << s;
return l;
}
...
Logger log;
log << "Test message " << 123;
I want the log to be flushed after "Test message"
and 123
are written to the stream. However, rather than the user being in control of the flushing, I want the logger to flush the stream when it reaches the ;
.
How can I do that?
Two solutions came to my mind, but I find both not acceptable:
I could put the new line at the beginning of each log message, so that the previous log is flushed, and in case of a crash all we loose is the current log.
operator<<
could flush. But flushing on each invocation of <<
incurs a big performance penalty due to the number of write operations on disk (wearing). I want the flush to involve the whole message.
I would like "TestMessage" << 123 to be flushed all together, in a single operation.
You can do that by returning a proxy object from log::operator<<
that has its own proxy::operator<<
which forwards to log::operator<<
, and flushes in its destructor.
Something along the line of
#include <iostream>
#include <iomanip>
struct Logger {
std::ostream& out = std::cout;
};
struct ProxyLogger {
Logger& l;
explicit ProxyLogger(Logger& l) : l(l) {}
template <typename T>
ProxyLogger& operator<<(const T& s) {
l.out << s;
return *this;
}
~ProxyLogger();
};
template<typename T>
ProxyLogger operator<<(Logger& l, const T& s) {
l.out << s;
return ProxyLogger(l);
}
ProxyLogger::~ProxyLogger() { l.out << "hello proxy" << std::flush; }
int main() {
Logger log;
log << "test" << 123;
}
test123hello proxy
The proxy adds hello proxy
to the message in its destructor for illustration.
The code you posted is somewhat bogus. Your operator<<
is a member function but has an additional Logger&
parameter. Thats not how you are going to get log << "test";
to work. Make it either a friend with the explicit argument or a member without.
Moreoever, a templated operator<<
looks rather cool at first, but as soon as you want to handle iomanipulators, as eg std::flush
, things get hairy, because most of them are function templates. I avoided passing the iomanipulator through Logger::operator<<
by letting the proxy pipe to the stream directly. You may want to make the Logger
s stream private and the proxy a friend. Making io manipulators actually work with this approach I consider as beyond the scope of this question.
For further reading I refer you to articles about RAII (resource acquisition is initialization).