我在上面看到了两个问题。第一个是分叉你的消息(到文件和控制台)。第二个是用一些额外的东西包装所写的内容。
meta_stream 处理 operator<< 重载。它使用 CRTP 静态分派给它的子类型:
template<class D, class substream>
struct meta_stream {
D& self() { return *static_cast<D*>(this); } // cast myself to D
// forwarders of operator<<
template<class X>
friend D& operator<<( meta_stream<D>& d, X const& x ) {
d.self().write_to(x);
return d.self();
}
friend D& operator<<(
meta_stream<D>& d,
substream&(*mod_func)(substream&)
) {
d.self().write_to(mod_func);
return d.self();
}
};
由于std::endl 和其他修饰符的工作方式,我不得不重写<< 两次——它们是重载函数的名称。
这样就解决了将同一个字符串输出到两个不同的ostream的问题:
template<class substream>
struct double_ostream:
meta_stream<double_ostream<substream>,substream>
{
substream* a = nullptr;
substream* b = nullptr;
template<class X>
void write_to( X&&x ) {
if (d.a) (*d.a) << x;
if (d.b) (*d.b) << std::forward<X>(x);
}
double_ostream( std::basic_ostream<CharT>* a_, std::basic_ostream<CharT>* b_ ):
a(a_), b(b_)
{}
double_ostream(double_ostream const&)=default;
double_ostream()=default;
double_ostream& operator=(double_ostream const&)=default;
};
注意通过meta_stream 使用CRTP。我只需要实现write_to。
首先,将 4 个记录器写入此数组:
enum loglevel {
debug, error, warning, info
};
double_stream<std::ostream> loggers[4];
给每个指针一个指向std::cout 的指针和一个指向(存储在其他地方)流包装您想要保存日志的文件的指针。如果您不希望将该级别记录到该输出流(例如,在发布中,跳过调试日志),您可以传递nullptr,并且您可以将内容记录到不同的日志文件(调试到一个文件,信息到另一个)。
double_stream<std::ostream> log( loglevel l ) {
double_stream<std::ostream> retval = loggers[l];
std::string message;
// insert code to generate the current date here in message
// insert code to print out the log level here into message
retval << message;
return retval;
}
现在log(debug) << "hello " << "world\n" 将为您写信息。
如果您不想在日志消息的末尾写下换行符,您可以做更多花哨的事情,但我怀疑这是否值得。只写换行符。
如果你真的想要那个功能:
template<class substream>
struct write_after_ostream:
meta_stream<write_after_ostream<substream>,substream>
{
substream* os = nullptr;
template<class X>
void write_to( X&&x ) {
if (os) *os << std::forward<X>(x);
}
~write_after_ostream() {
write_to(message);
}
write_after_ostream( substream* s, std::string m ):
os(s), message(m)
{}
std::string message;
}
write_after_ostream<double_stream<std::ostream>> log( loglevel l ) {
// note & -- store a reference to it, as we will be using a pointer later:
double_stream<std::ostream>& retval = loggers[l];
std::string pre_message;
// insert code to generate the current date here in pre_message
// insert code to print out the log level here into pre_message
retval << pre_message;
return {&retval, "\n"};
}
但我认为这不值得。