ter*_*nus 85 c++ gcc name-mangling
我目前正在研究一些日志代码,它们应该 - 除其他外 - 打印有关调用函数的信息.这应该相对容易,标准C++有一个type_info
类.它包含typeid'd类/函数/ etc的名称.但它被破坏了.它不是很有用.即typeid(std::vector<int>).name()
回归St6vectorIiSaIiEE
.
有没有办法从中产生有用的东西?就像std::vector<int>
上面的例子一样.如果它只适用于非模板类,那也没关系.
该解决方案应该适用于gcc,但如果我可以移植它会更好.这是为了记录所以它不是那么重要,它不能被关闭,但它应该有助于调试.
Ali*_*Ali 105
鉴于这个问题/答案得到了关注,以及来自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
Run Code Online (Sandbox Code Playgroud)
在文件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
Run Code Online (Sandbox Code Playgroud)
用法:
#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;
}
Run Code Online (Sandbox Code Playgroud)
它打印:
ptr_base的Base*
类型:指针的类型:Derived
使用g ++ 4.7.2测试,克++ 4.9.0 20140302(实验),铛++ 3.4(躯干184647),铛Linux上的64位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
Run Code Online (Sandbox Code Playgroud)
(2013年9月8日更新)
接受答案(如2013年9月7日的),当调用abi::__cxa_demangle()
成功,返回一个指针到本地,堆栈分配数组 ...哎哟!
另请注意,如果提供缓冲区,则abi::__cxa_demangle()
假定它在堆上分配.在堆栈上分配的缓冲区(从GNU DOC)的错误:"如果output_buffer
没有足够长的时间,它是使用扩展realloc
." 调用realloc()
指向堆栈的指针 ......哎哟!(另见Igor Skochinsky的善意评论.)
你可以很容易地验证这两种错误的:是从1024减少缓冲区大小,以接受的答案(如2013年9月7日),以更小的东西,例如16,并给它一个名字的东西不超过15(因此realloc()
是不叫).仍然,根据您的系统和编译器优化,输出将是:garbage/nothing/program crash.
要验证第二个错误:将缓冲区大小设置为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;
}
Run Code Online (Sandbox Code Playgroud)
moo*_*f2k 24
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>
}
Run Code Online (Sandbox Code Playgroud)
它基本上只是一个包装器abi::__cxa_demangle
,正如之前所建议的那样.
Kei*_*thB 13
这就是我们使用的.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
Run Code Online (Sandbox Code Playgroud)
在这里,看一下type_strings.hpp,它包含一个可以完成你想要的功能.
如果您只是寻找一个demangling工具,您可以使用它来破坏日志文件中显示的内容,请查看c++filt
binutils附带的内容.它可以解析C++和Java符号名称.
它是实现定义的,所以它不是可移植的。在 MSVC++ 中,name() 是未修饰的名称,您必须查看 raw_name() 才能获得修饰的名称。
这里只是暗中刺伤,但在 gcc 下,您可能想查看demangle.h
如果我们想要的只是用于记录目的的未处理类型名称,我们实际上可以在不使用std::type_info
甚至不使用RTTI 的情况下做到这一点。
适用于三大主要编译器前端(gcc、clang和msvc)的稍微可移植的解决方案是使用函数template
并从函数名称中提取类型名称。
gcc
并且clang
两者都提供__PRETTY_FUNCTION__
当前函数或函数模板的名称,字符串中包含所有类型参数。类似地,MSVC 具有__FUNCSIG__
等效性。其中每一个的格式都略有不同,例如,对于 的调用void foo<int>
,编译器将输出不同的内容:
gcc
被格式化 void foo() [with T = int; ]
clang
被格式化 void foo() [T = int]
msvc
被格式化 void foo<int>()
知道了这一点,只需解析出前缀和后缀并将其包装到函数中即可提取出类型名称。
我们甚至可以使用C ++ 17的std::string_view
和扩展constexpr
在获得字符串名称编译时只需通过解析一个模板函数的名称。这也可以在任何早期的 C++ 版本中完成,但这仍然需要某种形式的字符串解析。
例如:
#include <string_view>
template <typename T>
constexpr auto get_type_name() -> std::string_view
{
#if defined(__clang__)
constexpr auto prefix = std::string_view{"[T = "};
constexpr auto suffix = "]";
constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
#elif defined(__GNUC__)
constexpr auto prefix = std::string_view{"with T = "};
constexpr auto suffix = "; ";
constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
#elif defined(__MSC_VER)
constexpr auto prefix = std::string_view{"get_type_name<"};
constexpr auto suffix = ">(void)";
constexpr auto function = std::string_view{__FUNCSIG__};
#else
# error Unsupported compiler
#endif
const auto start = function.find(prefix) + prefix.size();
const auto end = function.find(suffix);
const auto size = end - start;
return function.substr(start, size);
}
Run Code Online (Sandbox Code Playgroud)
有了这个,您可以在编译时调用get_type_name<T>()
以获取std::string_view
指示未处理的类型名称。
例如:
std::cout << get_type_name<std::string>() << std::endl;
Run Code Online (Sandbox Code Playgroud)
在 GCC 上将输出:
std::__cxx11::basic_string<char>
Run Code Online (Sandbox Code Playgroud)
并且在 clang 将输出:
std::basic_string<char>
Run Code Online (Sandbox Code Playgroud)
与此方法类似的增强是避免 aprefix
和suffix
假设所有类型的函数名称都相同,并搜索哨兵类型以解析从每一端到哨兵的偏移量。这确保字符串搜索只发生一次,并且每次都假定偏移量找到字符串名称。例如,double
用作一个简单的哨兵:
std::__cxx11::basic_string<char>
Run Code Online (Sandbox Code Playgroud)
这并非对所有编译器都具有可移植性,但可以针对提供__FUNCSIG__
/__PRETTY_FUNCTION__
等效项的任何编译器进行修改;它只需要一点解析。
注意:这还没有经过全面测试,所以可能存在一些错误;但主要的想法是解析所有包含名称的输出——这通常是__func__
编译器上类似输出的副作用。
归档时间: |
|
查看次数: |
37411 次 |
最近记录: |