V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
glogo
V2EX  ›  C

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

  •  
  •   glogo · 2017-01-01 14:32:42 +08:00 · 3201 次点击
    这是一个创建于 2893 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    21 条回复    2017-01-03 23:14:03 +08:00
    introom
        1
    introom  
       2017-01-01 14:50:46 +08:00
    这不是 c preprocessor 的东西,这是 gnu automake 的东西,./configure 的时候会替换掉。
    glogo
        2
    glogo  
    OP
       2017-01-01 14:51:49 +08:00
    @introom 你是指着是 compiler-dependent 的特性,而不是语言特性的问题吗?
    glogo
        3
    glogo  
    OP
       2017-01-01 14:53:22 +08:00
    @introom 我使用 gnu 的 g++编译器编译 gflag 代码 和 用 clang 的 c++编译器编译我模仿我问题而写的代码,均能成功编译并运行
    introom
        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
    glogo
        5
    glogo  
    OP
       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
        6
    glogo  
    OP
       2017-01-01 15:05:22 +08:00
    @introom 我编译成功并与运行了我的模仿代码,然而我并没使用 autotools 呀
    introom
        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 会直接噎死的。
    glogo
        8
    glogo  
    OP
       2017-01-01 15:38:26 +08:00
    @introom ...是这样的呢...compiler 当然不会识别 @xxx@这样的 identifier 了,但是我想问的 @TEST_NAMESPACE@是在什么环节被替换的呢?这样的写法原因是啥?
    ipwx
        9
    ipwx  
       2017-01-01 16:02:18 +08:00
    @glogo 在 make 之前运行一下 autoconf ,然后工具链会读取 gflags_declare.h.in ,产生一个 gflags_declare.h 。

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

    所以,答案是,不会。

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

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

    至于你说的 g++ 编译 你的 那个 test.cpp 什么的,那是因为 那个 头文件的 宏定义对 程序的语法正确性 没有什么影响。。
    zhidian
        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 上,你搜这个变量(去掉前后“@”的字符串)不就可以找到来源……
    zhidian
        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)
    glogo
        21
    glogo  
    OP
       2017-01-03 23:14:03 +08:00
    @zhidian 现在搞清楚了,默认情况下, GFLAGS_NAMESPACE=google, ns=gflags
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1263 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 23:30 · PVG 07:30 · LAX 15:30 · JFK 18:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.