是否有一种方法一步一步地打印,C预处理器在扩展宏时正在做什么?
例如,我会给它一些C语言文本(例如:.h文件)进行预处理。为了演示起见,下面是一个简单的示例:
// somefile.h
#define q r
#define bar(x,z) x ## z
#define baz(y) qux ## y
#define foo(x,y) bar(x, baz(y))到目前为止,这仅仅是为了建立一个定义表。
接下来是详细展开的文本。对于本演示,我期望工作流/流程/输出如下所示:
$ magical_cpp_revealer somefile.h
Please enter some preprocessor text to analyse:
> foo(baz(p),q)
Here are the resulting preprocessor calculations:
,----.----.---------------------------.-----------------------------------------
|Step|Exp#| Expression | Reason
|====|====|===========================|=========================================
| 00 | 00 | foo(baz(p),q) | Original tokens.
| 01 | | | Definition found for 'foo': `foo(x,y)` = "bar(x, baz(y))"
| 02 | 01 | bar(x, baz(y)) | 'foo' begins expansion. Original tokens shown.
| 03 | | | 'foo' Stage 1: Raw parameter replacements elided: no # or ## operators present.
| 04 | | | 'foo' Stage 2: Stringification elided: no # operators present.
| 05 | | | 'foo' Stage 3: Concatenation elided: no ## operators present.
| 06 | | | 'foo' Stage 4: Argument scan begins.
| 07 | | | Argument for parameter 'x' is "baz(p)"
| 08 | 02 | baz(p) | Scanning "baz(p)" for macros to expand.
| 09 | | | Definition found for 'baz': `baz(y)` = "qux ## y"
| 10 | 03 | qux ## y | 'baz' begins expansion. Original tokens shown.
| 11 | 04 | qux ## p | 'foo->baz' Stage 1: Raw parameter replacements performed
| 12 | | | using 'y' = "p".
| 13 | | | 'foo->baz' Stage 2: Stringification elided: no # operators present.
| 14 | 05 | quxp | 'foo->baz' Stage 3: Concatenation performed.
| 15 | | | 'foo->baz' Stage 4: Argument scan elided: no parameters present.
| 16 | | | 'foo->baz' Stage 5: Expansive parameter replacements elided: no parameters present.
| 17 | | | 'foo->baz' Stage 6: Rescan begins
| 18 | | | No definition for 'quxp'
| 19 | | | 'foo->baz' Stage 6: Rescan concludes.
| 20 | 06 | quxp | 'baz' concludes expansion. Final result shown.
| 21 | | | 'foo' Stage 4: Argument scan continues.
| 22 | | | Currently:
| 23 | | | 'x' = "quxp"
| 24 | | | 'y' = To Be Determined
| 25 | | | Argument for parameter 'y' is "q"
| 26 | 07 | q | Scanning "q" for macros to expand.
| 27 | | | Definition found for 'q': `q` = "r"
| 28 | 08 | r | 'q' begins expansion. Original tokens shown.
| 29 | | | 'foo->q': Stage 1: Concatenation elided: no ## operators present.
| 30 | | | 'foo->q': Stage 2: Scan begins.
| 31 | | | No definition for 'r'
| 32 | | | 'foo->q': Stage 2: Scan concludes.
| 33 | 09 | r | 'q' concludes expansion. Final result shown.
| 34 | | | 'foo' Stage 4: Argument scan concludes.
| 35 | 10 | bar(x, baz(y)) | 'foo': Reminder of current token sequence.
| 36 | 11 | bar(quxp, baz(r)) | 'foo' Stage 5: Expansive parameter replacements performed
| 37 | | | using 'x' = "quxp",
| 38 | | | and 'y' = "r".
| 39 | | | 'foo' Stage 6: Rescan begins
| 40 | | | Definition found for 'bar': `bar(x,z)` = "x ## z"
| 41 | 12 | x ## z | 'bar' begins expansion. Original tokens shown.
| 42 | 13 | quxp ## baz(r) | 'foo->bar' Stage 1: Raw parameter replacements performed
| 43 | | | using 'x' = "quxp",
| 44 | | | and 'z' = "baz(r)".
| 45 | | | 'foo->bar' Stage 2: Stringification elided: no # operators present.
| 46 | 14 | quxpbaz(r) | 'foo->bar' Stage 3: Concatenation performed.
| 47 | | | 'foo->bar' Stage 4: Argument scan elided: no parameters present.
| 48 | | | 'foo->bar' Stage 5: Expansive parameter replacements elided: no parameters present.
| 49 | | | 'foo->bar' Stage 6: Rescan begins
| 50 | | | No definition for 'quxpbaz'
| 51 | | | No definition for '('
| 52 | | | No definition for 'r'
| 53 | | | No definition for ')'
| 54 | | | 'foo->baz' Stage 6: Rescan concludes.
| 55 | 15 | quxpbaz(r) | 'bar' concludes expansion. Final result shown.
| 56 | | | 'foo' Stage 6: Rescan concludes
| 57 | 16 | quxpbaz(r) | 'foo' concludes expansion. Final result shown.
'----'----'---------------------------'-----------------------------------------(附带说明和对未来读者的警告:我手工编写了上面的跟踪,它可能不是100%正确的,至少在表示预处理程序的工作方式方面是这样。)
请注意,我不仅试图说明预处理器对应该做什么的积极决定(例如:当它找到定义并开始扩展时),而且还演示了它关于不做什么的负面决定(例如:当令牌没有定义或#+##运算符不存在时)。这听起来可能有点具体,但对于理解为什么预处理器没有做我希望它做的事情很重要,通常是一个平淡无奇的结论,比如“我弄错了定义或标记”,或者“我忘了包括那个文件”。
如果有一种方法可以揭示MSVC的CL.EXE在使用“传统的预处理器”逻辑来扩展宏时的想法,我会更放心。
下面是一个没有回答问题的例子:
$ gcc -E somefile.h
...
quxpbaz(r)这就是我在Any utility to test expand C/C++ #define macros?这样的问题的答案中所发现的。
当有人要求查看宏的“扩展”时,gcc -E似乎是一个有效的答案。我正在寻找更高忠诚度的东西,而且我已经知道了gcc -E。
我正在编写C11代码,但我将包括C++标记,以防在该生态系统中存在与此相关的工具或技术。
我希望读到这篇文章的人可能是曾经做过或看到过类似工作的编译器作者(编译器跟踪选项?),或者写过这样的工具,或者他们的搜索结果比我幸运得多。或者,如果你关注所有的C语言产品,并且相对确定它不存在,那么我也会发现一个负面的答案也是有帮助的,尽管我会好奇为什么C预处理器会出现几十年,因为它的“陷阱”而声名狼藉,但仍然从未见过一个工具(或过程)来拉开预处理器的帷幕。)我希望这是真的存在。(祈祷)
发布于 2021-02-19 08:28:39
我建议找到一个高质量的编译器/预处理器,并编辑预处理器。
我要避免GCC和clang,因为它们太重了。我想看看libfirm的c解析器,特别是这个文件:https://github.com/libfirm/cparser/blob/master/src/parser/preprocessor.c。
libfirm的代码非常容易阅读和编辑,而且几乎不用花时间构建这个项目--这与LLVM/clang或GCC形成了大致的对比。
到目前为止,它已经吃掉了我抛给它的所有C99代码。
顺便说一句,我不是附属的,我只是觉得它很棒!我刚刚使用代码取得了很好的效果,并在IRC频道@ freenode上得到了极好的支持、帮助和指导。
编辑:
Linux中内核看门人团队使用的稀疏也很容易被黑客攻击。它还包括一个c-预处理程序:https://github.com/chrisforbes/sparse。
https://stackoverflow.com/questions/64569769
复制相似问题