【问题标题】:How does the C++ compiler dynamically replace namespace during compilation?C++编译器如何在编译时动态替换命名空间?
【发布时间】:2021-03-22 00:38:45
【问题描述】:

我是 C++ 新手,并试图了解预处理、编译和链接步骤是如何工作的。我在尝试使用 gflgas library 库时偶然发现了一个有趣的行为。

代码

以下是我在这里的示例中使用的代码:

#include <iostream>
#include <gflags/gflags.h>

DEFINE_string(name, "Tugberk", "Name of the person to greet");

int main(int argc, char *argv[]) {
    gflags::ParseCommandLineFlags(&argc, &argv, true);
    std::cout << "Hello " << FLAGS_name << std::endl;
}

如您所见,我正在使用gflags:: 来联系ParseCommandLineFlags 功能。我假设 gflags 这里是命名空间(但我很确定我在这里错了,稍后会详细介绍)。

编译

我已经运行了以下命令来编译这段代码:

我调用cc1plus 的原因是模仿g++ 步骤来创建.s 文件,以便我最终可以使用它通过汇编程序创建.o 文件。我无意直接调用cc1plus

/usr/lib/gcc/x86_64-linux-gnu/9/cc1plus -quiet -v -imultiarch x86_64-linux-gnu -D_GNU_SOURCE main.cpp -quiet -dumpbase main.cpp -mtune=generic -march=x86-64 -auxbase main -version -fasynchronous-unwind-tables -fstack-protector-strong -Wformat -Wformat-security -fstack-clash-protection -fcf-protection -o /tmp/ccFyWgz8.s

然后,我使用as 命令生成一个.o 文件,以便我可以使用nm llvm 符号表转储器检查它。

as -v --64 -o /tmp/ccONcT86.o /tmp/ccFyWgz8.s

我观察到的

然后我在能够生成的.o 文件上使用nm,此时我观察到ParseCommandLineFlags 函数的命名空间更改为google

# nm -C /tmp/ccONcT86.o
0000000000000000 V DW.ref.__gxx_personality_v0
                 U _GLOBAL_OFFSET_TABLE_
00000000000001a0 t _GLOBAL__sub_I__ZN3fLS10FLAGS_nameB5cxx11E
                 U _Unwind_Resume
000000000000006f t __static_initialization_and_destruction_0(int, int)
0000000000000000 B fLS::FLAGS_name[abi:cxx11]
0000000000000000 W fLS::StringFlagDestructor::StringFlagDestructor(void*, void*)
0000000000000000 W fLS::StringFlagDestructor::StringFlagDestructor(void*, void*)
0000000000000000 n fLS::StringFlagDestructor::StringFlagDestructor(void*, void*)
0000000000000000 W fLS::StringFlagDestructor::~StringFlagDestructor()
0000000000000000 W fLS::StringFlagDestructor::~StringFlagDestructor()
0000000000000000 n fLS::StringFlagDestructor::~StringFlagDestructor()
0000000000000000 W fLS::dont_pass0toDEFINE_string[abi:cxx11](char*, char const*)
0000000000000060 b fLS::FLAGS_noname
0000000000000070 b fLS::d_name
0000000000000068 b fLS::o_name
0000000000000020 b fLS::s_name
                 U google::FlagRegisterer::FlagRegisterer<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(char const*, char const*, char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*)
                 U google::ParseCommandLineFlags(int*, char***, bool)
                 U std::allocator<char>::allocator()
                 U std::allocator<char>::~allocator()
                 U std::ostream::operator<<(std::ostream& (*)(std::ostream&))
                 U std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)
                 U std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
                 U std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()
                 U std::ios_base::Init::Init()
                 U std::ios_base::Init::~Init()
                 U std::cout
                 U std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
0000000000000000 r std::piecewise_construct
0000000000000008 b std::__ioinit
                 U std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
                 U std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
0000000000000000 W operator delete(void*, void*)
0000000000000000 W operator new(unsigned long, void*)
                 U __cxa_atexit
                 U __dso_handle
                 U __gxx_personality_v0
                 U __stack_chk_fail
0000000000000000 T main

检查gflags源代码后,我可以在以下两个地方看到这可能来自:

当我检查 gflags_declare.h 文件的安装版本时,我可以看到命名空间被定义为 google

// ---------------------------------------------------------------------------
// Namespace of gflags library symbols.
#define GFLAGS_NAMESPACE google

问题

我假设gflags::ParseCommandLineFlags 语句上下文中的gflags 是命名空间,但似乎不是。这是从哪里来的,为什么编译器对此感到满意?编译器是如何知道将gflags:: 替换为google:: 的?

【问题讨论】:

  • 这就是 #define 预处理器指令的作用——类似于宏替换。
  • @1201ProgramAlarm 这是有道理的,但我不确定编译器是如何知道将我的代码中的gflags:: 部分替换为第一部分的。另一种问这个问题的方法是编译器如何通过声明gflags:: 知道我指的是什么?是不是因为头文件叫gflags.h
  • @Ron 我无法从该文档中弄清楚在我的示例中如何解决gflags。您能否指出具体的措辞?
  • @Ron 这对我来说非常有意义。我正在努力的是 GFLAGS_NAMESPACE 根本没有在我的代码中使用,它在库头文件中使用。所以,我无法理解我的代码中的gflags:: 引用如何被解释为“用'google'替换它”。
  • 另一种可能性是命名空间别名,如namespace gflags=google; 或更巧妙的namespace gflags=GFLAGS_NAMESPACE;

标签: c++ compilation g++


【解决方案1】:

感谢 Igor 上面的评论,我发现这是因为 gflags 命名空间也被导出以实现向后兼容性。见https://github.com/gflags/gflags/blob/827c769e5fc98e0f2a34c47cef953cc6328abced/src/gflags.h.in#L623https://github.com/gflags/gflags/blob/5d5a6c550100fa9d19f18861b61be9343e94dbcb/CMakeLists.txt#L350-L357

在安装时,这会在 gflags.h 文件中创建以下条目:

// Import gflags library symbols into alternative/deprecated namespace(s)
#include "gflags_gflags.h"

gflags_gflags.h文件的生成内容如下:

#ifndef GFLAGS_GFLAGS_H_
#  error The internal header gflags_gflags.h may only be included by gflags.h
#endif

#ifndef GFLAGS_NS_GFLAGS_H_
#define GFLAGS_NS_GFLAGS_H_


namespace gflags {


using GFLAGS_NAMESPACE::int32;
using GFLAGS_NAMESPACE::uint32;
using GFLAGS_NAMESPACE::int64;
using GFLAGS_NAMESPACE::uint64;

using GFLAGS_NAMESPACE::RegisterFlagValidator;
using GFLAGS_NAMESPACE::CommandLineFlagInfo;
using GFLAGS_NAMESPACE::GetAllFlags;
using GFLAGS_NAMESPACE::ShowUsageWithFlags;
using GFLAGS_NAMESPACE::ShowUsageWithFlagsRestrict;
using GFLAGS_NAMESPACE::DescribeOneFlag;
using GFLAGS_NAMESPACE::SetArgv;
using GFLAGS_NAMESPACE::GetArgvs;
using GFLAGS_NAMESPACE::GetArgv;
using GFLAGS_NAMESPACE::GetArgv0;
using GFLAGS_NAMESPACE::GetArgvSum;
using GFLAGS_NAMESPACE::ProgramInvocationName;
using GFLAGS_NAMESPACE::ProgramInvocationShortName;
using GFLAGS_NAMESPACE::ProgramUsage;
using GFLAGS_NAMESPACE::VersionString;
using GFLAGS_NAMESPACE::GetCommandLineOption;
using GFLAGS_NAMESPACE::GetCommandLineFlagInfo;
using GFLAGS_NAMESPACE::GetCommandLineFlagInfoOrDie;
using GFLAGS_NAMESPACE::FlagSettingMode;
using GFLAGS_NAMESPACE::SET_FLAGS_VALUE;
using GFLAGS_NAMESPACE::SET_FLAG_IF_DEFAULT;
using GFLAGS_NAMESPACE::SET_FLAGS_DEFAULT;
using GFLAGS_NAMESPACE::SetCommandLineOption;
using GFLAGS_NAMESPACE::SetCommandLineOptionWithMode;
using GFLAGS_NAMESPACE::FlagSaver;
using GFLAGS_NAMESPACE::CommandlineFlagsIntoString;
using GFLAGS_NAMESPACE::ReadFlagsFromString;
using GFLAGS_NAMESPACE::AppendFlagsIntoFile;
using GFLAGS_NAMESPACE::ReadFromFlagsFile;
using GFLAGS_NAMESPACE::BoolFromEnv;
using GFLAGS_NAMESPACE::Int32FromEnv;
using GFLAGS_NAMESPACE::Uint32FromEnv;
using GFLAGS_NAMESPACE::Int64FromEnv;
using GFLAGS_NAMESPACE::Uint64FromEnv;
using GFLAGS_NAMESPACE::DoubleFromEnv;
using GFLAGS_NAMESPACE::StringFromEnv;
using GFLAGS_NAMESPACE::SetUsageMessage;
using GFLAGS_NAMESPACE::SetVersionString;
using GFLAGS_NAMESPACE::ParseCommandLineNonHelpFlags;
using GFLAGS_NAMESPACE::HandleCommandLineHelpFlags;
using GFLAGS_NAMESPACE::AllowCommandLineReparsing;
using GFLAGS_NAMESPACE::ReparseCommandLineNonHelpFlags;
using GFLAGS_NAMESPACE::ShutDownCommandLineFlags;
using GFLAGS_NAMESPACE::FlagRegisterer;

#ifndef SWIG
using GFLAGS_NAMESPACE::ParseCommandLineFlags;
#endif


} // namespace gflags


#endif  // GFLAGS_NS_GFLAGS_H_

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-16
    相关资源
    最近更新 更多