ルー大柴みたいなタイトルだな。
typeidのname()
関数はマングルされた暗号のような型名を返しやがるため、専用の関数でデマングルをかます必要がある。
一応ClangにもLLVM実装の__cxa_demangle
が用意されている。
#include <cxxabi.h>
char* __cxa_demangle(const char* mangled_name,
char* output_buffer,
size_t* length,
int* status);
auto s = typeid(int).name(); // "i"
int status;
char* r = abi::__cxa_demangle(s, nullptr, nullptr, &status);
if (status == 0) {
puts(r); // "int"
}
free(r);
__cxa_demangle
はmalloc
された値を返すため、対象を己の意志によってfree
へと導く必要がある。
成功時、status
は0
、失敗時はそれ以外の値が代入される。
0 // 成功。mallocの結果を返す(利用後free必須)。output_buffer指定時はreallocの結果を返す場合がある
-1 // mallocに失敗。nullptrを返す
-2 // mangled_nameの解析に失敗。nullptrを返す
-3 // mangled_nameが空。output_buffer指定時にlengthが空。nullptrを返す
第二引数のoutput_buffer
を利用する場合はmalloc
されたオブジェクトを渡す必要がある。その際オブジェクトのサイズを第三引数のlength
に渡す。
int status;
size_t length = 64;
char* output_buffer = static_cast<char*>(malloc(length));
for (auto s : {typeid(int[9]).name(), typeid(std::string).name()}) {
char* r = abi::__cxa_demangle(s, output_buffer, &length, &status);
if (status == 0) { printf("%zu: %s\n", strlen(r), r); }
if (r != NULL) { output_buffer = r; }
}
free(output_buffer);
---------------- output ----------------
7: int [9]
85: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >
静的確保された配列を渡した場合は__cxa_demangle
側でrealloc
による拡張が行えずにクラッシュが発生する場合がある(malloc: *** error for object 0x090090090999: pointer being realloc'd was not allocated
)
output_buffer
を空の状態で渡せば__cxa_demangle
側でmalloc
される。こちらのほうが処理は単純になる。
int status;
size_t length;
char *output_buffer = nullptr;
for (auto s : {typeid(int*).name(), typeid(int).name(), typeid(std::vector<int>).name()}) {
output_buffer = abi::__cxa_demangle(s, output_buffer, &length, &status);
if (status == 0) { printf("%p(%zu,%zu)(%s)\n", output_buffer, length, strlen(output_buffer), output_buffer); }
}
free(output_buffer);
---------------- output ----------------
0x900901a00(5,4)(int*)
0x900901a00(5,3)(int)
0x900902f10(49,48)(std::__1::vector<int, std::__1::allocator<int> >)
ちなみに今回範囲for文のinitializer_listに指定したconst char* type_info{}.name()
のポインタはプログラム終了時まで有効な値となっているため問題ないが、std::string{}.c_str()
等の内部ポインタはstd::string{}
オブジェクトの生存期間に依存するため、注意しなければならない。
これが → std::string
こうなって → NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
こうなる → std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >
C++怖ぇえ……