首页 > 解决方案 > 用空实现替换“定义宏”

问题描述

我通常使用#define宏来添加代码,这些代码将在编译为调试时而不是在编译为发布时出现。例如:

#ifdef NDEBUG
# define LOG(msg) (void)msg
#else
# define LOG(msg) MyDebugLogger(msg)
#endif

取而代之的是,我正在考虑使用普通函数,只是不为 release 方法提供主体:

void MyDebugLogger(std::string const& msg);

在 MyDebugLogger.cpp 中:

void MyDebugLogger(std::string const& msg)
{
#ifdef NDEBUG
    std::clog << msg << "\n"; // Or whatever
#else
    (void)msg;
#endif
}

我期望编译器将有能力剥离调用并且不会在 Release 中增加额外的成本。我对么?出于某种原因,这可能是一种不好的做法吗?

编辑:我的问题是:如果我像以前一样使用宏,我知道在发布模式下,可执行文件会更小更快,因为所有代码都已被删除。如果我使用该功能,它会一样吗?正如编译器可能理解的那样,该函数什么都不做,也没有必要。(或者它会增加一个额外的,甚至很小的,用于调用一个空函数)

标签: c++optimization

解决方案


你的例子应该可以工作,稍微调整一下。在您当前的版本中,编译器仅“看到”函数签名并将发出对其符号的调用,该符号稍后将通过链接器解决,因此它无法自行优化它。(链接时间优化可能对此有所帮助,但这在很大程度上取决于您的设置,而动态链接将使这成为不可能)。所以也许在标题中尝试这样的事情:

// Assuming you are using clang or gcc,
// but is required to not give an error by the standard and probably
// not even needed.
[[gnu::always_inline]] 
void MyDebugLogger(std::string const& msg [[maybe_unused]])
{
  #ifdef NDEBUG
    MyDebugLoggerImplementation(msg);
  #endif
}

然后在你的 .cpp 文件中实现它。此方法的另一个好处是您的方法需要使用 NDEBUG 编译 Logger,而此方法为客户端代码提供了选择。


推荐阅读