【问题标题】:Custom gcc preprocessor自定义 gcc 预处理器
【发布时间】:2021-11-27 21:03:42
【问题描述】:

你能给我一个编写自定义 gcc 预处理器的例子吗?

我的目标是用适当的 CRC32 计算值替换 SID("foo") 类似的宏。对于任何其他宏,我想使用标准 cpp 预处理器。

使用-no-integrated-cpp -B 选项似乎可以实现此目标,但我找不到任何简单的使用示例。

【问题讨论】:

  • 这不是在回答您的问题,但您是否考虑过编写一个支持脚本来获取您的模板文件(例如 example.c.tmpl),计算 CRC 并替换为输出文件(例如 example.c)作为您制作过程的一部分?
  • 在我的情况下这很不方便,因为我没有特殊的模板,这些 SID 宏可以在源代码中的任何位置。当然,我可以添加一个自定义 Make 目标,它使用一个简单的脚本更改 SID 宏来处理所有 *.cpp 源,然后将损坏的源传递给 gcc....但我认为使用自定义预处理器可能更优雅。跨度>
  • 您是否尝试过标准方式:#undef SID #define SID ?
  • 我认为您应该更仔细地重新阅读最初的帖子。这个问题不能用标准的预处理器来解决。

标签: gcc c-preprocessor


【解决方案1】:

一种方法是使用program transformation system,在进行编译之前将 SID 宏调用“重写”只是您想要的,将预处理器的其余处理留给编译器本身.

我们的DMS Software Reengineering Toolkit 就是这样一个系统,它可以应用于包括C 在内的多种语言,特别是GCC 2/3/4 系列编译器。

要使用 DMS 实现这个想法,您可以使用其 C front end 运行 DMS 在编译步骤之前覆盖您的源代码。 DMS 可以解析代码无需扩展预处理器指令,构建 表示它的抽象语法树,对 AST 进行转换,然后将结果输出为可编译的 C 文本。

您将使用的具体转换规则是:

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

其中 ComputeCRC32 是执行其所说的自定义代码。 (DMS 包含一个 CRC32 实现,因此用于此的自定义代码非常短。

DMS 是完成这项任务的好帮手。您可以使用 PERL 来实现一些非常相似的东西。与 PERL(或其他一些字符串匹配/替换 hack)的区别在于 a) 它可能会在您想要替换的地方找到模式,例如

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

您可以通过仔细编码模式匹配来解决此问题,b) 无法匹配在令人惊讶的情况下发现的 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 路线,然后简单地取缔有趣的案例。如果你不能做到这一点,那么大锤子就有意义了。

【讨论】:

    【解决方案2】:

    警告:危险且丑陋的黑客攻击。现在闭上你的眼睛您可以通过在 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 -- $@"
    
    /usr/lib/gcc/i486-linux-gnu/4.3/cc1 $@
    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
    

    此示例调用原始预处理器,但会打印附加消息和参数。您可以使用自己的预处理器替换脚本。

    糟糕的黑客攻击已经结束。你现在可以睁开眼睛了。

    【讨论】:

    • 嗯...如果我错了,请纠正我,但我认为使用这种方法可以替换 SID 宏,将结果保存到某个临时文件,然后将标准预处理器应用于该临时文件。没有?
    • @pachanga 是的,您需要提取输入和输出文件的命令行选项,并为处理器的输出编写第二个临时文件(我相信您需要保留文件扩展名)。然后通过修补输入文件参数将处理后的文件作为输入文件传递给 THE ORIGINAL(TM) 预处理器。但保留所有其他参数,因为它们中的一些是位置相关的(如 -I、-D 或 -U)。在 THE ORIGINAL(TM) 预处理器完成后,您清理临时文件并留下 THE ORIGINAL(TM) 预处理器的退出代码。
    • 轻微改进:你可以自动找到相应的预处理器会像这样运行:g++ --print-prog-name=cc1plus 所以你的过滤器变成:#!/bin/sh echo "Own special preprocessor ; args = $@" $(g++ --print-prog-name=cc1plus) $@ exit $?
    • 更好的是exec $(${COLLECT_GCC} --print-prog-name=cc1) $@
    猜你喜欢
    • 2011-01-14
    • 1970-01-01
    • 2017-06-02
    • 2020-04-07
    • 1970-01-01
    • 2014-09-13
    • 2011-04-24
    • 2021-10-10
    相关资源
    最近更新 更多