调试宏与调试变量

Debug macro vs. Debug variable

本文关键字:调试 变量      更新时间:2023-10-16

下面是使用调试变量的示例

class A{
 public:
  A(bool debug):m_debug(debug){};
  ~A(){};
  void Test(){
    for(int i=0;i<1000000;i++){
      // do something
      if(m_debug) print();
    }
  }
  void print(){
    std::cout << "something" << std::endl;
  }

 private:
  bool m_debug;
};

以下是使用调试宏预处理器的示例

#include "Preprocessor.h"
class A{
 public:
  void Test(){
    for(int i=0;i<1000000;i++){
      // do something
#ifdef DEBUG
        print();
#endif
    }
  }
  void print(){
    std::cout << "something" << std::endl;
  }
};

在预处理器中,h只是

#define DEBUG

使用调试变量的好处是该类对全局预处理器头的依赖性减少了一个。宏方法的好处是,在运行时执行的if语句减少了1000000,这对于图形应用程序来说可能是至关重要的,比如说,当每帧每秒都很重要时。什么是更好的方法?

更好的方法是使用预处理器,但不需要新的头文件来定义宏。

您可以在编译时使用-DMACRO_NAME-DDEBUG设置标志。

只要作业是打印调试信息,就必须像Visual Studio那样使用宏(调试/发布构建)。然而,在Qt世界中,有一个类QLoggingCatagory,您可以在其中启用/禁用日志记录部分。每当消息被注销时,它都会调用函数QLoggingCategory::isDebugEnabled(),这让我认为这对性能来说不是什么大问题,至少在正常使用时是这样。

也就是说,如果我们将Visual Studio MFC应用程序与Qt应用程序进行比较,MFC应用程序就会快速启动。这至少在一定程度上可以归因于它使用了宏,并且在调试和发布版本中可以很容易地注意到差异,其中宏/调试信息是主要差异。

鉴于所有这些证据,我投票支持在您的情况下使用宏方法以获得最大性能。

首先,宏方式对内存和if测试都有好处(尽管这确实是一个很小的成本)。您有任何特殊的场景可以使用调试变量吗?

为什么不在CPP文件中定义A::Test()?从而可以将全局预处理器报头移动到CPP文件。无论如何,我不认为在头中公开这样的调试细节是个好主意。

另一种选择是,如果你不喜欢有一堆#ifdef DEBUG/#endif行,你可以创建一个宏,在定义的情况下输出它的参数(很像断言宏)。

#ifdef DEBUG
#define PRINT(x) (x)
#else
#define PRINT(x)
#endif
PRINT( std::cout << "something" << std::endl );

代码路径不会比"compiled-away"小多少。但是,如果您愿意执行重构步骤,您可以以更大的可执行文件为代价,使运行时调试版本更便宜。

这个想法是将调试状态作为Test()重构版本的模板参数,这样它就可以在每次迭代中打印调试语句,也可以不打印。在false被传递到模板的情况下,编译器的死代码消除过程将优化掉该语句,因为模板参数将在模板扩展中被视为编译时间常数。

当然,完全优化的版本仍然可以使用条件编译来始终禁用调试输出。

template <bool Debug>
void TestDebug(){
  for(int i=0;i<1000000;i++){
    // do something
    if (Debug) print();
  }
}
void Test(){
#ifdef DEBUG
  if(m_debug) TestDebug<true>();
  else TestDebug<false>();
#else
  TestDebug<false>();
#endif
}