简体   繁体   English

您可以在C / C ++ include指令中使用环境变量吗?

[英]Can you use environment variables in C/C++ include directives?

Say I have a folder layout as such: 说我有这样的文件夹布局:

.
+-- Project
    +-- src
        +-- foo.h
        +-- foo.cpp
    +-- test
        +-- test_foo.c

And test_foo.c looks as such: test_foo.c看起来像这样:

#include "../src/foo.h"
#include <stdio.h>
#include <assert.h>

int main() {
    assert(foo() == true);
    printf("Test complete");
    return 0;
}

Is there a way to replace the line #include "../src/foo.h" with a variable that points to the source directory? 有没有办法用指向源目录的变量替换#include "../src/foo.h"行? For instance, say in my environment I have a have a variable: 例如,假设在我的环境中我有一个变量:

PROJECT_SRC="./Project/src/"

And then I could have the include directive as such: 然后我可以这样包含include指令:

#include "PROJECT_SRC/foo.h"

This would be nice as I could then have a bash script that exports all the paths I need for a certain project. 那样很好,因为那时我可以拥有一个bash脚本,该脚本可以导出某个项目所需的所有路径。 Also, if the file is included in different test and build files I would have to set up the relative path for each (although not much work) which would be less robust than one absolute path. 另外,如果文件包含在不同的测试和构建文件中,则我将不得不为每个文件设置相对路径(尽管工作量不大),但相对路径的健壮性要比一个绝对路径差。

An alternative might be a tool like CMake that can do this. 一个替代方法可能是像CMake这样的工具。 Or is this considered bad practice? 还是这被认为是不良做法?

Weeelll...it is possible, sort of, but it's not pretty, and it has some pitfalls. Weeelll ...这是可能的,但不是很漂亮,并且有一些陷阱。 It is usually better to add the include path in the build system, such as (assuming plain make ): 通常最好在构建系统中添加include路径,例如(假设plain make ):

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

and #include the headers without the path in the source file. #include标头,而源文件中没有路径。 This will make make call the compiler with -Iyour/project/path , which will make the compiler look for headers in your/project/path . 这将使make调用与编译器-Iyour/project/path ,这将使编译器,在头your/project/path That is to say, in the Makefile you can have 也就是说,在Makefile中,您可以拥有

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

and in the sources 并在来源中

#include "foobar.h"

to have the effect of #include "foo/bar/foobar.h" . 具有#include "foo/bar/foobar.h"

...also, did I see you try to #include source files instead of headers? ...此外,我是否看到您尝试#include源文件而不是标头? Do not go down that road; 不要走那条路; down that road madness lies. 在那条疯狂的路上。 Compile the source files separately and link them together the usual way unless you have a really good reason to do otherwise. 单独编译源文件,除非你有一个非常好的理由不这样做他们联系在一起的常用方法。

So, I don 't see a reason why you would want to reference the project path directly in #include directives in the code; 因此,我看不出为什么要在代码的#include指令中直接引用项目路径的原因; the only change on the build system side is only that you have to pass -DPROJECT_PATH=foo/bar/ instead of -IPROJECT_PATH=foo/bar/ and that the construct is more brittle than the mechanisms that are actually designed for this sort of stuff. 生成系统方面的唯一变化是,您只需要传递-DPROJECT_PATH=foo/bar/而不是-IPROJECT_PATH=foo/bar/ ,并且该结构比为这种东西实际设计的机制更脆弱。 。 But if you really want to do it, then here is how: 但是,如果您真的想这样做,那么可以按照以下步骤操作:

The first problem you run into is that 您遇到的第一个问题是

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

is ill-formed, so the easy way is out. 格式不正确,因此可以轻松解决。 We have to try preprocessor magic, and it works like this: 我们必须尝试使用​​预处理器魔术,它的工作方式如下:

#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)

Perhaps start from the bottom up: 也许是从下而上开始的:

#define HEADER_STRING(s) #s

makes a string from its argument. 根据其参数生成一个字符串。 That is to say, HEADER_STRING(foo/bar/baz.h) expands to "foo/bar/baz.h" . 也就是说, HEADER_STRING(foo/bar/baz.h)扩展为"foo/bar/baz.h" Notably, macro parameters are not expanded, so HEADER_STRING(PROJECT_PATH) will expand to "PROJECT_PATH" even if a macro PROJECT_PATH is defined. 值得注意的是,宏参数未扩展,因此即使定义了宏PROJECT_PATHHEADER_STRING(PROJECT_PATH)也会扩展为"PROJECT_PATH" This is one of the most common problems you run into when you try to do anything complicated with the preprocessor, and the solution is to add another layer in which the parameters can be expanded: 这是您尝试对预处理器进行任何复杂处理时遇到的最常见问题之一,解决方案是添加可以扩展参数的另一层:

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

...we do not need this for HEADER_STRING , but it is used in HEADER , so keep the trick in mind. ...我们对于HEADER_STRING不需要此功能,但是它在HEADER ,因此请记住这一技巧。 I'm afraid the precise preprocessor substitution rules are somewhat arcane, and explaining them in detail goes beyond the scope of a SO answer. 恐怕精确的预处理器替换规则有些不可思议,对它们的详细解释超出了SO答案的范围。 In a nutshell, macros are expanded in layers, and when macros don't get expanded, the trick is usually to give them a place to expand, ie, to add another layer. 简而言之,宏是按层扩展的,当宏不扩展时,诀窍通常是为它们提供扩展的位置,即添加另一层。

HEADER_I then, 然后, HEADER_I

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

tapes its arguments together and passes them to HEADER_STRING . 将其参数HEADER_STRING在一起,并将其传递给HEADER_STRING HEADER_I(foo,bar) expands to HEADER_STRING(foobar) . HEADER_I(foo,bar)扩展为HEADER_STRING(foobar) Because of the problem I mentioned above, HEADER_I(PROJECT_PATH,foobar.h) expands to HEADER_STRING(PROJECT_PATHfoobar.h) , which in turn expands to "PROJECT_PATHfoobar.h" , so we need another layer for the expansion of PROJECT_PATH : 由于我上面提到的问题, HEADER_I(PROJECT_PATH,foobar.h)扩展为HEADER_STRING(PROJECT_PATHfoobar.h) ,后者又扩展为"PROJECT_PATHfoobar.h" ,因此我们需要另一层扩展PROJECT_PATH

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

This just adds the place for the path and name parameters to be expanded. 这只是添加了要扩展的pathname参数的位置。 Finally, with PROJECT_PATH #define d to foo/bar/ , HEADER(PROJECT_PATH,foobar.h) expands to "foo/bar/foobar.h" , and then we can say 最后,使用PROJECT_PATH #define d为foo/bar/ HEADER(PROJECT_PATH,foobar.h)扩展为"foo/bar/foobar.h" ,然后我们可以说

#include HEADER(PROJECT_PATH,foobar.h)

to #include "foo/bar/foobar.h" . #include "foo/bar/foobar.h" PROJECT_PATH can then be set in the makefile and passed with -DPROJECT_PATH=$(some_make_variable) . 然后可以在makefile中设置PROJECT_PATH并通过-DPROJECT_PATH=$(some_make_variable)传递。

The last pitfall is that you must take care to not let any spaces slip between the tokens. 最后一个陷阱是,您必须注意不要让令牌之间有任何间隔。

#include HEADER(PROJECT_PATH,foobar.h)

ultimately expands to "foo/bar/ foobar.h" (note the space), which does not work. 最终扩展为"foo/bar/ foobar.h" (请注意空格),这将不起作用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM