【问题标题】:how to avoid many #ifdef for logging statements如何避免许多#ifdef 记录语句
【发布时间】:2013-10-31 16:50:41
【问题描述】:

在我们的应用程序中,我们创建了自己的日志系统。在这个日志系统中有几种不同的日志类型,调试、错误、警告、通信、性能等等。有很多 #ifdef#endif 可以禁用特定的日志类型。这些 #ifdef#endif 使代码难以阅读。

我们正在考虑删除这些 #ifdef#endif 并在将消息写入文件之前进行检查。这意味着对日志系统有很多“useless”调用。这些调用不会导致任何写作活动。

没有这些 #ifdef#endif 是否有一种更好 的方法来打开/关闭日志类型>AND 这些“无用”的电话?

【问题讨论】:

  • 你能举例说明你的意思吗?您是指每种日志记录类型 1 个 ifdef,还是其他?也许一些显示 ifdefs 的代码会有所帮助......
  • 你不能用#ifdef ENABLE_WARN \n #define WARN(x) printf(x) \n #else \n #define WARN(x) \n #endif之类的东西把所有的ifdefs包含在一个地方吗?
  • @cluracan 不是 100% 肯定,但我的猜测是 OP 正试图避免到处都有大量 #ifdef DEBUG ... #endif
  • 您可以使用 const 全局日志级别并为每种类型(即 if(log_lvl
  • @DennisMeng 你的意思是他将 ifdef 放入代码中?!在这种情况下,只需在定义“log_error/warning/communication”宏的头文件中执行一次 ifdef,该宏要么什么都不做,要么根据日志级别记录?

标签: c++ c logging


【解决方案1】:

下面的呢:

// comment out if not needed
#define ENABLE_LOG

#ifdef ENABLE_LOG
#  define LOG(x) x
#else
#  define LOG(x) (void) 0 
#endif

稍后你可以打电话:

LOG(mylogger.call());

按照 Dietrich Epp 的建议更新了 #else 部分。

【讨论】:

  • 我推荐define LOG(x) (void) 0 用于else 分支,因为它不会为多余的分号生成编译器警告。
  • 为什么我们需要 #else 部分?
  • @5YrsLaterDBA 否则,如果您没有启用日志记录,对 LOG(mylogger.call()); 的调用将返回 LOG 未定义的错误
【解决方案2】:

除了运行良好的#define 解决方案之外,我还想介绍一个使用模板的替代方案

template<bool B>
void log(std::string message){}

template<>
void log<true>(std::string message){log_internal(message);}

#define DEBUG true
#define COMMUNICATION false

...

log<DEBUG>("this message will be logged");
log<COMMUNICATION>("this message won't");

#define 解决方案确实适用于大多数情况,但使用此解决方案有一些原因:

  • 您可能需要范围界定 - 即不要让您的 log 设备弄脏全局命名空间。此解决方案可以放在namespace 中,而#define 则不能。

  • 您可能需要更严格地控​​制可以做什么和不可以做什么。 LOG(x) 定义的问题是任何东西都可以放在x 中 - 如果关闭日志记录,您将看不到任何问题。

为了明确 - 您的代码可能会编译和使用

LOG(std::cout << "Here!" << endl);

因为此特定日志已关闭。但是从现在起 2 年后的某一天,有人会打开日志记录并在整个地方得到 endl undefined 错误。或者更糟 - 他可能会发现突然打开日志记录需要链接到一个早已不复存在的库(因为该日志记录专门调用了此库中定义的函数),或者使用了一个很久以来更改接口的函数(甚至被完全删除!真实故事:()

编辑

我被要求将这个添加到答案中:

在您不记录的情况下(空函数),似乎存在函数调用开销。当编译器对其进行优化时,情况并非如此。如果您想确定这一点,请在函数中添加 inline 指令。

此外,您可能希望将其从 std::string 更改为 const char *,以确保不调用字符串构造函数 - 但编译器也应自动优化。

无论如何,就像我说的那样,这本质上并不比#define 解决方案更好。实际上,我在我的项目中仍然使用#define - 但在某些特定情况下,此模板解决方案更可取。

【讨论】:

  • 关闭后不会再调用log函数吗?即使实际功能什么也不做。在 #define 方法中,当它在关闭状态下编译时,LOG 行就像什么都没有。
  • @OnlyJoe 不,不会。编译器认为它是一个空函数,所以它什么也不做。如果你真的想要,你可以添加inline 指令以确保考虑。
  • @cluracan 你能把内联指令添加到你的答案中吗?
猜你喜欢
  • 1970-01-01
  • 2018-12-23
  • 1970-01-01
  • 2016-02-19
  • 1970-01-01
  • 2011-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多