【问题标题】:Expanding a single C pre-processor directive扩展单个 C 预处理器指令
【发布时间】:2013-12-16 13:01:20
【问题描述】:

我需要扩展一个预处理器指令,例如: 拥有一个源文件和两个标头,我只想从一个特定标头扩展一个 define,而保留所有其他 includedefine 不变。

主要思想是,给定类似这样的代码:

defs.h:

#define FOO(X,op) int X(int a,int b) { return a op b; }

other_file.h:

#define ONE 1
#define TWO 2
#define THREE 3
#define FOUR 4
#define FIVE 5

main.c:

"file: main.c "
#include <stdio.h>
#include "defs.h"
#include "other_file.h"
FOO(add,+)
FOO(sub,-)
FOO(mul,*)
FOO(div,/)

int main()

{

  printf("%d\n",add(ONE,TWO));
  printf("%d\n",sub(THREE,FOUR));
  printf("%d\n",mul(FIVE,FIVE));
  printf("%d\n",div(25,FIVE));
  return 0;
}

我会让 main.c 输出具有相同的包含,但将 FOO 扩展到创建的函数。我知道这个例子很傻,但我打算在更大的代码数据库上运行它。

这样做的动机是在宏中定义的函数中运行cccc。运行它的最简单方法是扩展这些宏。我也欢迎其他方法来做到这一点。

【问题讨论】:

  • 您不能这样做(除非对源文件进行大量的预处理)。我会用更简单的正则表达式方法来做到这一点(只需用你喜欢的脚本语言编写你自己的预处理器,perl?)
  • 好吧,我更喜欢python。将“函数”(使用正则表达式)放到临时文件中,运行预处理器并将其放回处理后的文件中的想法太丑陋了?
  • 不,没关系,但我不会覆盖原始文件。例如,您可以将源文件从 .C 重命名为 .raw.C,并且您的预处理器可以读取所有 .raw.C 文件以生成纯 .C 文件。如果你必须改变一些东西......

标签: c-preprocessor code-metrics


【解决方案1】:

您可以使用 GCC 的 -E-nostdinc-nostdinc++-fpreprocessed 参数。

对于您的示例,您可以运行:

gcc -E -nostdinc -fpreprocessed main.c

输出将是:

# 1 "main.c"
#include <stdio.h>
#include "defs.h"
#include "other_file.h"
FOO(add,+)
FOO(sub,-)
FOO(mul,*)
FOO(div,/)

int main()

{
  printf("%d\n",add(ONE,TWO));
  printf("%d\n",sub(THREE,FOUR));
  printf("%d\n",mul(FIVE,FIVE));
  printf("%d\n",div(25,FIVE));
  return 0;
}

如果标题不是那么复杂,就像在您的示例中那样,您可以强制 gcc 对整个文件进行预处理,即使缺少一些宏也是如此。例如:

cp other_file.h other_file.h_orig
echo "" > other_file.h
gcc -E -nostdinc main.c

输出:

# 1 "main.c"
# 1 "<command-line>"
# 1 "main.c"
main.c:1:19: error: no include path in which to search for stdio.h
 #include <stdio.h>
                   ^

# 1 "defs.h" 1
# 3 "main.c" 2
# 1 "other_file.h" 1
# 4 "main.c" 2
int add(int a,int b) { return a + b; }
int sub(int a,int b) { return a - b; }
int mul(int a,int b) { return a * b; }
int div(int a,int b) { return a / b; }

int main()

{
  printf("%d\n",add(ONE,TWO));
  printf("%d\n",sub(THREE,FOUR));
  printf("%d\n",mul(FIVE,FIVE));
  printf("%d\n",div(25,FIVE));
  return 0;
}

不过,它会删除包含的标头...并会在 std 标头上向您打印一个错误,该错误将发送到 stderr 而不是 stdout。

这适用于您的小示例,但在较大的代码库上您可能会遇到一些问题...

以下是手册(GCC 4.8.2)中参数的简要总结:

-E:预处理阶段结束后停止;不要正确运行编译器。输出采用预处理源代码的形式,其中 发送到标准输出。

-fpreprocessed:向预处理器指示输入文件已经被预处理。这会抑制宏之类的东西 扩展,三字符转换,转义换行拼接,和 大多数指令的处理。

-nostdinc:不要在标准系统目录中搜索头文件。只有您使用 -I 选项指定的目录。

-nostdinc++:不在 C++ 特定的标准目录中搜索头文件,但仍搜索其他标准 目录。

【讨论】:

    【解决方案2】:

    我们的DMS Software Reengineering Toolkit 及其C Front End 将执行此操作。

    DMS 提供通用程序解析/分析基础架构。 C 前端在此基础上提供全功能 C 前端,并配有 C 预处理器。

    通常,DMS C 预处理器的行为与标准预处理器类似:它对所有内容进行预处理,生成替换的标记流。不同寻常的是,它可以配置为不处理条件(这是全有或全无),或仅扩展指定的宏。特别是,它接受一个自定义 #pragma,声明宏应该(不)扩展。

    我不清楚这是否值得付出努力。是的,如果您认为宏应该是非透明的,那么度量工具可能会在某些宏被大量使用的地方产生更准确的答案。如果你认为宏本质上只是一个看起来很有趣的子程序,那么扩展这个宏就像内联一个函数体,你不会这样做来计算指标。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-04
      • 1970-01-01
      • 1970-01-01
      • 2011-05-16
      • 1970-01-01
      相关资源
      最近更新 更多