观察以下程序:
#include __FILE__
main(){}
预处理器陷入无限递归,包括自身内部的副本并抱怨main()
已被定义。
如果我可以使用宏来包含文件,我可以根据它派生文件名__FILE__
并包含它吗?
例如,我想包含"foo.h"
while inside "foo.cpp"
,但从__FILE__
.
可以用预处理器完成吗?
C标准规定了三种形式#include
:
#include <file>
#include "file"
#include ANYTHING ELSE
在前两种情况下,不会发生宏扩展,因此无法改变行为。在第三种情况下,C99 说(§6.10.2p4):
指令中的预处理标记
#include
是[宏扩展]。所有替换后产生的指令应匹配前两种形式之一[脚注:请注意,相邻的字符串文字不会连接成单个字符串文字]。将 < 和 > 预处理标记对或一对 " 字符之间的一系列预处理标记组合成单个标头名称预处理标记的方法是实现定义的。
略有不同但实际上等效的措辞出现在 C++98 §16.2p4 中。
任何带有“shall”的句子都提出了一个硬性要求:在这种情况下,如果ANYTHING ELSE
扩展为除了以 开头<
和结尾>
或以 开头和结尾的标记序列之外的任何内容,则程序是非良构的"
。该标记序列的确切解释是实现定义的,但请注意脚注特别禁止字符串文字连接。
因此,由于 的扩展__FILE__
是一个字符串常量,在 an 中使用它的唯一方法#include
是
#include __FILE__
正如您所发现的,这会导致无限递归,并且
#define LT <
#define GT >
#include LT __FILE__ etc GT
这对我可以方便地测试的所有编译器都有有趣但无用的影响。假设以上内容在一个名为的文件中test.c
:
"test.c" etc
,其中包含引号和空格。LT
扩展(我认为这是违反一致性的观点),抱怨没有匹配的>
,然后尝试打开一个名为__FILE__ etc GT
.(GCC 的行为记录在这里;其他任何事情你都靠自己。)
tl; dr:没有办法从预处理器内部做你想做的事。我建议从您的构建系统中计算出要包含的文件的名称,并使用-D
开关通知编译器(在 Unixy 系统上,您需要双引号,-DINCLUDEME='"includeme.h"'
;我不会说 CMD)
我想出的最好的是:
#define foo(x) #x
#include foo(x)
prog.cpp:2:16: 错误: x: 没有这样的文件或目录