最近在看 gflags 的源码,刚开始看到头文件的部分,就看到一处奇怪的地方。
在 gflags_declare.h.in 文件里,有这样的两处代码
41-43 行处
// ---------------------------------------------------------------------------
// Namespace of gflags library symbols.
#define GFLAGS_NAMESPACE @GFLAGS_NAMESPACE@
86 行处
namespace GFLAGS_NAMESPACE {
// omitting some code here
}
请问 #define 指令里的两个 @@符号,起的怎样的作用呢?
1
introom 2017-01-01 14:50:46 +08:00
这不是 c preprocessor 的东西,这是 gnu automake 的东西,./configure 的时候会替换掉。
|
3
glogo OP @introom 我使用 gnu 的 g++编译器编译 gflag 代码 和 用 clang 的 c++编译器编译我模仿我问题而写的代码,均能成功编译并运行
|
4
introom 2017-01-01 14:56:51 +08:00
没有 compiler-dependent 这种叫法, c++只有 undefined, implementation-defined, unspecified, well-formed. 参见这里: http://eel.is/c++draft/intro.defs
你说的 @@,和 c++没有一点关系,这是 autotools 的东西, variable substituion, 参见这里: https://www.gnu.org/software/autoconf/manual/autoconf.html#Makefile-Substitutions |
5
glogo OP @introom 抱歉,是我的用词不当,我想表达的意思是 这个是编译器对语言未定义行为的一种“自作主张”的实现,是这样吗?
autotools 是什么呀?是编译器工具链的一部分吗? 我在编译我模仿问题所编写的代码时,使用 g++ -E 1.cpp ,生成了预处理后的文件,发现里面的 @@ 符号不见了。 我的代码大致是这样: ``` #define TEST_NAMESPACE @TEST_NAMESPACE@ ``` ``` namespace TEST_NAMESPACE { // some code here } ``` |
7
introom 2017-01-01 15:16:18 +08:00
durian ❯❯❯ cat test.cpp
#define TEST_NAMESPACE @TEST_NAMESPACE@ namespace TEST_NAMESPACE { // some code here } durian ❯❯❯ g++ -E test.cpp ~/volatile/temp/cpp # 1 "test.cpp" # 1 "<built-in>" # 1 "<command-line>" # 1 "/usr/include/stdc-predef.h" 1 3 4 # 1 "<command-line>" 2 # 1 "test.cpp" namespace @TEST_NAMESPACE@ { } 大哥我还以为你发现了什么美丽新世界呢,,,,,, 大哥, namespace ns_name { declarations } 中, ns_name 必须是 identifier, 大哥, [lex.name]p1 白纸黑字写了 identifier 的构成。 所以 namespace @FOOBAR@ {}这样的鬼直接交给 compiler , compiler 会直接噎死的。 |
8
glogo OP @introom ...是这样的呢...compiler 当然不会识别 @xxx@这样的 identifier 了,但是我想问的 @TEST_NAMESPACE@是在什么环节被替换的呢?这样的写法原因是啥?
|
9
ipwx 2017-01-01 16:02:18 +08:00
@glogo 在 make 之前运行一下 autoconf ,然后工具链会读取 gflags_declare.h.in ,产生一个 gflags_declare.h 。
理由?很简单,不同平台上面需要不同的 define 。另外一点是为了支持 feature 的精细开关,用以禁用一部分功能,减小编译出来的二进制大小和依赖库数量。 |
10
glogo OP @ipwx 是这样的,原工程使用的确实是 cmake ,但是我写了一个模拟程序,仅用 g++编译器进行了编译运行成功了,我不清楚我所使用的 g++编译器有没有点用你所说的 autoconf 和 make 工具,换句话说我对编译工具链这块儿的了解有缺失的地方,不对的请您补充,我调用了 g++ test.cpp 后编译器会调用 autoconf 和 make 吗?
|
11
introom 2017-01-01 17:38:10 +08:00
|
12
ipwx 2017-01-01 17:50:42 +08:00
@glogo g++ 应该是不会自动调用 autoconf 的。但是 cmake 就不知道了。
我映像中的 cmake 一般流程是根据不同平台产生不同的编译脚本。比如对 *nix 产生一个 Makefile ,对 Visual C++ 产生一个 .sln 等等。然后你(手工地)再用对应平台的工具来进行编译。 所以一个典型的 cmake 流程: mkdir build && cd build && cmake .. && make 同时, cmake 是很灵活的,书写 cmake 脚本的人可以在产生 Makefile 的阶段中做任何事情。我不知道你的 cmake 脚本有没有处理 .h.in 里面的特殊标记。 |
13
glogo OP @ipwx 我编写的程序是直接运行 g++ test.cpp 的,没有使用 make/cmake 之类的工具。但是不使用这些工具也照样能处理 @TEST_NAMESPACE@这样的替换,所以我觉得这个替换不仅仅是 make/cmake 之类的工具支持的,应该在 g++工具链的某些环节支持的?
|
14
ipwx 2017-01-02 00:29:25 +08:00
@glogo 你得先确认 test.cpp 是不是引用了 .h.in 而不是某个 .h ……
如果你说的是对的话,大概 g++ 本身支持?无所谓了啦, g++ 支持比标准 c++ 更多的特性又没什么副作用。 |
15
glogo OP @ipwx 我确认,因为 test.h 和 test.cpp 就是我写的。
tets.h 的内容就是这样而已: ``` #ifndef MYTEST_H_ #define MYTEST_H_ #include <iostream> #define MYTEST_NAMESPACE @MYTEST_NAMESPACE@ namespace MYTEST { class A { public: A() { std::cout << "Hello A" << std::endl; } }; } #endif ~ ``` 而 test.cpp 就只是 include 了这个 test.cpp 然后声明了一个 A 对象。 所以我猜想 g++本身就支持了,是编译器的行为,但是我 google 了一番之后没有找到相关的资料,我非常想找到资料能确认这个特性。 |
18
araraloren 2017-01-03 09:14:12 +08:00
@glogo 这么简单的问题都能。。。。
至少你要先搞清楚 autotools 到底处在哪个位置 首先 g++ 是 GCC 工具链的一部分,用来编译 c++的 然后 makefile 是用来自动化编译的东西,是为了更方便的编译,是在 GCC 上层的东西 autotools / cmake 又是一套构建在 makefile 之上的 工具 ,用来更好的对 工程的编译细节进行 管理,同时 这东西写起来比 makefile 简单多了 至于你说的 g++ 编译 你的 那个 test.cpp 什么的,那是因为 那个 头文件的 宏定义对 程序的语法正确性 没有什么影响。。 |
19
zhidian 2017-01-03 10:29:25 +08:00
我猜是 cmake 里的变量,那个 *.h.in 文件处理完后会变成 *.h 文件,里面的
`#define GFLAGS_NAMESPACE @GFLAGS_NAMESPACE@` 会变成一个 `#define GFLAGS_NAMESPACE 一个字符串`。你可以参考我的笔记: https://github.com/district10/cmake-templates/blob/61a6fc91e91d6579c0d8c3caf18f25aa12e9a1e4/qt4-gui/Configs.h.in 外,如果是在 github 上,你搜这个变量(去掉前后“@”的字符串)不就可以找到来源…… |
20
zhidian 2017-01-03 10:46:00 +08:00
#吐槽#
你应该加上链接的: https://github.com/gflags/gflags/blob/master/src/gflags_declare.h.in#L43 我翻了一下源码,在根目录的 CMakeLists ( https://github.com/gflags/gflags/blob/master/CMakeLists.txt#L148 )里有: 可以看到工程名叫 "gflags": set (PACKAGE_NAME "gflags") 然后 NAMESPACE 这个变量也是这个值: gflags_define (STRING NAMESPACE "Name(s) of library namespace (separate multiple options by semicolon)" "google;${PACKAGE_NAME}" "${PACKAGE_NAME}") gflags_property (NAMESPACE ADVANCED TRUE) 然后尝试定了一个 GFLAGS_NAMESPACE_SECONDARY 变量,是一个列表,目测使很多 namespace 的候选项(我也看不懂): set (GFLAGS_NAMESPACE_SECONDARY "${NAMESPACE}") list (REMOVE_DUPLICATES GFLAGS_NAMESPACE_SECONDARY) if (NOT GFLAGS_NAMESPACE_SECONDARY) message (FATAL_ERROR "GFLAGS_NAMESPACE must be set to one (or more) valid C++ namespace identifier(s separated by semicolon \";\").") endif () foreach (ns IN LISTS GFLAGS_NAMESPACE_SECONDARY) if (NOT ns MATCHES "^[a-zA-Z][a-zA-Z0-9_]*$") message (FATAL_ERROR "GFLAGS_NAMESPACE contains invalid namespace identifier: ${ns}") endif () endforeach () 最后从里面选了第一个值作为 GFLAGS_NAMESPACE list (GET GFLAGS_NAMESPACE_SECONDARY 0 GFLAGS_NAMESPACE) list (REMOVE_AT GFLAGS_NAMESPACE_SECONDARY 0) |