2010-08-23 104 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路线,并简单地取缔这些有趣的案例。如果你不能做到这一点,那么大锤子是有道理的。