【问题标题】:C Macro - Dynamic #includeC 宏 - 动态 #include
【发布时间】:2011-08-17 22:33:10
【问题描述】:

我试图弄清楚如何使用 GCC 为 #include 语句构建变量字符串。

我的想法是,对于我编写的每个源模块,我都希望包含一个在构建过程早期创建的动态生成的 C 源作为标头。

生成此文件不是问题。不幸的是,包括它。

到目前为止我所拥有的是(identities.h):

// identities.h

# define PASTER2(str)  #str
# define PASTER(str)   PASTER2(str ## .iden)
# define EVALUATOR(x)  PASTER(x)

# define IDENTITIES_FILE EVALUATOR(__FILE__)
# include IDENTITIES_FILE

理想情况下,这将像这样使用(main.c):

//main.c

# include "identities.h"

int main() {return 0;}

在编译到 yield 之前,预处理器会在一次传递中对其进行扩展:

//main.c (preprocessed)

# include "main.c.iden"

int main() {return 0;}

我使用的两个间接级别(PASTER 和 EVALUATOR)是 this 帖子的结果。

不幸的是,这不起作用,我留下了错误:

obj/win32/dbg/main.o
In file included from main.c:1:0:
identities.h:42:1: error: #include expects "FILENAME" or <FILENAME>

我认为问题在于 include 语句缺少引号。有什么想法吗?

【问题讨论】:

  • 如果你有一个“动态生成的 C 源代码”,那么你能动态生成一个包含所有你想要包含的头文件的新头文件吗?虽然我仍然很想知道是否有可能做你想做的事。
  • 简短回答:你不能这样做,期间。
  • @leet:不,这个问题需要一种绝对简约的方法来修改当前的构建系统。已经同意我们将尝试这样做,通过编辑每个文件以包含单个通用头文件“identities.h” - 并且只包含一个文件。
  • @AlexandreC。奇怪,我能做到。我不能存在于你的宇宙中;-)。我认为你的意思是你不能用 standard C/C++ 做到这一点。

标签: c macros include c-preprocessor


【解决方案1】:

这实际上是在 Linux 源代码树中完成的;见line 100 of compiler-gcc.h

#define __gcc_header(x) #x
#define _gcc_header(x) __gcc_header(linux/compiler-gcc##x.h)
#define gcc_header(x) _gcc_header(x)
#include gcc_header(__GNUC__)

我试图弄清楚如何使用 GCC 为 #include 语句构建变量字符串。

此标记将__GNUC__ 的值粘贴到字符串中; "linux/compiler-gcc" __GNUC__ ".h" 然后将结果字符串化。这可能是一个 gcc 预处理器扩展。

这是一个例子,

t1.h

#define FOO 10

t2.h

#define FOO 20

交流

#ifndef VERSION
#define VERSION 1
#endif
#define __gcc_header(x) #x
#define _gcc_header(x) __gcc_header(t##x.h)
#define gcc_header(x) _gcc_header(x)
#include gcc_header(VERSION)
#include <stdio.h>

int main(void)
{
        printf("FOO is %d\n", FOO);
        return 0;
}

这里有两个编译,

g++ -o a a.cc
g++ -DVERSION=2 -o a a.cc

任一编译的输出都给出了预期的结果。

与 Linux 源代码一样,您可以关闭 gcc 预定义值。 echo | g++ -dM -E - 会给出一个列表。

对于您的情况,您可以使用 makefile 将定义传递给编译器,以允许在不更改源代码的情况下动态包含生成的标头。但是一个简单的替代方法是在模板源文件上运行sed 等并将其替换为已知的包含名称。

这两种技术都适用于生成测试夹具等。但是,对于编译器功能发现,这是一种更好的方法。对于使用 IDE 的程序员来说,这可能是他们唯一的选择。

【讨论】:

  • 您当然不能像其他人指出的那样使用__FILE__。在 C++ 和更新的 C 中,它是一个指针而不是字符串文字(根据 arelius)。
  • Linux 合并文件,现在只需使用预处理器条件进行检查。这是link to an older version。另一种可能性是新 GCC 和 clang 中的 has_include extentions
【解决方案2】:

我相当肯定你不能做你想做的事,__FILE__ 返回一个字符串,## 对令牌起作用,并且没有 CPP 字符串 concat 预处理器宏。通常这是因为两个字符串连续,例如

"Hello" " World"

将被 C++ 解析器视为单个字符串,但 #include 是预处理器的一部分,因此无法利用这一事实。

旧答案:

你为什么这样做

{ #str, str ## .iden }

我确定这不是预处理器语法,您希望通过它实现什么?您是否尝试过:

str ## .iden

“{”可以解释您遇到的错误。

【讨论】:

【解决方案3】:

Boost Preprocessor library 中的 BOOST_PP_STRINGIZE 怎么样。它专门用于在名称周围添加引号。

【讨论】:

  • 不幸的是,这只是在文本前添加一个磅:“#define BOOST_PP_STRINGIZE_I(text) #text”
  • BOOST_PP_STRINGIZE 不只是添加英镑#。该文档特别指出,与仅使用 # 不同,此宏实际上允许扩展其参数(而 # 不允许)。经过几次测试,您的主要问题似乎是 FILE 已扩展为已在引号之间的文件名,并且无法删除它们(至少,据我所知)。
  • @Mikael:你说得对,我自己也得出了这个结论。
  • 我建议您使用构建系统(例如 cmake)或自制脚本来解决此类问题。这就是 Qt 为 .ui 文件生成头文件的方式。
  • 是的,我已经使用 GCC 和 Make:gcc -c -DEVALUTOR_ARGUMENT=$
【解决方案4】:

暂时跳过整个包含语法,我不明白您的代码试图做什么。你说:

# define PASTER(str)  { #str, str ## .iden }

你给它main.c并期待"main.c.iden",但它返回{"main.c", main.c.iden }

您是在寻找这个吗?

#define PASTER2(str) #str
#define PASTER(str) PASTER2(str ## .iden)

【讨论】:

  • 你是对的,我已经改变了它,但不幸的是它产生了同样的错误..
【解决方案5】:

你不能像这样使用预处理器。您必须为#include 指令提供文件名,它不能是其他宏。

【讨论】:

  • 我知道这是不正确的(搜索“Computed Includes”):tigcc.ticalc.org/doc/cpp.html
  • @JT 这是 GCC 预处理器的一个“特性”——它不是用于预处理的 C 或 C++ 标准的一部分。
  • @JT,实际上他说得很对,计算的包含是 GCC 扩展,您没有将问题标记为 GCC-only。
  • 抱歉没有标记,尽管我确实在问题中声明我使用的是 GCC。
  • C 标准 --- C99 标准实际上允许“计算包含”(或其某种形式)。请参阅 6.10.2/4 ... "# include pp-tokens newline",其中 pp-token 必须扩展为 &lt;header&gt;"header-file" 之一
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
  • 2013-04-09
  • 2023-03-30
  • 2012-12-10
相关资源
最近更新 更多