[C++] RTTI(執行時期型態資訊)的 Demangling

挽救回不來的進度(?)  這些日子懶到只有發幾篇歌詞翻譯,連一點點的技術性文章都沒有產出,為了確認我還活著而且沒有轉職(??),想說發一篇不久前看到 C++ 裡一個好用的函式。
  • RTTI,執行期型態資訊
  這個應該很多人都已經知道了,只要引入 <typeinfo> 標頭檔,再使用 typeid 運算子就可以得知目前某個變數的型態資訊,在多型與動態型別方面很有用。
  
  而其中,有個 name() 成員函式可以取得型態的名稱,用法如下:
#include <typeinfo>
// 略...
int i;
cout << typeid(i).name() << endl;
// 輸出: i
  如果你是使用 gcc 的話就會看到輸出為單個字母:i,這不難理解,就是 int 嘛,但是這樣呢?
char *c = new char;  // 字元指標
int foo(float a);  // 接受一個 float 並回傳 int 的函式
string s;  // 字串
cout << typeid(c).name() << endl;
cout << typeid(foo).name() << endl;
cout << typeid(s).name() << endl;
// 輸出: 
// Pc
// FifE
// NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
  第一個就是指向 char 的 Pointer 嘛,那接下來...「FifE」? 我是知道 Five 啦;「NSt7...... EEE」 ......??
  我彷彿看到了許多黑人問號。
黑人問號
  會出現這麼奇怪的名字是因為 C++ 有一個叫做 Name Mangling 的機制,簡單說就是把型態名加入一些特色並改寫成一個不會與其它名字重複的技術,但是經過 mangle 的名字對我們人類來說實在不好理解啊!所以我們要介紹一個很好用的函式:abi::__cxa_demangle()
  • Demangling:現出本名
  在使用這個函式之前要先引入 <cxxabi.h> 標頭檔*,接著我們來看看實際使用的例子:
#include <cxxabi.h>
// 略...
int status;
char *c;
char *realname = abi::__cxa_demangle(typeid(c).name(), 0, 0, &status);
cout << realname << endl;
// 輸出: char*
free(realname);  // 記得釋放記憶體

  我們總共傳了四個參數給這個函式,分別是:
          1) 要分析的字串(const char*)
          2) 要將結果輸出的地方(char*)
          3) 最長可以輸出多長?(size_t*)
          4) 儲存狀態碼(int*)
  而這個函式回傳的是 char*,我們可以接收他的回傳值來取代傳入第二、三個參數,因此在這裡將第二、三個參數設為 0(或 NULL)。
 另外很重要的是,這個回傳的結果字串是透過 C 動態分配的記憶體,所以在使用結束後要將這個 char* 用 free() 釋放。(2020/4/1 補

  最後我們來看看,解析 string 的 mangled name 會怎麼樣:
int status;
string s;
char *realname = abi::__cxa_demangle(typeid(s).name(), 0, 0, &status);
cout << realname << endl;
// 輸出: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
free(realname);
  非常詳細地印出了我們不曾在意過的 string 的真正定義,同時也請注意:這個結果會依據編譯器的不同而可能有不同的結果,在這裡我是使用 gcc 5.1.0 編譯的。

  那麼,這次就到這邊了,謝謝大家的閱讀!如有疑慮或指正也歡迎留言提出。

*這個標頭檔和函式似乎是 gcc 的 extension,但是經過筆者測試,clang 也是可以使用的。

參考資料:
  1) type_info - cppreference 

  2) Chapter 28. Demangling - gcc onlinedocs

後記:
  隔了好久終於有新文章了~ 其實我有點擔心一直發翻譯會不會沒人想看XD

留言

  1. 回覆
    1. 感謝你的指正,我沒注意到他底下有說要記得把回傳的char* free 掉

      稍後會進行修改

      刪除

張貼留言

這個網誌中的熱門文章

[C] 每天來點字串用法 (2) - strcpy()、strncpy()

[Python] *args 和 **kwargs 是什麼?一次搞懂它們!

[C] 每天來點字串用法 (5) - strcat()、strncat()