C 预处理器:构建路径字符串

C preprocessor: building a path string

给定一个之前定义的宏:

#define FILENAME somefile.h

我想将它与定义该文件(相对)路径的另一个宏字符串连接起来。我目前的做法是这样做:

#define DIRECTORY ../somedir/

#define STRINGIFY_(x) #x
#define FILE2_(dir, file) STRINGIFY_(dir ## file)
#define FILE_(dir, file) FILE2_(dir, file)

#include FILE_(DIRECTORY, FILENAME)

然而,这会导致错误 (GCC4.9):

error: pasting "/" and "file" does not give a valid preprocessing token

DIRECTORY 定义中删除最后的正斜杠可以消除此错误,但显然不会产生所需的结果。当我尝试以其他方式走私 / 时,会出现类似的错误。例如:

#define FILE2_(dir, file) STRINGIFY_(dir ## / ## file)

出于同样的原因不起作用。

我想知道这里出了什么问题,显然,我想知道如何避免这种情况。

编辑:根据 Columbo 的建议将双下划线更改为单下划线。显然,包含双下划线的标识符保留给实现,无论它们出现在哪里(我的印象是这只适用于 ID 开头的双下划线)。

[cpp.include]/4:

A preprocessing directive of the form

        # include pp-tokens new-line

(that does not match one of the two previous forms) is permitted. The preprocessing tokens after include in the directive are processed just as in normal text (i.e., each identifier currently defined as a macro name is replaced by its replacement list of preprocessing tokens). If the directive resulting after all replacements does not match one of the two previous forms, the behavior is undefined.152


152 Note that adjacent string literals are not concatenated into a single string literal (see the translation phases in 2.2); thus, an expansion that results in two string literals is an invalid directive.

因此,尽管 #include MACRO 有效,但 MACRO 必须直接扩展为 #include 的有效参数。字符串文字的连接在预处理后发生两个翻译阶段。

此外,在 ## 运算符的定义中,[cpp.concat]/3:

For both object-like and function-like macro invocations, before the replacement list is reexamined for more macro names to replace, each instance of a ## preprocessing token in the replacement list (not from an argument) is deleted and the preceding preprocessing token is concatenated with the following preprocessing token. [..] If the result is not a valid preprocessing token, the behavior is undefined.

因此A##B的结果必须是一个个有效的预处理令牌。 / 是一个自己的预处理标记,目录和文件的名称也是如此。
您不能连接 "abc/xyz",因为 abc/ 不是有效的预处理标记 - "abc 不是一个预处理标记,而是两个,尽管 "abc"是一个。
另一方面,如果你连接 <abc/xyz>,那么 /xyz 被连接,检查,我们又遇到问题了。

因此似乎不可能使用 ## 连接路径。你的方法对我来说也是不可能的。


使用 GCC,这很好:

#define PATH <foo/bar/
#define FILE boo>

#define ARG PATH FILE
#include ARG

works 因为 GCC 的预处理器删除了白色 space (出于某种原因)。不适用于 VC++ 或 Clang,并且不在标准范围内,因此绝对不推荐。