从宏__FILE__派生的文件包括文件是什么?

5

请看下面这个程序:

#include __FILE__
main(){}

预处理器陷入无限递归中,包括自身的副本和抱怨main()已经被定义。
如果我可以使用宏来包含文件,我能否根据__FILE__派生一个文件名并将其包含进来?
例如,在"foo.cpp"内部时,我想包含"foo.h",但是要从__FILE__派生它。 预处理器能做到吗?

1
你无法编辑文件名,相邻字符串字面值的连接发生在翻译的第6阶段,但预处理发生在第4阶段,因此你不能使用字符串连接。这意味着不可能构建一个可以被预处理器包含的文件名从__FILE__ - Jonathan Leffler
2个回答

6

C标准规定了三种形式的#include

#include <file>
#include "file"
#include ANYTHING ELSE

在前两种情况下,不会进行宏展开,因此无法改变行为。在第三种情况下,C99规定(§6.10.2p4):
引用:

指令中 #include 后的预处理标记 [将被宏扩展]。所有替换后的指令必须与之前两种形式之一匹配[脚注:注意相邻的字符串字面量不会连接成单个字符串字面量]。在 < 和 > 预处理标记对或一对 " 字符之间的预处理标记序列如何组合成单个头文件名预处理标记的方法是实现定义的。

C++98 §16.2p4 中出现了略有不同但实质上等效的措辞。
任何带有“shall”的句子都会强加硬性要求:在这种情况下,如果 ANYTHING ELSE 扩展为除以以 < 开头且以 > 结尾或以 " 开头和结尾的标记序列之外的任何内容,则程序是非法的。该标记序列的确切解释是实现定义的,但请注意,脚注明确禁止字符串字面量的连接。
因此,由于__FILE__的扩展是一个字符串常量,使用它在#include中的唯一方式是:
#include __FILE__

正如您发现的那样,这会导致无限递归。

#define LT <
#define GT >
#include LT __FILE__ etc GT

这会对我方便测试的所有编译器产生有趣但无用的影响。假设以上内容在名为test.c的文件中:

  • GCC尝试打开一个名为"test.c" etc的文件,引号和空格原样包含。
  • clang更加字面,寻找同一文件名但前后带有空格。
  • MSVC仅扩展LT宏(我认为这是一种合规违规行为),抱怨没有匹配的>,然后尝试打开名为__FILE__ etc GT的文件。

GCC的行为在此处有说明;其他情况需要自行解决。)

tl;dr:无法在预处理器内部完成您想要的操作。 我建议从构建系统中找出要包含的文件名称,并使用-D开关通知编译器(在类Unix系统上,您将需要双引号,-DINCLUDEME='"includeme.h"';我不会CMD)。


0
我能想到的最好的方案是这个:
#define foo(x) #x
#include foo(x)
prog.cpp:2:16: 错误:x:没有那个文件或目录

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接