鉴于这个问题/答案受到的关注,以及GManNickG提供的宝贵反馈,我稍微整理了一下代码。提供了两个版本:一个使用C++11功能,另一个只使用C++98功能。
在文件type.hpp中
#ifndef TYPE_HPP
#define TYPE_HPP
#include <string>
#include <typeinfo>
std::string demangle(const char* name);
template <class T>
std::string type(const T& t) {
return demangle(typeid(t).name());
}
#endif
在文件 type.cpp 中(需要 C++11)
#include "type.hpp"
#ifdef __GNUG__
#include <cstdlib>
#include <memory>
#include <cxxabi.h>
std::string demangle(const char* name) {
int status = -4;
std::unique_ptr<char, void(*)(void*)> res {
abi::__cxa_demangle(name, NULL, NULL, &status),
std::free
};
return (status==0) ? res.get() : name ;
}
#else
std::string demangle(const char* name) {
return name;
}
#endif
使用方法:
#include <iostream>
#include "type.hpp"
struct Base { virtual ~Base() {} };
struct Derived : public Base { };
int main() {
Base* ptr_base = new Derived();
std::cout << "Type of ptr_base: " << type(ptr_base) << std::endl;
std::cout << "Type of pointee: " << type(*ptr_base) << std::endl;
delete ptr_base;
}
它会打印:
ptr_base 的类型:Base*
pointee 的类型:Derived
在 Linux 64 位上使用 g++ 4.7.2、g++ 4.9.0 20140302(实验版)、clang++ 3.4(trunk 184647)、clang 3.5(trunk 202594)以及在 g++ 4.7.2(Mingw32,Win32 XP SP2)上进行了测试。
如果您无法使用 C++11 特性,则可以使用以下方法在 C++98 中完成,文件 type.cpp 现在为:
#include "type.hpp"
#ifdef __GNUG__
#include <cstdlib>
#include <memory>
#include <cxxabi.h>
struct handle {
char* p;
handle(char* ptr) : p(ptr) { }
~handle() { std::free(p); }
};
std::string demangle(const char* name) {
int status = -4;
handle result( abi::__cxa_demangle(name, NULL, NULL, &status) );
return (status==0) ? result.p : name ;
}
#else
std::string demangle(const char* name) {
return name;
}
#endif
(2013年9月8日更新)
被接受的答案(截至2013年9月7日)在调用abi::__cxa_demangle()
成功时,返回一个指向本地的、堆栈分配的数组……疼!
还要注意,如果提供了缓冲区,abi::__cxa_demangle()
会认为它是在堆上分配的。在堆栈上分配缓冲区是一个错误(来自gnu doc):“如果output_buffer
不够长,它会使用realloc
扩展。”在指向堆栈的指针上调用realloc()
……疼!(请参见Igor Skochinsky的友善评论。)
你可以很容易地验证这两个bug:只需将被接受的答案(截至2013年9月7日)中的缓冲区大小从1024减小到更小的值,例如16,并给它一个名字不超过15个字符(因此不会调用realloc()
)。不过,根据你的系统和编译器优化,输出可能是:垃圾/无/程序崩溃。
要验证第二个bug:将缓冲区大小设置为1,并用一个名字超过1个字符的东西调用它。当你运行它时,程序几乎肯定会崩溃,因为它试图用指向堆栈的指针调用realloc()
。
(2010年12月27日的旧答案)
KeithB的代码做出了重要的更改:缓冲区必须由malloc分配或指定为NULL。不要在堆栈上分配它。
智慧的做法是也检查一下状态。
我没能找到HAVE_CXA_DEMANGLE
。我检查了__GNUG__
,尽管这并不能保证代码能编译。有没有更好的主意?
#include <cxxabi.h>
const string demangle(const char* name) {
int status = -4;
char* res = abi::__cxa_demangle(name, NULL, NULL, &status);
const char* const demangled_name = (status==0)?res:name;
string ret_val(demangled_name);
free(res);
return ret_val;
}
output_buffer
是一个内存区域,使用malloc分配,长度为*length字节,用于存储解码后的名称。如果output_buffer不够长,则使用realloc进行扩展。output_buffer也可以为NULL;在这种情况下,解码后的名称将放置在使用malloc分配的内存区域中。 - Igor Skochinskyabi::__cxa_demangle
期望它在堆上分配。”非常感谢您查找文档! - Aliret_val
抛出异常,这可能会导致泄漏。您可以使用作用域保护来防止这种情况发生。 - GManNickGstd::unique_ptr<char, decltype(&std::free)>
作为指针的签名,这样可能会更清晰明了。 - mindvirus&tolower
(::tolower
与std::tolower
)时,我曾经遇到过一个非常奇怪的编译错误。这很可能是编译器的问题,但除非你能保证所有编译器在所有情况下都正确处理decltype(&std::free)
,否则我会保留它原样;回答中的版本将正常工作。 - Ali