2010-08-23 103 views
13

請給我一個編寫自定義gcc預處理器的例子嗎?自定義gcc預處理器

我的目標是用合適的CRC32計算值替換SID(「foo」)相似的宏。對於任何其他宏,我想使用標準的cpp預處理器。

看起來可以使用-no-integrated-cpp -B選項來實現此目標,但是我找不到任何簡單的使用示例。

+4

這不會回答你的問題,但是你有沒有考慮編寫一個支持腳本來接受你的模板文件(例如'example.c.tmpl'),計算CRC並替換成一個輸出文件(例如'example.c' )作爲你製作過程的一部分? – 2010-08-23 08:36:17

+1

在我的情況下,這是不方便的,因爲我沒有特殊的模板,這些SID宏可以在源代碼中的任何地方。當然,我可以添加一個自定義的Make目標,它通過一個簡單的腳本更改SID宏來處理所有* .cpp源,然後將損壞的源傳遞給gcc ....但我認爲使用自定義預處理器可能更優雅。 – pachanga 2010-08-23 11:13:38

+0

您是否試過標準方式: #undef SID #define SID ? – 2010-08-28 07:29:49

回答

21

警告:危險和醜陋的黑客。現在關閉你的眼睛你可以通過在gcc命令行中添加'-no-integrated-cpp'和'-B'開關來掛鉤你自己的預處理器。 '-no-integrated-cpp'表示在它使用其內部搜索路徑之前,gcc在其「預處理器」的'-B'路徑中搜索。如果使用'-E'選項調用'cc1','cc1plus'或'cc1obj'程序(這些是C,C++和Objective-c編譯器),則可以識別預處理器的調用。當你看到這個選項時,你可以做你自己的預處理。如果沒有'-E'選項,則將所有參數傳遞給原始程序。當有這樣的選項時,你可以做自己的預處理,並將操縱的文件傳遞給原始編譯器。

它看起來像這樣:

> cat cc1 
#!/bin/sh 

echo "My own special preprocessor -- [email protected]" 

/usr/lib/gcc/i486-linux-gnu/4.3/cc1 [email protected] 
exit $? 

> chmod 755 cc1 
> gcc -no-integrated-cpp -B$PWD x.c 
My own special preprocessor -- -E -quiet x.c -mtune=generic -o /tmp/cc68tIbc.i 
My own special preprocessor -- -fpreprocessed /tmp/cc68tIbc.i -quiet -dumpbase x.c -mtune=generic -auxbase x -o /tmp/cc0WGHdh.s 

此示例調用原來的預處理器,而是輸出額外的消息和參數。您可以用自己的預處理器替換腳本。

糟糕的黑客已經結束。你現在可以睜開你的眼睛。

+0

嗯...糾正我,如果我錯了,但我認爲使用這種方法,我可以替換SID宏,將結果保存到一些臨時文件,然後將標準預處理器應用於此臨時文件。沒有? – pachanga 2010-09-10 20:26:56

+0

@pachanga是的,您需要提取輸入和輸出文件的命令行選項,併爲處理器的輸出寫入第二個tempfile(我相信您需要保留文件擴展名)。然後通過修改輸入文件參數將處理後的文件作爲輸入文件傳遞給THE ORIGINAL(TM)預處理器。但保留所有其他參數,因爲它們中的一些參數依賴於位置(如-I,-D或-U)。在ORIGINAL(TM)預處理器完成之後,您將清理您的臨時文件,並使用THE ORIGINAL(TM)預處理器的退出代碼離開。 – Rudi 2010-09-13 07:05:06

+3

略有改善:可以自動找到對應 預處理器將像這樣已經運行: G ++ --print-PROG-NAME = cc1plus 所以,你的過濾器變: #/ bin/sh的 回聲「自己的特殊preprocessor; args = $ @「 $(g ++ --print-prog-name = cc1plus)$ @ exit $? – blais 2011-09-02 19:53:05

2

的一種方法是使用一個program transformation system,爲「改寫」 只是的SID宏調用你想要什麼,你做編譯之前,離開預處理器處理,以編譯器本身的其餘部分。

我們的DMS Software Reengineering Toolkit是一個這樣的系統,可以應用於包括C語言在內的許多語言,特別是GCC 2/3/4系列編譯器。

要使用DMS實現此想法,您需要在編譯步驟之前在源代碼上運行DMS,其代碼爲C front end 。 DMS可以解析代碼而不擴展預處理器指令,構建 代表它的抽象語法樹,在AST上執行轉換,然後將結果作爲可編譯C文本吐出。

你會使用

具體轉換規則是:

rule replace_SID_invocation(s:STRING):expression->expression 
      = "SID(\s)" -> ComputeCRC32(s); 

其中ComputeCRC32是自定義的代碼,不會說話是算數的。 (DMS包含一個CRC32實現,所以這個自定義代碼很短,

DMS對於這個任務來說是一個很大的錘子,你可以使用PERL來實現一些非常相似的東西,它與PERL(或者其他一些字符串匹配/替換黑客)是這樣的風險:a)它可能會發現模式某處不需要想要替換,例如

... QSID("foo")... // this isn't a SID invocation 

,你也許可以通過仔細編碼的模式匹配修復,B)不匹配suprising情況下發現了一個SID電話:

... SID (/* master login id */ "Joel") ... // need to account for formatting and whitespace 

和c)無法處理各種逃生在字面字符串本身中出現的字符:

... SID("f\no\072") ... // need to handle all of GCC's weird escapes 

DMS的C前端爲您處理所有逃脫;上面的ComputeCRC32函數將看到包含實際預期字符的字符串,而不是您在源代碼中看到的原始文本。

所以它的確是一個關心你是否關心黑暗角落的案例,或者如果你認爲你可能有更多的特殊處理要做。

考慮到您描述問題的方式,我會非常想嘗試去Perl路線,並簡單地取締這些有趣的案例。如果你不能做到這一點,那麼大錘子是有道理的。