typeid(T).name() 方法在 linux 下通过 g++ 编译得不到正确的类型

说实话,我不是 c++ 程序员,之前虽然出于个人兴趣研究过 c++,也跟风看过《c++ primer》。

但是要说水平,我估计还没入门吧。

要不是之前苦学过一段时间的 c,估计连基本的语法也一知半解了。

最近在研究 nodejs,当然不是研究 nodejs 常用的 api 怎么用,如果只是仅仅止步于此的话,根本用不着接触到 c++ 了。

我们知道,nodejs 是用 c++ 写的,而且 nodejs 本身是由 v8 引擎强力驱动的。因此,如果想在 nodejs 上更进一步,知其然更知其所以然的话,研究下 nodejs 的源码是必不可少的。

这里说研究二字,或许有点夸张了,更好的说法应该是开始学习吧。

刚开始其实是碰到 c++ 模板的代码,看的云里雾里的。因为好久没接触过 c++ 了,因此咋一看,愣是没太看懂表示的啥意思。

于是就只能开始上网搜几个示例,自己写写练练手嘛。

哪知道碰到下面这个示例:

#include 
#include 
#include 
#include 

using namespace std;

template
void printType(const T& t){
  cout << typeid(T).name() << endl;
}

int main(int argc, char* argv[]) {
  const string t("test");

  printType(&t);

  return 0;
}

这个 typeid 以前还真是从来没用过,后来一查 c++ 文档 ,才明白这个方法是获取变量类型的,就跟 JavaScript 里面的 typeof 有点类似。

这明显有 api 可以查的问题都算不得问题,问题是,当我在编译运行的时候,却遇到了令我困惑的地方了。

很明显,我是在 linux 系统中,动用 g++ 进行编译的

$ g++ test.cc
$ ./a.out
PKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE

但是输出的结果,好像不是我想要看到的内容啊,简直跟乱码一样。

于是我又改了改代码,干脆改成这样测试下:

#include 
#include 
#include 
#include 

using namespace std;

int main(int argc, char* argv[]) {
  cout << typeid(int).name() << endl;
  cout << typeid(string).name() << endl;
  
  return 0;
}

但是依旧是乱码

$ g++ test.cc
$ ./a.out
i
NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE

心想,这是什么鬼,为什么跟文档不一样。

于是,只得开始找原因了。

经过漫长的搜寻查找以后,终于功夫不负有心人,在 stackoverflow 上找到了这个问题的解决方案:https://stackoverflow.com/questions/789402/typeid-returns-extra-characters-in-g?r=SearchResults#

大意就是说,这是 gcc 编译器的问题,编译出来得到的结果 mangled 了,这个词可以理解为“混乱”或者“错位”的意思,因此必须要用 c++filt 程序 unmangled 回去。

但是这个 c++filt 是个什么东西呢?

姑且先不管,先试试看有没有效果吧。

$ ./a.out | c++filt -t
int
std::__cxx11::basic_string, std::allocator >

可以看到,用管道符这么一转换,果然得到了想要的结果。

但是这个 c++filt 究竟是个什么程序呢?

这个时候,熟悉 Linux 的各位肯定能想到,我就 man 一下嘛,自然能找到得想要的答案吧。

那我们就 man 一下吧。

C++FILT(1)                           GNU Development Tools                           C++FILT(1)

NAME
       c++filt - Demangle C++ and Java symbols.

SYNOPSIS
       c++filt [-_|--strip-underscore]
               [-n|--no-strip-underscore]
               [-p|--no-params]
               [-t|--types]
               [-i|--no-verbose]
               [-s format|--format=format]
               [--help]  [--version]  [symbol...]

DESCRIPTION
       The C++ and Java languages provide function overloading, which means that you can write
       many functions with the same name, providing that each function takes parameters of
       different types.  In order to be able to distinguish these similarly named functions C++
       and Java encode them into a low-level assembler name which uniquely identifies each
       different version.  This process is known as mangling. The c++filt [1] program does the
       inverse mapping: it decodes (demangles) low-level names into user-level names so that
       they can be read.
       
       .....

由于篇幅有限,这里就不全贴出来了,我们从 description 中可以看到,这个程序主要用于解码低等级的名称,解码为用户可阅读的名称,之所以要这么做,是因为 c++ 中的重载特性所决定的。

但是这个程序很有意思的地方是,它不仅仅能解码类型,还能给字符解码:

$ c++filt -n _Z1fv
f()

所以看到这里,你就该明白了,这个 c++filt 相当于是个解码的玩意儿,因为本身代码是 gcc 编译的,为了优化编译开销,给这个地方进行了编码,因此想要阅读,必须要用这个程序解码回来才行。

不过如果不是有经验的程序员来指点迷津,寻常人哪里知道这个地方这种弯弯绕绕呢?

本来觉得这地方挺简单的,好像没有必要记录下来。

但是后来一想,有时候,我们碰到过的坑,花时间记录下来,即使在之后的日子里一直无人问津,但是谁知道,会不会有一天有某些人能够从中获取到哪怕一丝丝灵感呢。

我想,哪怕只是抱着这么个美好的愿景,去做这么一件事,那也是一件极好的事情吧。

你可能感兴趣的:(typeid(T).name() 方法在 linux 下通过 g++ 编译得不到正确的类型)