C++中仅调试的ostreams?

pyr*_*chi 12 c++ debugging iostream

我已经实现了一个ostreamfor debug输出,它最终发送调试信息OutputDebugString.它的典型用法看起来像这样(debugostream对象在哪里):

debug << "some error\n";
Run Code Online (Sandbox Code Playgroud)

对于发布版本,不输出这些调试语句的最不痛苦和最高效的方法是什么?

小智 9

最常见(当然也是性能最佳)的方法是使用预处理器删除它们,使用类似这样的东西(最简单的实现):

#ifdef RELEASE
  #define DBOUT( x )
#else
  #define DBOUT( x )  x
#endif
Run Code Online (Sandbox Code Playgroud)

然后你可以说

DBOUT( debug << "some error\n" );
Run Code Online (Sandbox Code Playgroud)

编辑: 您当然可以使DBOUT更复杂:

#define DBOUT( x ) \
   debug << x  << "\n"
Run Code Online (Sandbox Code Playgroud)

这允许更好的语法:

DBOUT( "Value is " << 42 );
Run Code Online (Sandbox Code Playgroud)

第二种方法是将DBOUT定义为流.这意味着您必须实现某种类型的null流类 - 请参阅实现no-op std :: ostream.但是,此类流在发布版本中确实具有运行时开销.


Ste*_*sop 7

这个怎么样?您必须检查它在发布中实际上是否优化:

#ifdef NDEBUG
    class DebugStream {};
    template <typename T>
    DebugStream &operator<<(DebugStream &s, T) { return s; }
#else
    typedef ostream DebugStream;
#endif
Run Code Online (Sandbox Code Playgroud)

您必须将调试流对象作为DebugStream传递,而不是作为ostream传递,因为在发布版本中它不是一个.这是一个优点,因为如果您的调试流不是ostream,这意味着您不会产生支持ostream接口的空流的通常运行时惩罚(虚拟函数实际上被调用但什么都不做).

警告:我刚刚做了这个,通常我会做类似于Neil的回答 - 有一个宏意思"只在调试版本中执行此操作",因此它在源代码中显式是什么是调试代码,什么不是.我实际上并不想抽象的一些事情.

Neil的宏也有一个属性,绝对肯定不会在发布中评估它的论点.相比之下,即使我的模板内联,你会发现有时:

debug << someFunction() << "\n";
Run Code Online (Sandbox Code Playgroud)

不能被优化为什么,因为编译器不一定知道someFunction()没有副作用.当然,如果someFunction() 确实有副作用,那么您可能希望在发布版本中调用它,但这是日志和功能代码的特殊混合.


Sha*_*n-X 7

更漂亮的方法:

#ifdef _DEBUG
#define DBOUT cout // or any other ostream
#else
#define DBOUT 0 && cout
#endif

DBOUT << "This is a debug build." << endl;
DBOUT << "Some result: " << doSomething() << endl;
Run Code Online (Sandbox Code Playgroud)

只要你不做任何奇怪的事情,调用和传递给的函数DBOUT将不会在发布版本中调用.该宏因运算符优先级和逻辑AND而起作用; 因为&&优先级低于<<,发布版本编译DBOUT << "a"0 && (cout << "a").如果左侧的表达式求值为零,则逻辑AND不会评估右侧的表达式false; 因为左手表达式总是计算为零,所以任何值得使用的编译器都会删除右手表达式,除非禁用所有优化(即使这样,显然仍然无法访问代码).


这是一个会破坏这个宏的奇怪事物的例子:

DBOUT << "This is a debug build." << endl, doSomething();
Run Code Online (Sandbox Code Playgroud)

看逗号.doSomething()无论是否_DEBUG定义,都将被调用.这是因为语句在发布版本中评估为:

(0 && (cout << "This is a debug build." << endl)), doSomething();
// evaluates further to:
false, doSomething();
Run Code Online (Sandbox Code Playgroud)

要在此宏中使用逗号,必须将逗号括在括号中,如下所示:

DBOUT << "Value of b: " << (a, b) << endl;
Run Code Online (Sandbox Code Playgroud)

另一个例子:

(DBOUT << "Hello, ") << "World" << endl; // Compiler error on release build
Run Code Online (Sandbox Code Playgroud)

在发布版本中,评估为:

(0 && (cout << "Hello, ")) << "World" << endl;
// evaluates further to:
false << "World" << endl;
Run Code Online (Sandbox Code Playgroud)

这会导致编译器错误,因为除非定义了自定义运算符,否则指针bool不能向左移位char.此语法还会导致其他问题:

(DBOUT << "Result: ") << doSomething() << endl;
// evaluates to:
false << doSomething() << endl;
Run Code Online (Sandbox Code Playgroud)

就像逗号使用得不好时一样,doSomething()仍然会被调用,因为它的结果必须传递给左移运算符.(只有在定义了一个bool通过char指针左移的自定义运算符时才会发生这种情况;否则会发生编译错误.)

不要括号DBOUT << ....如果你想为一个字面整数移位括号,然后将它括起来,但我不知道有一个很好的理由来为一个流操作符括号.

  • 正是我想象中的样子,以及我一直在寻找的东西。非常感谢 ! (2认同)