cpp : 如何理解 and/or 调试复杂的宏?

Cpp : How to understand and/or debug complex macros?

我正在尝试学习我发现不太容易的预处理器技巧 (Can we have recursive macros?, Is there a way to use C++ preprocessor stringification on variadic macro arguments?, C++ preprocessor __VA_ARGS__ number of arguments, Variadic macro trick, ...)。

我知道 -E 选项可以查看预处理器整个过程的结果,但我想知道是否存在选项或方法来逐步查看结果。确实,有时很难理解当宏调用调用宏的宏时会发生什么......具有禁用上下文,涂蓝色的机制......简而言之,我想知道是否有一种带有断点和其他的预处理器调试器存在工具。

(不要回答说这种预处理器指令的使用是危险的、丑陋的、可怕的,在 C 中不是好的做法,会产生不可读的代码……我知道这一点,这不是问题)。

是的,此工具作为 Eclipse IDE 的一项功能存在。我认为访问该功能的默认方法是将鼠标悬停在您想要展开的宏上(这将显示完整的展开),然后按键盘上的 F2(出现一个弹出窗口,允许您逐步执行每个扩张)。

当我使用这个工具来了解更多关于宏的知识时,它非常有帮助。只要稍加练习,你就不再需要它了。

如果有人对如何使用此功能感到困惑,我在 Eclipse 文档中找到了教程 here

查看您的宏有什么问题的唯一方法是添加编译完成时将保留临时文件的选项。对于 gcc,它是 -save-temps 选项。您可以打开 .i 文件和展开的宏。

IDE 索引器(如 Eclipse)不会有太大帮助。在错误发生之前,它们不会扩展(如其他答案所述)宏。

这个 answer 与另一个问题相关。

当你使用奇怪的预处理器技巧(这是合法的)时,要求编译器生成预处理形式(例如,如果使用 GCC,则使用 gcc -C -E)并查看该预处理形式是很有用的.

实际上,对于源文件 foo.c,(有时)使用 gcc -C -E foo.c > foo.i 获取其预处理形式 foo.i 并查看 foo.i 是有意义的。

有时,在没有线路信息的情况下获得 foo.i 甚至是有意义的。这里的技巧(删除以 # 开头的行中包含的行信息)是:

gcc -C -E foo.c | grep -v '^#' > foo.i

然后你可以indent foo.i并编译它,例如与 gcc -Wall -c foo.i;您将在预处理文件中获得错误位置,您可以了解如何获得它并返回到您的预处理器宏(或它们的调用)。

记住 C preprocessor is mostly a textual transformation working at the file level. It is not possible to macro-expand a few lines in isolation (because prior lines might have played with #if combined with #define -perhaps in prior #include-d files- or preprocessor options such as -DNDEBUG passed to gcc or g++). On Linux see also feature_test_macros(7)

一个已知的扩展示例是 assert,它在编译时有或没有 -DNDEBUG 传递给编译器时工作不同。 assert(i++ > 0) 的含义(一个非常错误的代码)取决于它并说明 macro-expansion 不能在本地完成(你可能想象一些先前的 header 甚至 #define NDEBUG 1如果当然是品味不佳)。

宏扩展依赖于上下文的另一个示例(实际上非​​常常见)是使用 __LINE____COUNTER__ 的任何宏 ...

注意。您不需要 Eclipse 来完成所有这些,足够好了 source code editor (my preference is emacs 但这是个人喜好问题):对于预处理任务,您可以使用编译器。