我目前正在研究一些日志代码,它们应该 - 除其他外 - 打印有关调用函数的信息。这应该相对容易,标准C ++有一个type_info
类。它包含typeid'd类/函数/ etc的名称。但它被破坏了。它不是很有用。即typeid(std::vector<int>).name()
返回St6vectorIiSaIiEE
。
有没有办法从中产生有用的东西?就像上面例子中的std::vector<int>
一样。如果它只适用于非模板类,那也没关系。
该解决方案应该适用于gcc,但如果我可以移植它会更好。这是为了记录所以它不是那么重要,它不能被关闭,但它应该有助于调试。
鉴于这个问题/答案得到了关注,以及来自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; // some arbitrary value to eliminate the compiler warning
// enable c++11 by passing the flag -std=c++11 to g++
std::unique_ptr<char, void(*)(void*)> res {
abi::__cxa_demangle(name, NULL, NULL, &status),
std::free
};
return (status==0) ? res.get() : name ;
}
#else
// does nothing if not g++
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(); // Please use smart pointers in YOUR code!
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*
指针的类型:Derived
使用g ++ 4.7.2,g ++ 4.9.0 20140302(实验性),clang ++ 3.4(主干184647),在Linux 64位上的clang 3.5(主干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; // some arbitrary value to eliminate the compiler warning
handle result( abi::__cxa_demangle(name, NULL, NULL, &status) );
return (status==0) ? result.p : name ;
}
#else
// does nothing if not g++
std::string demangle(const char* name) {
return name;
}
#endif
(2013年9月8日更新)
The accepted answer (as of Sep 7, 2013),当调用abi::__cxa_demangle()
成功时,返回一个指向本地堆栈分配数组的指针......哎哟!
另请注意,如果提供缓冲区,abi::__cxa_demangle()
会假定它在堆上分配。在堆栈上分配缓冲区是一个错误(来自gnu doc):“如果output_buffer
不够长,它将使用realloc
进行扩展。”在指向堆栈的指针上调用realloc()
...哎哟! (另见Igor Skochinsky的善意评论。)
您可以轻松验证这两个错误:只需将接受的答案(截至2013年9月7日)中的缓冲区大小从1024减小到更小的值,例如16,并给它一个名称不超过15的东西(所以realloc()
不叫)。仍然,根据您的系统和编译器优化,输出将是:garbage / nothing / program crash。
要验证第二个错误:将缓冲区大小设置为1,并使用名称长度超过1个字符的内容调用它。当你运行它时,程序几乎肯定会崩溃,因为它试图用指向堆栈的指针调用realloc()
。
(2010年12月27日的旧答案)
对KeithB's code进行的重要更改:缓冲区必须由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;
}
// KeithB's solution is good, but has one serious flaw in that unless buf is static
// it'll get trashed from the stack before it is returned in res - and will point who-knows-where
// Here's that problem fixed, but the code is still non-re-entrant and not thread-safe.
// Anyone care to improve it?
#include <cxxabi.h>
// todo: javadoc this properly
const char* demangle(const char* name)
{
static char buf[1024];
size_t size = sizeof(buf);
int status;
// todo:
char* res = abi::__cxa_demangle (name,
buf,
&size,
&status);
buf[sizeof(buf) - 1] = 0; // I'd hope __cxa_demangle does this when the name is huge, but just in case.
return res;
}
accepted solution [1]的效果非常好。我发现至少有一个案例(我不会称之为一个角落案例),它没有报告我的预期......有引用。
对于这些情况,我发现了另一种解决方案,发布在底部。
有问题的案例(使用[1]中定义的type
):
int i = 1;
cout << "Type of " << "i" << " is " << type(i) << endl;
int & ri = i;
cout << "Type of " << "ri" << " is " << type(ri) << endl;
产生
Type of i is int
Type of ri is int
解决方案(使用type_name<decltype(obj)>()
,请参阅下面的代码):
cout << "Type of " << "i" << " is " << type_name<decltype(i)>() << endl;
cout << "Type of " << "ri" << " is " << type_name<decltype(ri)>() << endl;
产生
Type of i is int
Type of ri is int&
根据需要(至少由我)
代码。由于特化问题,它必须位于包含的标题中,而不是在单独编译的源中。例如,请参阅undefined reference to template function。
#ifndef _MSC_VER
# include <cxxabi.h>
#endif
#include <memory>
#include <string>
#include <cstdlib>
template <class T>
std::string
type_name()
{
typedef typename std::remove_reference<T>::type TR;
std::unique_ptr<char, void(*)(void*)> own
(
#ifndef _MSC_VER
abi::__cxa_demangle(typeid(TR).name(), nullptr,
nullptr, nullptr),
#else
nullptr,
#endif
std::free
);
std::string r = own != nullptr ? own.get() : typeid(TR).name();
if (std::is_const<TR>::value)
r += " const";
if (std::is_volatile<TR>::value)
r += " volatile";
if (std::is_lvalue_reference<T>::value)
r += "&";
else if (std::is_rvalue_reference<T>::value)
r += "&&";
return r;
}
我一直想使用type_info,但我确信name()成员函数的结果是非标准的,并且不一定会返回任何可以转换为有意义结果的内容。 如果您坚持使用一个编译器,那么可能有一个编译器特定的函数可以执行您想要的操作。查看文档。
Boost核心包含一个demangler。结帐core/demangle.hpp:
#include <boost/core/demangle.hpp>
#include <typeinfo>
#include <iostream>
template<class T> struct X
{
};
int main()
{
char const * name = typeid( X<int> ).name();
std::cout << name << std::endl; // prints 1XIiE
std::cout << boost::core::demangle( name ) << std::endl; // prints X<int>
}
它基本上只是abi::__cxa_demangle
的包装,正如之前所建议的那样。
这就是我们使用的。 HAVE_CXA_DEMANGLE仅在可用时设置(仅限最新版本的GCC)。
#ifdef HAVE_CXA_DEMANGLE
const char* demangle(const char* name)
{
char buf[1024];
unsigned int size=1024;
int status;
char* res = abi::__cxa_demangle (name,
buf,
&size,
&status);
return res;
}
#else
const char* demangle(const char* name)
{
return name;
}
#endif
在这里,看看type_strings.hpp它包含一个功能,你做你想要的。
如果您只是寻找一个解码工具,例如可以用来破坏日志文件中显示的东西,看看c++filt
,它带有binutils。它可以解析C ++和Java符号名称。
不是一个完整的解决方案,但您可能想要了解一些标准(或广泛支持的)宏的定义。在日志代码中常见的是查看宏的使用:
__FUNCTION__
__FILE__
__LINE__
e.g.:
log(__FILE__, __LINE__, __FUNCTION__, mymessage);
它是实现定义的,所以它不是可移植的东西。在MSVC ++中,name()是未修饰的名称,您必须查看raw_name()以获取装饰的名称。 只是在黑暗中刺伤,但在gcc下,你可能想看看demangle.h
我还发现了一个名为__PRETTY_FUNCTION__
的宏,它可以解决问题。它给出了一个漂亮的函数名称(数字:))。这就是我需要的。
即它给了我以下内容:
virtual bool mutex::do_unlock()
但我认为它不适用于其他编译器。
阿里的解决方案略有不同。如果你想让代码仍然非常相似
typeid(bla).name()
,
写这个而不是
Typeid(bla).name()
(仅限于首都首字母)
那么你可能会对此感兴趣:
在文件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());
}
*/
class Typeid {
public:
template <class T>
Typeid(const T& t) : typ(typeid(t)) {}
std::string name() { return demangle(typ.name()); }
private:
const std::type_info& typ;
};
#endif
type.cpp与Ali的解决方案保持一致
快来看看__cxa_demangle
你可以找到的cxxabi.h
。