如果使用debug标志编译,则启用调试打印语句

时间:2017-10-29 12:18:09

标签: c++ debugging

我们说我有一个有很多功能的项目。为了调试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

虽然效果很好,但它看起来并不优雅。是否有标准/更好的方法来实现这一目标?

1 个答案:

答案 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库,因为它有一个有趣的组合使用流,同时保留了每次通话的能力。事物(例如记录时间戳,行号等)。