问题 使用宏构造#include指令的路径
我希望为我的程序的目标配置相关部分提供宏动态创建的包含文件路径。
例如,我想构建一个可以像这样调用的宏:
#include TARGET_PATH_OF(header.h)
这会扩展到这样的事情:
#include "corefoundation/header.h"
当为OSX配置源(在本例中)时
到目前为止,所有尝试都失败了我希望以前有人这样做过吗?
什么不起作用的例子:
#include <iostream>
#include <boost/preprocessor.hpp>
#define Dir directory/
#define File filename.h
#define MakePath(f) BOOST_PP_STRINGIZE(BOOST_PP_CAT(Dir,f))
#define MyPath MakePath(File)
using namespace std;
int main() {
// this is a test - yes I know I could just concatenate strings here
// but that is not the case for #include
cout << MyPath << endl;
}
错误:
./enableif.cpp:31:13: error: pasting formed '/filename', an invalid preprocessing token
cout << MyPath << endl;
^
./enableif.cpp:26:16: note: expanded from macro 'MyPath'
#define MyPath MakePath(File)
^
./enableif.cpp:25:40: note: expanded from macro 'MakePath'
#define MakePath(f) BOOST_PP_STRINGIZE(BOOST_PP_CAT(Dir,f))
^
/usr/local/include/boost/preprocessor/cat.hpp:22:32: note: expanded from macro 'BOOST_PP_CAT'
# define BOOST_PP_CAT(a, b) BOOST_PP_CAT_I(a, b)
^
/usr/local/include/boost/preprocessor/cat.hpp:29:36: note: expanded from macro 'BOOST_PP_CAT_I'
# define BOOST_PP_CAT_I(a, b) a ## b
^
1 error generated.
6704
2017-08-18 07:30
起源
答案:
我倾向于同意评论 utnapistim的回答 尽管你可以,但你不应该这样做。但实际上,您可以使用符合标准的C编译器。 [注1]
有两个问题需要克服。第一个是你不能使用 ##
运算符用于创建不是有效预处理程序标记的东西,并且路径名不符合有效的预处理程序标记,因为它们包括 / 和 。 字符。 (该 。 如果令牌以数字开头,那就没问题了 / 永远不会工作。)
你实际上并不需要连接令牌以便将它们与字符串串联起来 #
运算符,因为该运算符将字符串化整个宏参数,并且参数可能包含多个标记。但是,stringify尊重空格[注2],所以 STRINGIFY(Dir File)
不行;它会导致 "directory/ filename.h"
并且文件名中的无关空间将导致 #include
失败。所以你需要结合 Dir
和 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)
注意调用中的空格字符 PATH
将被保留。所以 Path(Dir, File)
将失败。
当然,你不需要复杂的 IDENT
宏如果你可以编写没有空格的串联。例如:
#define XSTR(x) #x
#define STR(x) XSTR(x)
#define Dir sys
#define File socket.h
#include STR(Dir/File)
笔记
我用clang,gcc和icc尝试了它 godbolt。我不知道它是否适用于Visual Studio。
更准确地说,它半空白:空白被转换为单个空格字符。
10
2017-08-18 16:10
答案:
我倾向于同意评论 utnapistim的回答 尽管你可以,但你不应该这样做。但实际上,您可以使用符合标准的C编译器。 [注1]
有两个问题需要克服。第一个是你不能使用 ##
运算符用于创建不是有效预处理程序标记的东西,并且路径名不符合有效的预处理程序标记,因为它们包括 / 和 。 字符。 (该 。 如果令牌以数字开头,那就没问题了 / 永远不会工作。)
你实际上并不需要连接令牌以便将它们与字符串串联起来 #
运算符,因为该运算符将字符串化整个宏参数,并且参数可能包含多个标记。但是,stringify尊重空格[注2],所以 STRINGIFY(Dir File)
不行;它会导致 "directory/ filename.h"
并且文件名中的无关空间将导致 #include
失败。所以你需要结合 Dir
和 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)
注意调用中的空格字符 PATH
将被保留。所以 Path(Dir, File)
将失败。
当然,你不需要复杂的 IDENT
宏如果你可以编写没有空格的串联。例如:
#define XSTR(x) #x
#define STR(x) XSTR(x)
#define Dir sys
#define File socket.h
#include STR(Dir/File)
笔记
我用clang,gcc和icc尝试了它 godbolt。我不知道它是否适用于Visual Studio。
更准确地说,它半空白:空白被转换为单个空格字符。
10
2017-08-18 16:10
我希望为我的程序的目标配置相关部分提供宏动态创建的包含文件路径。
你应该无法(如果你能够这样做,你可能不应该这样做)。
您正在有效地尝试在源文件中执行编译器的工作,这没有多大意义。如果要根据编译的机器更改包含路径,这是一个已解决的问题(但未在头文件中解决)。
规范解决方案:
在Makefile或CMakeLists.txt中使用IF,根据Visual Studio中的构建配置使用自定义属性页(或者只是为用户在OS环境中设置构建的特定设置)。
然后,将include指令写为:
#include <filename.h> // no path here
并且依赖于环境/构建系统,以便在调用编译器时使路径可用。
1
2017-08-18 07:45
根据你的描述,听起来你发现并非每一个 ""
是一个字符串。尤其是, #include "corefoundation/header.h"
看起来像一个普通的字符串但它不是。语法上,引用文本 外 预处理程序指令适用于编译器,并编译为以null结尾的字符串文字。预处理器指令中的带引号的文本由预处理器以实现定义的方式解释。
也就是说,你的例子中的错误是因为Boost粘贴了第二个和第三个标记: /
和 filename
。第一,第四和第五个令牌(directory
, .
和 h
)保持不变。显然,这不是你想要的。
依靠自动字符串连接更容易。 "directory/" "filename"
是与字符串相同的字符串 "directory/filename"
请注意,两个片段之间没有+。
0
2017-08-18 08:29
这适用于VS2013。 (它可以更简单,当然。)
#define myIDENT(x) x
#define myXSTR(x) #x
#define mySTR(x) myXSTR(x)
#define myPATH(x,y) mySTR(myIDENT(x)myIDENT(y))
#define myLIBAEdir D:\\Georgy\\myprojects\\LibraryAE\\build\\native\\include\\ //here whitespace!
#define myFile libae.h
#include myPATH(myLIBAEdir,myFile)
0
2017-08-25 07:07