У меня есть собственный класс ведения журнала, который поддерживает синтаксис iostream
через шаблонный оператор <<
:
template< class T >
MyLoggingClass & operator <<(MyLoggingClass &, const T &) {
// do stuff
}
У меня также есть специализированная версия этого оператора, которая должна вызываться при завершении сообщения журнала:
template< >
MyLoggingClass & operator <<(MyLoggingClass &, consts EndOfMessageType &){
// build the message and process it
}
EndOfMessageType
определяется следующим образом:
class EndOfMessageType {};
const EndOfMessageType eom = EndOfMessageType( );
Глобальная константа eom
определяется так, чтобы пользователи могли использовать его так же, как std :: endl
в конце своих сообщений журнала. У меня вопрос, есть ли какие-либо подводные камни в этом решении или есть какой-то определенный шаблон для этого?
Заранее спасибо!
Я думаю, ваше решение приемлемо. Если вы хотите сделать это по-другому, вы можете создать класс Message
, который будет использоваться вместо вашего MyLoggingClass
и обеспечивать автоматическое завершение.
{
Message m;
m << "Line: " << l; // or m << line(l)
m << "Message: foo"; // or m << message("foo");
log << m; // this would automatically format the message
}
Я сделал это так, как и некоторые другие люди. Есть функция Ошибка
/ Журнал
/ Предупреждение
/ и т. д., которая может выглядеть так
DiagnosticBuilder Error( ErrType type, string msg, int line );
Это вернет временный объект построителя, класс которого в основном определен как
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;
};
Так что, если вы строите сообщение журнала в цикле, пусть так и будет
DiagnosticBuilder b(Error("The data is: %"));
/* do some loop */
b << result;
Как только автоматически вызывается деструктор компоновщика, сообщение генерируется. В основном вы использовали бы его анонимно
Error("Hello %, my name is %") << "dear" << "litb";
std::endl
— это функция, а не объект, а operator<<
перегружен для приема указателя на функцию, принимающую и возвращающую ссылку на ostream
. . Эта перегрузка просто вызывает функцию и передает *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 ;)
}
Просто альтернатива для рассмотрения.
Кстати, я не думаю, что есть необходимость в специализации оператора: обычная перегрузка делает то же самое, если не лучше.