我们说我有一个有很多功能的项目。为了调试purpopses,我希望每个人在调用时打印出一条诊断消息(或几个):
int f(int arg) {
cerr << "f() called with argument" << arg << endl;
int ret = 42;
cerr << "f() returned " << ret << endl;
return ret;
}
等等。在一个功能中,cerr上可能会打印多达五到六条消息。除非设置了调试标志(如NDEBUG
),否则我希望能够禁用它们。我能做的一件事就是用if语句包装每个cerr语句。
#ifdef NDEBUG
const bool DEBUG_ON = true;
#else
const bool DEBUG_ON = false;
#endif
int f(int arg) {
if (DEBUG_ON) {
cerr << "f() called with argument" << arg << endl;
}
int ret = 42;
if (DEBUG_ON) {
cerr << "f() returned " << ret << endl;
}
return ret;
}
但它真的很乏味,最后,几十个这样的陈述几乎无法理解。我的想法是将cerr
替换为带有重载<<
运算符的自定义对象,该运算符会将传递给它的任何参数发送到空白&#34;像这样:
class NullOutStream {
public:
NullOutStream operator<<(...) {
return *this;
}
};
NullOutStream debug_out;
#ifdef NDEBUG
#define debug_out cerr
#else
#define debug_out debug_out
#endif
虽然效果很好,但它看起来并不优雅。是否有标准/更好的方法来实现这一目标?
答案 0 :(得分:1)
没有&#34;标准&#34;这样做的方式。有许多日志/断言库可以处理类似的问题,并且有不同的方法可以解决这个问题。要记住的一些事项:
保留日志语句的副作用(可能与您的一样)可能是也可能不是。例如,如果您执行了类似的操作:
debug_out << ++i;
无论您是否处于调试模式,这都会增加i
。在这种情况下,这可能是理想的。相反,如果你这样做:
debug_out << some_object.expensive_to_string_operation();
即使您未处于调试模式,也会调用expensive_to_string_operation()
。在这种情况下,这可能是不可取的。
如果您使用printf
样式的调试宏,则不会发生这种情况。像这样:
的
#ifdef NDEBUG
#define logf(...)
#else
#define logf(...) printf(__VA_ARGS__)
#endif
原因是当处于非调试模式时,预处理器将删除参数。
您可能希望在每个日志语句之前或之后执行其他操作,例如记录时间戳,刷新日志文件等。可以使用析构函数对流进行此操作,但实现起来更复杂。使用函数调用更容易做到这一点,例如像这样的东西:
#define log(msg) printf("%s: %s\n", timestamp(), msg)
您可能希望使用日志记录文件名/行号。同样,使用函数调用比使用流更容易。
如果你想按对象类型进行自定义格式化,那么流可能会更好 - printf界面并不适合这种格式。
我建议您查看一些现有的日志记录库,以了解不同的方法。我建议查看Google的glog库,因为它有一个有趣的组合使用流,同时保留了每次通话的能力。事物(例如记录时间戳,行号等)。