[英]Tool for tracing C preprocessor execution during macro expansion?
有沒有辦法逐步打印,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)
這就是我在任何實用程序來測試擴展 C/C++ #define 宏之類的問題的答案中找到的內容? .
當有人要求查看宏的“擴展”時, gcc -E
似乎是一個有效的答案。 我正在尋找保真度更高的東西,我已經知道gcc -E
。
我正在編寫 ISO C11 代碼,但我包含了C++
標簽,以防該生態系統中有與此相關的工具或技術。
我希望讀到這篇文章的人可能是一個編譯器作者,他做過或看過類似的工作(編譯器跟蹤選項?),或者編寫了這樣的工具,或者他們的搜索結果比我幸運得多。 或者,如果您密切關注所有的 C 語言產品,並且相對確定這不存在,那么我會發現一個否定的答案也有幫助,盡管我很好奇為什么 C 預處理器會已經存在了幾十年,因其“陷阱”而臭名昭著,但仍然從未見過一種工具(或過程)來拉開預處理器的帷幕。 (我希望這真的存在。手指交叉)
我建議找到一個高質量的編譯器/預處理器並編輯預處理器。
我會避免 GCC 和 clang,因為它們太重了 IMO。 我會看看 libfirm 中的 cparser,特別是這個文件: https : //github.com/libfirm/cparser/blob/master/src/parser/preprocessor.c
來自 libfirm 的代碼非常易於閱讀和編輯,並且幾乎不需要時間來構建項目——與 LLVM/clang 或 GCC 形成了粗略的對比。
到目前為止,它已經吃掉了我扔給它的所有 C99 代碼。
順便說一下,我沒有附屬,我只是覺得它很搖滾! 我剛剛使用的代碼效果很好,並在 IRC 頻道#firm @ freenode 上獲得了極好的支持、幫助和指導。
編輯:
Linux 中的內核管理員團隊使用的 Sparse 也很容易被黑客攻擊。 它還包括一個 c 預處理器: https : //github.com/chrisforbes/sparse
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.