我倾向于同意评论乌特纳皮斯蒂姆的回答即使你可以这样做,你也不应该这样做。但事实上,您可以使用符合标准的 C 编译器。 [注1]
There are two issues to overcome. The first one is that you cannot use the ##
operator to create something which is not a valid preprocessor token, and pathnames do not qualify as valid preprocessor tokens because they include / and . characters. (The . would be ok if the token started with a digit, but the / will never work.)
实际上,您并不需要连接标记即可将它们与#
运算符,因为该运算符将字符串化整个宏参数,并且该参数可能包含多个标记。然而,stringify 尊重空白[注 2],所以STRINGIFY(Dir File)
行不通;这将导致"directory/ filename.h"
文件名中的多余空格将导致#include
失败。所以你需要连接Dir
and File
没有任何空格。
下面通过使用类似函数的宏来解决第二个问题,该宏仅返回其参数:
#define IDENT(x) x
#define XSTR(x) #x
#define STR(x) XSTR(x)
#define PATH(x,y) STR(IDENT(x)IDENT(y))
#define Dir sys/
#define File socket.h
#include PATH(Dir,File)
Warning:(感谢@jed 传递了这个问题。)如果连接的字符串包含在其他地方定义为宏的标识符,则此处将发生意外的宏替换。应小心避免这种情况,特别是如果Dir
and/or File
不受控制(例如,通过在编译器调用中定义为命令行参数)。
您还需要注意,某些实现可能会定义可能以类似标记的方式显示在文件路径中的单词。例如,GCC 可以定义具有如下名称的宏unix
and linux
除非使用显式 C 标准(不是默认标准)调用它。这可能是由类似的路径触发的platform/linux/my-header.h
甚至linux-specific/my-header.h
.
为了避免这些问题,如果您使用此技巧,我建议您:
另外,您不需要复杂的IDENT
宏,如果你可以编写没有空格的串联。例如:
#define XSTR(x) #x
#define STR(x) XSTR(x)
#define Dir sys
#define File socket.h
#include STR(Dir/File)
Notes
-
我用 clang、gcc 和 icc 尝试过,可以在godbolt。我不知道它是否适用于 Visual Studio。
-
更准确地说,它半尊重空白:空白被转换为单个空格字符。