【问题标题】:Using the C Preprocessor for languages other than C将 C 预处理器用于 C 以外的语言
【发布时间】:2009-07-08 18:37:41
【问题描述】:

C Preprocessor 的维基百科条目指出:

预处理器的语言 指令与语法无关 C,所以 C 预处理器也可以 可独立用于处理其他 文件类型。

如何做到这一点?有什么例子或技巧吗?

编辑:是的,我最感兴趣的是宏处理。即使它可能不可取或不可维护,但知道什么是可能的仍然很有用。

【问题讨论】:

  • 预处理器需要做什么?如果只需要宏处理,可以使用m4:gnu.org/software/m4
  • 哇,m4?我的意思是.. 认真的吗?我认为这对大多数人来说是一件很容易忘记的事情。现在用在什么地方?我知道的一件事是,它用于编译 sendmail 的 .cf 文件。
  • m4 可能在你运行 configure 时仍然可以使用;制作;进行安装。仔细查看配置。
  • 我对 m4 的第一次介绍是它被用于为 gEDA 设计软件制作电子设备电路板封装设计。我认为它仍在那个套件中使用(恕我直言,它是最好的套件之一,并且是开源的)。
  • @dwhall 我昨天听从了你和其他人的建议,并使用 M4 为我正在进行的项目实施了 GL ES/金属混合着色器。在让它工作的过程中,我有两个认识: 1. 我以前使用过 M4(用于 Sendmail),但我讨厌它; 2. 与 C 预处理器等更简单且广泛使用的工具和 ERB 等现代更强大的工具相比,M4 是一个过时的、笨拙的垃圾。 dnl?严重地?我不能提供一个标志来从非输出生产线中去除空格吗?不要让我开始尝试让float3→vec 和float3(1.0)→vec3(1.0) 工作。

标签: c-preprocessor


【解决方案1】:

您可以直接调用CPP:

cpp <file>

而不是通过 gcc 调用它:

gcc -E filename

但请注意,正如在同一篇 Wikipedia 文章中所提到的,C 预处理器的语言并没有真正适合通用用途:

但是,由于 C 预处理器没有其他一些功能 预处理器,例如递归宏,根据 引用,条件中的字符串评估和图灵 完整性,与更通用的宏相比,它非常有限 处理器,例如 m4。

您是否考虑过使用更灵活的宏处理语言,例如前面提到的 m4?

【讨论】:

  • +1 参考m4。传统上,这一直是复杂任务的首选预处理器。相比之下,CPP 非常原始。
  • CPP 无法真正让您“开心”。另一方面,m4 是一个享受的雷区;只需要克服最初的痛苦。
  • m4 看起来确实很有趣,感谢所有提到这一点的人。
  • 只要确保你不在标准 C/C++ 代码中使用 m4 宏,否则后面的编码器会讨厌你的胆量。 ;-)
【解决方案2】:

例如,汇编程序。虽然许多汇编程序都有自己的方式来#include 头文件和#define 宏,但为此使用 C 预处理器会很有用。例如,GNU make 有通过运行预处理器 ('cpp') 将 *.S 文件转换为 *.s 文件的隐含规则,然后将 *.s 文件提供给 GNU 汇编器 ('as')。

【讨论】:

  • 虽然根据文件名的单个字母的大小写是一个糟糕的设计选择......我在其他项目中做了同样的事情,我使用了 .asm 或 . src 用于“真实”源代码,标准 .s 用于 cpp 发出的代码。不幸的是,有许多开发人员(甚至使用 Gnu 工具)生活在无法单独区分名称的文件系统上。例如,NTFS 默认保留大小写,但不区分大小写。
  • 我没有说这是一个智能的选择。 ;-)
  • 实际上 GNU Make 并不通过 C 预处理器(至少不是直接)运行它,而是通过 C 编译器 $(CC) 运行它,这种方法适用于区分大小写的文件系统,因为它没有t 需要两个名称相似的文件。
  • @Joe D:两个帐户都错了。隐含的 make 规则是“to make N.s from N.S”,调用是$(CPP) $(CPPFLAGS)。 (GNU 制作手册,隐式规则目录。)
【解决方案3】:

是的,可以通过 gcc 预处理器(例如“gcc -E”)解析您自己的语言来完成。

我们已经用我们的特定语言完成了我的工作。它有很多优点:

  • 您可以使用非常强大的 C 的包含语句 (#include)
  • 您可以使用您的#ifdef 结构
  • 您可以定义常量 (#define MAGIC_NUMBER 42) 或宏函数 (#define min(x,y) ( (x(

...以及 c 处理器中的其他内容。

但是,您也继承了不安全的 C 结构,并且没有与您的主要语言集成的预处理器是它的原因。考虑最小宏并执行以下操作:

a = 2;
b = 3;

c = min(a--, b--);

想想在 min 函数之后 a 和 b 会有什么值?

您引入的非类型常量也是如此

详情请参阅Safer C 书籍。

【讨论】:

  • 是您的源代码示例...2,因为后缀增量/减量运算符很烂,而不是因为与 C 的宏预处理器有关的任何事情。与 JavaScript 一样,存在许多“C: The Bad Parts”,postfix-++/-- 就是一个很好的例子。我的建议是忘记它们的存在,只使用前缀-++/--(例如min(--a, --b); 和for (int i = 0; i &lt; count; ++i))。
  • @SlippD.Thompson 我不同意。虽然后缀递增和递减的行为是...... 独特的, 对于需要在递增当前值的同时拥有旧值的情况,例如推入/弹出堆栈,它仍然可以用作语法糖. *stack++ = myvar/myvar = *--stack。此外,您在该宏中放入 what 并不重要,无论如何,行为都会与函数不同。您的示例扩展为(纠正 Roalt 在 min 宏中的拼写错误)( (--a) &lt; (--b) ? (--a) : (--b) ),或(假设从左到右评估)1 &lt; 2 ? 0 : 1,这是错误的。
  • #define MIN(a, b) ({ typeof(a) _a = a; typeof(b) _b = _b; _a > _b ? _a : _b;})
【解决方案4】:

许多 C 编译器都有一个标志,告诉它们只进行预处理。对于 gcc,它是 -E 标志。例如:

$ gcc -E -                 
#define FOO foo
bar FOO baz

将输出:

# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "<stdin>"

bar foo baz

对于其他 C 编译器,您必须查看手册以了解如何切换到仅预处理模式。

【讨论】:

  • 你可能想让你的例子真正做点什么。
【解决方案5】:

通常,您可以调用 C 编译器并选择仅预处理(并忽略任何 #line 语句)。举个简单的例子:

<?php
function foo()
{
#ifdef DEBUG
    echo "Some debug info.";
#endif
    echo "Foo!";
}

foo();

我们用预处理语句定义一个 PHP 源文件。然后我们可以对其进行预处理(gcc 也可以这样做):

cl -nologo -EP foo.php > foo2.php

由于DEBUG 没有被定义,第一个echo 被删除。另外,以 # 开头的行是 PHP 中的 cmets,因此您不必为“调试”构建对它们进行预处理。

编辑:因为您询问了宏。这也很好用,可用于生成样板代码等。

【讨论】:

  • 真的有人在做这种事吗?
  • @Geo,是的。例如,Perl 有(或曾经有)一个命令行选项来通过 CPP 运行 perl 脚本,然后再让解析器处理它。如果您只需要一些 #ifdef 处理来打开和关闭代码块或 #include 来共享源代码行,那么 cpp 实际上是一个不错的工具选择。
【解决方案6】:

使用微软的编译器,我认为(我只是查了一下,还没有测试过)它是/P compiler option。

其他编译器可能具有类似的选项(或者,对于某些编译器,预处理器实际上可能是不同的可执行文件,通常由编译器隐式运行,但您也可以单独显式运行)。

【讨论】:

    【解决方案7】:

    假设您使用的是 GCC,您可以获取任何普通的旧文本文件,无论其内容如何,​​然后运行:

    gcc -E 文件名

    文件中的任何预处理器指令都将由预处理器处理,然后 GCC 将退出。

    关键是文本文件的实际内容是什么并不重要,因为预处理器只关心它自己的指令。

    【讨论】:

      【解决方案8】:

      我听说有人在 Ada 代码上使用 C 预处理器。 Ada has no preprocessor,所以如果你想预处理你的代码,你必须做类似的事情。

      然而,不给它一个是一个有意识的设计决定,所以这样做是非常不符合 Ada 的。我不建议任何人这样做。

      【讨论】:

        【解决方案9】:

        不久前,我在一个使用imake 生成makefile 的项目上做了一些工作。我记得,它基本上是生成 make 文件的 c 预处理器语法。

        【讨论】:

          【解决方案10】:

          C 预处理器也可以在编译 Haskell 代码之前由 Glasgow Haskell Compiler (GHC) 调用,方法是传递 -cpp 标志。

          【讨论】:

            【解决方案11】:

            您可以在编译器中为另一种语言实现 C 预处理器。

            您可以使用它来预处理任何类型的文本文件,但为此目的还有更好的东西。

            【讨论】:

              【解决方案12】:

              基本上它的意思是预处理器与 C 语法无关。它们基本上是遵循一组规则的简单解析器。所以你可以使用预处理器,就像你使用 sed 或 awk 来完成一些愚蠢的任务一样。不过不要问我为什么要这样做。

              例如,在文本文件中:

              #define pi 3.141
              
              pi is not an irrational number.
              

              然后你运行预处理器&你会得到。

              3.141 不是一个无理数。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2021-12-04
                • 1970-01-01
                • 1970-01-01
                • 2013-06-13
                • 2010-11-19
                • 2011-02-22
                • 2016-09-16
                • 1970-01-01
                相关资源
                最近更新 更多