I have a custom logging class that supports iostream
-syntax via a templated operator <<
:
template< class T >
MyLoggingClass & operator <<(MyLoggingClass &, const T &) {
// do stuff
}
I also have a specialized version of this operator that is supposed to be called when a log-message is complete:
template< >
MyLoggingClass & operator <<(MyLoggingClass &, consts EndOfMessageType &){
// build the message and process it
}
EndOfMessageType
is defined like this:
class EndOfMessageType {};
const EndOfMessageType eom = EndOfMessageType( );
The global constant eom
is defined so that users can use it just like std::endl
at the end of their log-messages. My question is, a开发者_如何学Pythonre there any pitfalls to this solution, or is there some established pattern to do this?
Thanks in advance!
std::endl
is a function, not an object, and operator<<
is overloaded for accepting a pointer to a function taking and returning a reference to ostream
. This overload just calls the function and passes *this
.
#include <iostream>
int main()
{
std::cout << "Let's end this line now";
std::endl(std::cout); //this is the result of cout << endl, or cout << &endl ;)
}
Just an alternative to consider.
By the way, I don't think there is any need to specialize the operator: a normal overload does just as well, if not better.
I think your solution is acceptable. If you wanted to do it differently, you could create a class Message
, that would be used instead of the your MyLoggingClass
and provided automatic termination.
{
Message m;
m << "Line: " << l; // or m << line(l)
m << "Message: foo"; // or m << message("foo");
log << m; // this would automatically format the message
}
I have done it this way, like some other people did. Have a function Error
/ Log
/ Warning
/ etc that could look like this
DiagnosticBuilder Error( ErrType type, string msg, int line );
This will return a temporary builder object, whose class is basically defined like
struct DiagnosticBuilder {
DiagnosticBuilder(std::string const& format)
:m_emit(true), m_format(format)
{ }
DiagnosticBuilder(DiagnosticBuilder const& other)
:m_emit(other.m_emit), m_format(other.m_format), m_args(other.m_args) {
other.m_emit = false;
}
~DiagnosticBuilder() {
if(m_emit) {
/* iterate over m_format, and print the next arg
everytime you hit '%' */
}
}
DiagnosticBuilder &operator<<(string const& s) {
m_args.push_back(s);
return *this;
}
DiagnosticBuilder &operator<<(int n) {
std::ostringstream oss; oss << n;
m_args.push_back(oss.str());
return *this;
}
// ...
private:
mutable bool m_emit;
std::string m_format;
std::vector<std::string> m_args;
};
So if you are building a log message in a loop, be it so
DiagnosticBuilder b(Error("The data is: %"));
/* do some loop */
b << result;
As soon as the builder's destructor is called automatically, the message is emitted. Mostly you would use it anonymously
Error("Hello %, my name is %") << "dear" << "litb";
精彩评论