可以在 C/C++ include 指令中使用环境变量吗?
Can you use environment variables in C/C++ include directives?
假设我有这样的文件夹布局:
.
+-- Project
+-- src
+-- foo.h
+-- foo.cpp
+-- test
+-- test_foo.c
而 test_foo.c
看起来是这样的:
#include "../src/foo.h"
#include <stdio.h>
#include <assert.h>
int main() {
assert(foo() == true);
printf("Test complete");
return 0;
}
有没有办法用指向源目录的变量替换行 #include "../src/foo.h"
?例如,假设在我的环境中我有一个变量:
PROJECT_SRC="./Project/src/"
然后我可以这样包含指令:
#include "PROJECT_SRC/foo.h"
这会很好,因为我可以有一个 bash 脚本来导出某个项目所需的所有路径。此外,如果该文件包含在不同的测试和构建文件中,我将不得不为每个文件设置相对路径(尽管工作量不大),这比一个绝对路径更不可靠。
替代方法可能是像 CMake
这样可以执行此操作的工具。或者这被认为是不好的做法?
Weeelll...有点可能,但它并不漂亮,而且有一些缺陷。通常最好在构建系统中添加包含路径,例如(假设普通 make
):
# C PreProcessor flags. This variable is used by make's implicit rules for
# everything preprocessor-related.
CPPFLAGS += -I$(PROJECT_PATH)
和#include
headers 没有源文件中的路径。这将使 make
使用 -Iyour/project/path
调用编译器,这将使编译器在 your/project/path
中查找 headers。也就是说,在Makefile中你可以有
PROJECT_PATH = foo/bar
CPPFLAGS = -I$(PROJECT_PATH)
并在来源中
#include "foobar.h"
达到#include "foo/bar/foobar.h"
.
的效果
...另外,我是否看到您尝试 #include
源文件而不是 headers?不要走那条路;在那条路上,疯狂就在于。单独编译源文件,然后 link 按照通常的方式将它们一起编译,除非您有 确实 不这样做的充分理由。
所以,我看不出为什么要在代码的 #include
指令中直接引用项目路径;构建系统方面的唯一变化只是您必须传递 -DPROJECT_PATH=foo/bar/
而不是 -IPROJECT_PATH=foo/bar/
并且该构造比实际为此类内容设计的机制更脆弱。但如果你真的想这样做,那么方法如下:
您 运行 遇到的第一个问题是
#include "foo/bar/" "baz.h" // no dice.
是ill-formed,所以简单的方法就出来了。我们必须尝试预处理器魔法,它是这样工作的:
#define HEADER_STRING(s) #s
#define HEADER_I(path, name) HEADER_STRING(path ## name)
#define HEADER(path, name) HEADER_I(path, name)
// v-- important: no spaces allowed here!
#include HEADER(PROJECT_PATH,foobar.h)
也许从下往上开始:
#define HEADER_STRING(s) #s
从它的参数中生成一个字符串。也就是说,HEADER_STRING(foo/bar/baz.h)
扩展为"foo/bar/baz.h"
。值得注意的是,宏参数不会扩展,因此即使定义了宏 PROJECT_PATH
,HEADER_STRING(PROJECT_PATH)
也会扩展为 "PROJECT_PATH"
。这是您 运行 在尝试使用预处理器执行任何复杂操作时遇到的最常见问题之一,解决方案是添加另一个可以扩展参数的层:
#define HEADER_STRING_I(s) #s
#define HEADER_STRING(s) HEADER_STRING_I(s)
...我们在 HEADER_STRING
中不需要这个,但在 HEADER
中使用它,所以请记住这个技巧。恐怕精确的预处理器替换规则有些神秘,详细解释它们超出了 SO 答案的范围。简而言之,宏是分层展开的,当宏不展开时,技巧通常是给它们一个展开的地方,即添加另一层。
HEADER_I
然后,
#define HEADER_I(path, name) HEADER_STRING(path ## name)
将它的参数打包在一起并传递给 HEADER_STRING
。 HEADER_I(foo,bar)
扩展为 HEADER_STRING(foobar)
。因为我上面提到的问题,HEADER_I(PROJECT_PATH,foobar.h)
扩展为HEADER_STRING(PROJECT_PATHfoobar.h)
,进而扩展为"PROJECT_PATHfoobar.h"
,所以我们需要另外一层对PROJECT_PATH
进行扩展:
#define HEADER(path, name) HEADER_I(path, name)
这只是增加了path
和name
参数扩展的地方。最后,用 PROJECT_PATH
#define
d 到 foo/bar/
,HEADER(PROJECT_PATH,foobar.h)
扩展为 "foo/bar/foobar.h"
,然后我们可以说
#include HEADER(PROJECT_PATH,foobar.h)
到#include "foo/bar/foobar.h"
。 PROJECT_PATH
然后可以在 makefile 中设置并与 -DPROJECT_PATH=$(some_make_variable)
一起传递。
最后一个陷阱是,您必须注意不要让任何 spaces 在标记之间滑动。
#include HEADER(PROJECT_PATH,foobar.h)
最终扩展为 "foo/bar/ foobar.h"
(注意 space),这是行不通的。
假设我有这样的文件夹布局:
.
+-- Project
+-- src
+-- foo.h
+-- foo.cpp
+-- test
+-- test_foo.c
而 test_foo.c
看起来是这样的:
#include "../src/foo.h"
#include <stdio.h>
#include <assert.h>
int main() {
assert(foo() == true);
printf("Test complete");
return 0;
}
有没有办法用指向源目录的变量替换行 #include "../src/foo.h"
?例如,假设在我的环境中我有一个变量:
PROJECT_SRC="./Project/src/"
然后我可以这样包含指令:
#include "PROJECT_SRC/foo.h"
这会很好,因为我可以有一个 bash 脚本来导出某个项目所需的所有路径。此外,如果该文件包含在不同的测试和构建文件中,我将不得不为每个文件设置相对路径(尽管工作量不大),这比一个绝对路径更不可靠。
替代方法可能是像 CMake
这样可以执行此操作的工具。或者这被认为是不好的做法?
Weeelll...有点可能,但它并不漂亮,而且有一些缺陷。通常最好在构建系统中添加包含路径,例如(假设普通 make
):
# C PreProcessor flags. This variable is used by make's implicit rules for
# everything preprocessor-related.
CPPFLAGS += -I$(PROJECT_PATH)
和#include
headers 没有源文件中的路径。这将使 make
使用 -Iyour/project/path
调用编译器,这将使编译器在 your/project/path
中查找 headers。也就是说,在Makefile中你可以有
PROJECT_PATH = foo/bar
CPPFLAGS = -I$(PROJECT_PATH)
并在来源中
#include "foobar.h"
达到#include "foo/bar/foobar.h"
.
...另外,我是否看到您尝试 #include
源文件而不是 headers?不要走那条路;在那条路上,疯狂就在于。单独编译源文件,然后 link 按照通常的方式将它们一起编译,除非您有 确实 不这样做的充分理由。
所以,我看不出为什么要在代码的 #include
指令中直接引用项目路径;构建系统方面的唯一变化只是您必须传递 -DPROJECT_PATH=foo/bar/
而不是 -IPROJECT_PATH=foo/bar/
并且该构造比实际为此类内容设计的机制更脆弱。但如果你真的想这样做,那么方法如下:
您 运行 遇到的第一个问题是
#include "foo/bar/" "baz.h" // no dice.
是ill-formed,所以简单的方法就出来了。我们必须尝试预处理器魔法,它是这样工作的:
#define HEADER_STRING(s) #s
#define HEADER_I(path, name) HEADER_STRING(path ## name)
#define HEADER(path, name) HEADER_I(path, name)
// v-- important: no spaces allowed here!
#include HEADER(PROJECT_PATH,foobar.h)
也许从下往上开始:
#define HEADER_STRING(s) #s
从它的参数中生成一个字符串。也就是说,HEADER_STRING(foo/bar/baz.h)
扩展为"foo/bar/baz.h"
。值得注意的是,宏参数不会扩展,因此即使定义了宏 PROJECT_PATH
,HEADER_STRING(PROJECT_PATH)
也会扩展为 "PROJECT_PATH"
。这是您 运行 在尝试使用预处理器执行任何复杂操作时遇到的最常见问题之一,解决方案是添加另一个可以扩展参数的层:
#define HEADER_STRING_I(s) #s
#define HEADER_STRING(s) HEADER_STRING_I(s)
...我们在 HEADER_STRING
中不需要这个,但在 HEADER
中使用它,所以请记住这个技巧。恐怕精确的预处理器替换规则有些神秘,详细解释它们超出了 SO 答案的范围。简而言之,宏是分层展开的,当宏不展开时,技巧通常是给它们一个展开的地方,即添加另一层。
HEADER_I
然后,
#define HEADER_I(path, name) HEADER_STRING(path ## name)
将它的参数打包在一起并传递给 HEADER_STRING
。 HEADER_I(foo,bar)
扩展为 HEADER_STRING(foobar)
。因为我上面提到的问题,HEADER_I(PROJECT_PATH,foobar.h)
扩展为HEADER_STRING(PROJECT_PATHfoobar.h)
,进而扩展为"PROJECT_PATHfoobar.h"
,所以我们需要另外一层对PROJECT_PATH
进行扩展:
#define HEADER(path, name) HEADER_I(path, name)
这只是增加了path
和name
参数扩展的地方。最后,用 PROJECT_PATH
#define
d 到 foo/bar/
,HEADER(PROJECT_PATH,foobar.h)
扩展为 "foo/bar/foobar.h"
,然后我们可以说
#include HEADER(PROJECT_PATH,foobar.h)
到#include "foo/bar/foobar.h"
。 PROJECT_PATH
然后可以在 makefile 中设置并与 -DPROJECT_PATH=$(some_make_variable)
一起传递。
最后一个陷阱是,您必须注意不要让任何 spaces 在标记之间滑动。
#include HEADER(PROJECT_PATH,foobar.h)
最终扩展为 "foo/bar/ foobar.h"
(注意 space),这是行不通的。