【问题标题】:Standard way for writing a debug mode in C++用 C++ 编写调试模式的标准方法
【发布时间】:2015-02-26 08:40:37
【问题描述】:

在调试模式下的代码中是否有“最佳实践”或类似的编码? 例如,

#include <iostream>

int main()
{
    #ifdef MY_DEBUG_DEF
    std::cout << "This is only printed if MY_DEBUG_DEF is defined\n";
    #endif

    return 0;
}

或者这被认为是不好的做法,因为代码变得有点混乱? 我注意到一些库(例如 libcurl,它是一个大型且知名的库)具有此功能;如果你用 libcurl 定义 VERBOSE,你基本上会得到一个调试模式

谢谢。

【问题讨论】:

    标签: c++ debugging


    【解决方案1】:

    更常用的方法是遵循 assert(3) 的约定:使用 #ifndef NDEBUG .... #endifcode 进行包装,这仅对调试有用,并且没有任何明显的副作用。

    您甚至可以添加一些调试打印宏,例如

    extern bool wantdebug;
    #ifndef NDEBUG
    #define OUTDEBUG(Out) do { if (wantdebug) \
       std::cerr << __FILE__ << ":" << __LINE__ \
                 << " " << Out << std::endl; \
    } while(0)
    #else
    #define OUTDEBUG(Out) do {}while(0)
    #endif
    

    并在代码中的适当位置使用 OUTDEBUG("x=" &lt;&lt; x) 之类的东西。然后wantdebug 标志将通过调试器设置,或者通过一些程序参数。您可能想要发出换行符并刷新cerr(或cout,或您自己的调试输出流)-使用std::endl ...-以立即显示调试输出(因此您的程序将来崩溃会仍然给出合理的调试输出)。

    【讨论】:

    • 这对cout 来说是有意义的(尽管我更喜欢将编写换行符和刷新缓冲区的无关操作分开;endl 经常被误解,我只是避免它),但是@ 987654332@ 设置了unitbuf,因此无论如何它都会在每个输出操作上刷新。
    • 我仍然相信endl 是有意义的。您当然希望每条调试消息都在自己的行中!
    【解决方案2】:

    这是一种可接受的方法。是的,它变得一团糟,但它也使调试变得更加容易。另外,您通常可以折叠这些东西,从而消除混乱。

    正如您所提到的,libcurl 使用该方法。我以前也有一位为惠普工作的老师在他们的打印机软件上工作,他们使用了相同的方法。

    【讨论】:

    • 好的,从你的回复我得出的结论是这是可以接受的:)
    【解决方案3】:

    我个人更喜欢启用运行时的日志记录。这意味着您不必重新编译即可获得“调试输出”。所以我有一个命令行参数-v=n,其中n 默认为零,并在实际程序中存储在变量verbosity 中。使用-v=1 我可以获得基本跟踪(代码的基本流程),使用-v=2 我可以获得“更多内容”(例如选定函数中的内部状态转储)。两个级别通常就足够了,但有时三个级别可能会很好。另一种方法是使verbosity 成为位模式,并根据设置的位启用/禁用某些功能 - 因此设置bit 0 用于基本跟踪,bit 1 在某些模块中提供额外信息,位 2 提供额外跟踪在另一个模块中,等等。如果您想真正花哨,您可以使用名称,例如-trace=all_basic-trace=detailed-trace=module_A-trace=all_basic,module_A,module_B 或类似名称。

    将此与宏结合起来:

     #define TRACE do { if (verbosity > 0) \ 
           std::cout << __FILE__ << ":" << __LINE__ << ":" \
                     << __PRETTY_FUNCTION__ << std::endl; } while(0)
    

    对于可能需要大量额外时间的事情,例如验证大型复杂数据结构(树、链表等)的正确性,那么在该代码周围使用#ifndef NDEBUG 将是一件好事。当然,假设您相信您永远不会在发布版本中搞砸。

    真正的 livig 代码在这里: https://github.com/Leporacanthicus/lacsap/blob/master/trace.h https://github.com/Leporacanthicus/lacsap/blob/master/trace.cpp 例如在这里使用: https://github.com/Leporacanthicus/lacsap/blob/master/expr.cpp

    (请注意,一些经常被调用的简单函数没有“TRACE” - 它只会使跟踪变得混乱并使其太长)

    【讨论】:

    • 我建议你删除while(0)之后的;,这样你就可以写TRACE;,这是一个指令。
    • 确实,这就是链接源所具有的 - 在这里输入不同的变体时只是简单的错字......
    【解决方案4】:

    使用记录器可能会更好,例如

    上述方法非常灵活,但重量级。您也可以实现一些简单的宏:

    #ifdef NDEBUG
    #define DBG(FMT, ...)
    #else // !NDEBUG
    #define DBG(FMT, ...) fprintf (stderr, FMT, ## __VA_ARGS__)
    #endif // NDEBUG
    

    以上是来自Macros with a Variable Number of Arguments.的GCC语法 对于VC,另请参阅How to make a variadic macro (variable number of arguments)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-30
      • 1970-01-01
      • 2023-04-04
      • 2010-10-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多