你能在C/C++ include指令中使用环境变量吗?

shi*_*git 2 c++ linux include preprocessor-directive

假设我有一个文件夹布局:

.
+-- Project
    +-- src
        +-- foo.h
        +-- foo.cpp
    +-- test
        +-- test_foo.c
Run Code Online (Sandbox Code Playgroud)

而且test_foo.c看起来这样的:

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

int main() {
    assert(foo() == true);
    printf("Test complete");
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

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

PROJECT_SRC="./Project/src/"
Run Code Online (Sandbox Code Playgroud)

然后我可以使用include指令:

#include "PROJECT_SRC/foo.h"
Run Code Online (Sandbox Code Playgroud)

这将是很好的,因为我可以有一个bash脚本导出我需要的某个项目的所有路径.此外,如果文件包含在不同的测试和构建文件中,我将不得不为每个文件设置相对路径(尽管工作量不大),这个路径不如一个绝对路径强大.

另一种选择可能CMake就是可以做到这一点的工具.或者这被认为是不好的做法?

Win*_*ute 6

Weeelll ......有可能,但它不漂亮,而且有一些陷阱.通常最好在构建系统中添加include路径,例如(假设为plain make):

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

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

PROJECT_PATH = foo/bar
CPPFLAGS = -I$(PROJECT_PATH)
Run Code Online (Sandbox Code Playgroud)

在消息来源中

#include "foobar.h"
Run Code Online (Sandbox Code Playgroud)

有效果的#include "foo/bar/foobar.h".

...另外,我是否看到你试图找到#include文件而不是标题?不要走那条路; 那道疯狂的谎言.单独编译源文件,除非你有一个它们链接在一起,通常的方式真的很好的理由不这样做.

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

你遇到的第一个问题是

#include "foo/bar/" "baz.h" // no dice.
Run Code Online (Sandbox Code Playgroud)

形象不对,所以简单的方法就出来了.我们必须尝试预处理器魔术,它的工作原理如下:

#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)
Run Code Online (Sandbox Code Playgroud)

也许从下往上:

#define HEADER_STRING(s) #s
Run Code Online (Sandbox Code Playgroud)

从其参数中生成一个字符串.也就是说,HEADER_STRING(foo/bar/baz.h)扩展到"foo/bar/baz.h".值得注意的是,宏参数不会扩展,因此即使定义了宏,HEADER_STRING(PROJECT_PATH)也会扩展为宏参数.当您尝试使用预处理器执行任何复杂操作时,这是您遇到的最常见问题之一,解决方案是添加另一个可以扩展参数的层:"PROJECT_PATH"PROJECT_PATH

#define HEADER_STRING_I(s) #s
#define HEADER_STRING(s) HEADER_STRING_I(s)
Run Code Online (Sandbox Code Playgroud)

...我们不需要这个HEADER_STRING,但它被用于HEADER,所以记住这个诀窍.我担心精确的预处理器替换规则有点晦涩,详细解释它们超出了SO答案的范围.简而言之,宏在层中扩展,当宏不扩展时,通常的技巧是为它们提供扩展的位置,即添加另一层.

HEADER_I 然后,

#define HEADER_I(path, name) HEADER_STRING(path ## name)
Run Code Online (Sandbox Code Playgroud)

将其参数捆绑在一起并传递给它们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)
Run Code Online (Sandbox Code Playgroud)

这只是增加了对的地方path,并name加以扩展参数.最后,用PROJECT_PATH #defined to foo/bar/,HEADER(PROJECT_PATH,foobar.h)扩展到"foo/bar/foobar.h",然后我们可以说

#include HEADER(PROJECT_PATH,foobar.h)
Run Code Online (Sandbox Code Playgroud)

到#include "foo/bar/foobar.h".PROJECT_PATH然后可以在makefile中设置并传递-DPROJECT_PATH=$(some_make_variable).

最后一个陷阱是你必须注意不要让任何空间在令牌之间滑动.

#include HEADER(PROJECT_PATH,foobar.h)
Run Code Online (Sandbox Code Playgroud)

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