C/C++ 宏的问题 两个 @@符号

2017-01-01 14:32:42 +08:00
 glogo

最近在看 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 指令里的两个 @@符号,起的怎样的作用呢?

3189 次点击
所在节点    C
21 条回复
introom
2017-01-01 14:50:46 +08:00
这不是 c preprocessor 的东西,这是 gnu automake 的东西,./configure 的时候会替换掉。
glogo
2017-01-01 14:51:49 +08:00
@introom 你是指着是 compiler-dependent 的特性,而不是语言特性的问题吗?
glogo
2017-01-01 14:53:22 +08:00
@introom 我使用 gnu 的 g++编译器编译 gflag 代码 和 用 clang 的 c++编译器编译我模仿我问题而写的代码,均能成功编译并运行
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
glogo
2017-01-01 15:01:14 +08:00
@introom 抱歉,是我的用词不当,我想表达的意思是 这个是编译器对语言未定义行为的一种“自作主张”的实现,是这样吗?

autotools 是什么呀?是编译器工具链的一部分吗?

我在编译我模仿问题所编写的代码时,使用 g++ -E 1.cpp ,生成了预处理后的文件,发现里面的 @@ 符号不见了。
我的代码大致是这样:
```
#define TEST_NAMESPACE @TEST_NAMESPACE@
```

```
namespace TEST_NAMESPACE {
// some code here
}
```
glogo
2017-01-01 15:05:22 +08:00
@introom 我编译成功并与运行了我的模仿代码,然而我并没使用 autotools 呀
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 会直接噎死的。
glogo
2017-01-01 15:38:26 +08:00
@introom ...是这样的呢...compiler 当然不会识别 @xxx@这样的 identifier 了,但是我想问的 @TEST_NAMESPACE@是在什么环节被替换的呢?这样的写法原因是啥?
ipwx
2017-01-01 16:02:18 +08:00
@glogo 在 make 之前运行一下 autoconf ,然后工具链会读取 gflags_declare.h.in ,产生一个 gflags_declare.h 。

理由?很简单,不同平台上面需要不同的 define 。另外一点是为了支持 feature 的精细开关,用以禁用一部分功能,减小编译出来的二进制大小和依赖库数量。
glogo
2017-01-01 17:07:04 +08:00
@ipwx 是这样的,原工程使用的确实是 cmake ,但是我写了一个模拟程序,仅用 g++编译器进行了编译运行成功了,我不清楚我所使用的 g++编译器有没有点用你所说的 autoconf 和 make 工具,换句话说我对编译工具链这块儿的了解有缺失的地方,不对的请您补充,我调用了 g++ test.cpp 后编译器会调用 autoconf 和 make 吗?
introom
2017-01-01 17:38:10 +08:00
@glogo 大哥我又来了, g++ -v test.cpp, 会显示出 g++到底干了啥,从 cpp, 到 gcc, 到 as ,到 ld 。

所以,答案是,不会。

大哥新年快乐。
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 里面的特殊标记。
glogo
2017-01-02 00:27:55 +08:00
@ipwx 我编写的程序是直接运行 g++ test.cpp 的,没有使用 make/cmake 之类的工具。但是不使用这些工具也照样能处理 @TEST_NAMESPACE@这样的替换,所以我觉得这个替换不仅仅是 make/cmake 之类的工具支持的,应该在 g++工具链的某些环节支持的?
ipwx
2017-01-02 00:29:25 +08:00
@glogo 你得先确认 test.cpp 是不是引用了 .h.in 而不是某个 .h ……

如果你说的是对的话,大概 g++ 本身支持?无所谓了啦, g++ 支持比标准 c++ 更多的特性又没什么副作用。
glogo
2017-01-02 01:01:20 +08:00
@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 了一番之后没有找到相关的资料,我非常想找到资料能确认这个特性。
glogo
2017-01-02 02:36:47 +08:00
@introom @ipwx 这是个笨问题,是我看错了,答案确实是你们所说的那样。非常感谢!
introom
2017-01-02 12:41:59 +08:00
@glogo 没事,大哥客气了。认识大哥感到很开心。
araraloren
2017-01-03 09:14:12 +08:00
@glogo 这么简单的问题都能。。。。
至少你要先搞清楚 autotools 到底处在哪个位置
首先
g++ 是 GCC 工具链的一部分,用来编译 c++的
然后
makefile 是用来自动化编译的东西,是为了更方便的编译,是在 GCC 上层的东西
autotools / cmake 又是一套构建在 makefile 之上的 工具 ,用来更好的对 工程的编译细节进行 管理,同时 这东西写起来比 makefile 简单多了

至于你说的 g++ 编译 你的 那个 test.cpp 什么的,那是因为 那个 头文件的 宏定义对 程序的语法正确性 没有什么影响。。
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 上,你搜这个变量(去掉前后“@”的字符串)不就可以找到来源……
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)

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/331561

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX