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

Jor*_*eit 5 c macros include c-preprocessor

给定一个先前定义的宏:

#define FILENAME somefile.h
Run Code Online (Sandbox Code Playgroud)

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

#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)
Run Code Online (Sandbox Code Playgroud)

但是,这会导致错误(GCC4.9):

错误:粘贴“ /”和“文件”没有给出有效的预处理令牌

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

#define FILE2_(dir, file) STRINGIFY_(dir ## / ## file)
Run Code Online (Sandbox Code Playgroud)

由于相同的原因而无法正常工作。

我想知道这里出了什么问题,显然是如何避免这一点。

编辑:在哥伦布的建议下,将双下划线改为单打。显然,包含双下划线的标识符将保留给实现,而不管它们出现在什么地方(我的印象是,仅对ID开头的双下划线成立)。

Col*_*mbo 4

[cpp.include]/4:

表单的预处理指令

        # include pp-代币换行

(与前面两种形式之一不匹配)是允许的。include指令中后面的预处理标记的处理方式与普通文本中一样(即,当前定义为宏名称的每个标识符都被替换为预处理标记的替换列表)。如果所有替换后生成的指令与前面的两种形式之一不匹配,则行为未定义。152


152 请注意,相邻的字符串文字不会连接成单个字符串文字(参见 2.2 中的翻译阶段);因此,导致两个字符串文字的扩展是无效的指令。

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

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

对于类似对象和类似函数的宏调用,在重新检查替换列表以查找要替换的更多宏名称之前,将##删除替换列表中预处理标记的每个实例(不是来自参数),并连接前面的预处理标记带有以下预处理标记。[..]如果结果不是有效的预处理标记,则行为未定义。

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

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


对于 GCC,这很好:

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

#define ARG PATH FILE
#include ARG
Run Code Online (Sandbox Code Playgroud)

它之所以有效,是因为 GCC 预处理器删除了空白(出于某种原因)。不适用于 VC++ 或 Clang,并且不属于标准范围,因此绝对不推荐。