I’m working on a simple logger wrapper for my projects which will let me to easily swap out the backend.
This is my ideal interface:
log::error << "some" << " log " << "message";
The way I implemented it was:
-
log::error#operator<<returns a temporarySinkobject. -
Sink#operator<<returns*thisand defines a move constructor. -
The complete message can be utilized in
Sink‘s destructor which is called at the end of the invocation chain.
Contrived Implementation:
#include <iostream>
#include <string>
struct Sink {
Sink (std::string const& msg) : m_message(msg) {}
// no copying
Sink (Sink const& orig) = delete;
// move constructor
Sink (Sink && orig) : m_message(std::move(orig.m_message)) {};
// use the complete string in the destructor
~Sink() { std::cerr << m_message << std::endl;}
Sink operator<< (std::string const& msg) {
m_message.append(msg);
return std::move(*this);
}
std::string m_message;
};
struct Level {
Sink operator<< (std::string const& msg) { return Sink(msg); }
};
int main() {
Level log;
log << "this" << " is " << "a " << "test";
}
This works fine except I need a clean way of disabling logging.
If I wasn’t using chaining, my log function could use a pre-processor directive to remove the function’s content
void log (std::string) {
#ifdef LOGGING_ENABLED
// log message
#endif
}
The compiler would then optimize and remove the empty function call. But I don’t know how I’d do that with the api I’m trying to achieve. I know it’s possible because glog does it somehow.
Using directives like this defeats the purpose of having a nice api.
#ifdef LOGGING_ENABLED
log << "this" << " is " << "a " << "test";
#endif
What is a clean way of disabling these types of chained calls?
Any help is appreciated.
You have to imlement another
Sinkwhich does nothing when logging. Glog calls this a null-stream:In your case, a simple implementation would look like
This is very simple and can be optimized away from the compiler.