记录器的一个问题是即使您不想要它们也会记录。它们通常要么硬连线写入文件,要么回显到控制台 - 有时两者都是。 这与单元测试相反,其中记录到任一接收器将减慢测试速度。
我也反对使用printf样式的可变参数,因为人们切割,粘贴和忘记更改参数类型或从字符串中删除参数时,它们总是会出错。
所以我决定对此采取一些措施。
我的基本想法是让一个LogStream对象接受ostream
作为参数,这样你就可以在单元测试中指定一个文件,控制台或者静默地吞下它。这是通过
作为同时接受严重性级别的Logger类的参数。
这是记录器的概念流程,它只将致命事件记录到ostringstream(从单元测试中解释):
std::ostringstream oss;
auto logstream = LogStream(oss);
auto logger = Logger(logstream, FATAL);
logger(INFO) << "This is a log test\n"; // (1)
std::cout << oss.str(); // Display nothing as nothing fatal has been logged
考虑到这个问题,我需要一个临时流(ostream可以工作)来保存流,然后再决定它是否满足要放在LogStream对象中的条件。
我遇到的问题是在第(1)行,您将拥有函数调用运算符,它返回对LogStream的引用,并在退出后构建其余的流:
LogStream& operator()(Severity severity)
{
// stream not completed
return *m_dest;
}
我认为有一种方法,因为你可以获得使插入运算符重载的朋友函数,它接受一个流参数:
std::ostream& operator<<(std::ostream& os, const Complex& c)
{
os << c.real << '.' << c.imaginary;
return os;
}
但到目前为止,我一直无法弄清楚函数运算符的确切语法。
是否可能,或者我是在咆哮错误的树?
答案 0 :(得分:1)
你需要像这样的设计:
enum class LogLevel
{
FATAL,
INFO
};
class LogStream : public std::ostream
{
public:
LogStream( const std::ostream& )
// ...
{
}
};
class EatStream : public std::ostream
{
// does nothing
};
class Logger
{
const LogStream m_logger;
const EatStream m_nolog;
const LogLevel m_loglevel;
public:
Logger( const LogStream&&, const LogLevel ) // I'd suggest move, but you might as well just take a reference and copy
// ...
{
// ...
}
std::ostream& operator()( const LogLevel level )
{
return level <= m_loglevel ? m_logger : m_nolog;
}
};
这里的关键是LogStream和EatStream有一个共同的基础,你的operator()通过引用返回。它不一定是std :: ostream,因为方便或者懒惰才使用它。一旦返回对公共库的引用,就可以正确地从vftable调用虚拟。请注意,您返回的引用由Logger保持活动状态,因此它不应超出operator()()的结果范围(在您的情况下这可能不是问题,只是为了完整性而注明)。