你可以在C / C ++ include指令中使用环境variables吗?

说我有一个文件夹布局如此:

. +-- 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; } 

有没有办法用指向源目录的variablesreplace#include "../src/foo.h"行? 例如,在我的环境中说我有一个variables:

 PROJECT_SRC="./Project/src/" 

然后我可以有这样的include指令:

 #include "PROJECT_SRC/foo.h" 

这将是很好,因为我可以有一个bash脚本,导出我需要一个特定的项目的所有path。 另外,如果文件包含在不同的testing和构build文件中,我将不得不为每个文件设置相对path(尽pipe不是很多工作),这将比绝对path更不稳定。

另一种方法可能是像CMake这样的工具。 或者这被认为是不好的做法?

Weeelll …这是可能的,但不是很好,它有一些陷阱。 在构建系统中添加包含路径通常会更好,例如(假定为纯make ):

 # C PreProcessor flags. This variable is used by make's implicit rules for # everything preprocessor-related. CPPFLAGS += -I$(PROJECT_PATH) 

#include包含源文件中没有路径的头文件。 这将使编译器使用-Iyour/project/path编译,这将使编译器在your/project/path查找标题。 也就是说,在Makefile中你可以拥有

 PROJECT_PATH = foo/bar CPPFLAGS = -I$(PROJECT_PATH) 

并在来源

 #include "foobar.h" 

具有#include "foo/bar/foobar.h"

…还有,我看到你尝试#include源文件,而不是头文件? 不要走那条路; 走在疯狂的路上。 除非你有一个非常好的理由,否则分别编译源文件并按通常的方式将它们链接起来。

所以,我不明白为什么要在代码中的#include指令中直接引用项目路径; 在构建系统方面唯一的改变只是你必须传递-DPROJECT_PATH=foo/bar/而不是-IPROJECT_PATH=foo/bar/而构造比实际为这类东西设计的机制更脆。 但如果你真的想这样做,那么这里是如何:

你碰到的第一个问题是

 #include "foo/bar/" "baz.h" // no dice. 

是不合格的,所以简单的办法就出来了。 我们必须尝试预处理器的魔法,它的工作原理是这样的:

 #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_PATHHEADER_STRING(PROJECT_PATH)也会展开为"PROJECT_PATH" 。 这是您尝试执行任何与预处理器相关的任何操作时遇到的最常见的问题之一,解决方案是添加另一个可以扩展参数的图层:

 #define HEADER_STRING_I(s) #s #define HEADER_STRING(s) HEADER_STRING_I(s) 

…我们不需要这个HEADER_STRING ,但是它用在HEADER ,所以记住这个技巧。 恐怕精确的预处理器替换规则有些神秘,详细解释它们超出了答案的范围。 简而言之,宏是分层展开的,当宏不扩展的时候,诀窍通常是给它们一个扩展的地方,即添加另一个层。

HEADER_I那么,

 #define HEADER_I(path, name) HEADER_STRING(path ## name) 

将它们的参数放在一起并将它们传递给HEADER_STRINGHEADER_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) 

这只是增加了要扩展的pathname参数的位置。 最后,用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" 。 然后可以在makefile中设置PROJECT_PATH ,并使用-DPROJECT_PATH=$(some_make_variable)传递。

最后一个缺陷是你必须小心不要让代币之间有任何空隙。

 #include HEADER(PROJECT_PATH,foobar.h) 

最终扩展到"foo/bar/ foobar.h" (注意空间),这是行不通的。