取消std :: type_info :: name的结果


93

我目前正在研究一些日志记录代码,这些代码除其他事项外,应该打印有关调用函数的信息。这应该是相对容易的,标准C ++有一个type_info类。这包含类型标识符的类/函数/等的名称。但它被粉碎了。这不是很有用。即typeid(std::vector<int>).name()返回St6vectorIiSaIiEE

有没有办法从中产生有用的东西?就像std::vector<int>上面的例子一样。如果它仅适用于非模板类,那也很好。

该解决方案应该适用于gcc,但是如果我可以移植的话会更好。它是用于日志记录的,因此不能关闭它并不是很重要,但是它对调试很有帮助。

Answers:


117

鉴于这个问题/答案受到关注,以及来自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

在Linux 64位和g ++ 4.7.2(Mingw32,Win32 XP SP2)上使用g ++ 4.7.2,g ++ 4.9.0 20140302(实验性),clang ++ 3.4(trunk 184647),clang 3.5(trunk 202594)进行了测试。

如果您不能使用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日更新)

调用abi::__cxa_demangle()成功后,可接受的答案(截至2013年9月7日)将返回指向本地堆栈分配数组的指针 ……哎!
还要注意,如果您提供缓冲区,则abi::__cxa_demangle()假定它是在堆上分配的。在堆栈上分配缓冲区是一个错误(来自gnu doc):“如果output_buffer不够长,则使用进行扩展realloc。” 调用realloc()指向堆栈的指针 ...哎呀!(另请参阅Igor Skochinsky的友善评论。)

您可以轻松地验证这两个错误:只需将接受的答案(截至2013年9月7日)中的缓冲区大小从1024减小到较小的值(例如16),并为其命名超过15(realloc()被称为)。尽管如此,取决于您的系统和编译器的优化,输出将是:垃圾/什么都没有/程序崩溃。
要验证第二个错误:将缓冲区大小设置为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;
}

注意这需要#include <cxxabi.h>。否则效果很好,谢谢。
jterrace 2012年

2
docs开始output_buffer分配了mlength的* length个字节的内存区域,其中存储了已分解的名称。如果output_buffer不够长,则使用realloc对其进行扩展。output_buffer可以改为NULL;在这种情况下,已取消组合的名称将放置在使用malloc分配的内存区域中。
伊戈尔·斯科钦斯基

2
@IgorSkochinsky是的,我先前的评论中有错别字,但我无法编辑。我想写的是:“上次检查时abi::__cxa_demangle希望将其分配到堆上。 ”非常感谢您查找文档!
阿里

1
请注意,从技术上讲,如果ret_val在施工过程中摔倒,这可能会泄漏。您可以使用范围保护器来防止这种情况。
GManNickG

3
如果可能更清楚地std::unique_ptr<char, decltype(&std::free)>用作指针的签名。
mindvirus

27

Boost核心包含一个分解器。结帐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正如先前所建议的,它基本上只是的包装。


1
如果可以选择升压,那么这是最好的选择!
hbobenicio

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  

6
您需要包含#include <cxxabi.h>
fuenfundachtzig,2010年

有趣。我没有定义
HAVE_CXA_DEMANGLE

@Matt我的意思是说,基于autoconf的构建系统仅设置HAVE_CXA_DEMANGLE(如果可用)。
KeithB 2010年

19
警告!上面的代码很可能会使程序崩溃。缓冲区必须由malloc分配或指定为NULL。不要在堆栈上分配它。请参阅下面的代码。
阿里2010年

当心,res可能会返回NULL :)
Zibri

8

在这里,看看type_strings.hpp它包含一个执行所需功能的函数。

如果您只想寻找一种拆解工具(例如,可以使用该工具来破坏日志文件中显示的内容),请查看c++filtbinutils附带的。它可以分解C ++和Java符号名称。


请注意,cxa_demange()(链接到的代码使用)和cx ++ filt都是gcc特定的。没有便携式的方法可以做到这一点。
KeithB

c ++ filt不会削减它,我在编译时需要这些东西(或大部分),大部分是用宏完成的。
总站

4
链接到type_strings.cpp似乎已损坏。
StackedCrooked

1
嗨@GregoryPakosz您上面评论中的github链接似乎也坏了:(干杯
olibre 2015年

只是一个重要的FYI:abi::__cxa_demangle()其远非<cxxabi.h> GCC特有的 -在遥远的过去,它们可能仅是GCC,但在撰写本文时,这<cxxabi.h>已成为根深蒂固的临时标准。因此,尽管答案的代码链接是DOI,我可以保证锵提供在这种情况下,提供一流的支持... QV,从锵的libcxxabi资料来源:各DECL,IMPL,巨大的考验:git.io/vRTBogit.io/vRTBhgit.io/vRTRf –测试代码的注释指出,与GCC相比,Clang实现具有更多的分解能力。
fish2000

4

它是由实现定义的,因此它不是可移植的。在MSVC ++中,name()是未修饰的名称,您必须查看raw_name()才能获得修饰的名称。
这里只是暗中的一击,但在gcc下,您可能要看一下demangle.h


3

这不是一个完整的解决方案,但是您可能需要查看一些标准(或广泛支持的)宏的定义。在日志记录代码中通常会看到宏的用法:

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);

4
更不用说PRETTY_FUNCTION了
CesarB

1
这将为您提供有关代码中位置的信息。问题在问的是类型的漂亮名称,例如std :: vector。
KeithB

他提到这是用于调试,我说这不是一个完整的解决方案。其他宏(例如FUNCDNAME)将返回修饰后的名称。
路加福音

实际上,重新阅读该问题是“我目前正在编写一些日志记录代码,这些代码除其他外应打印有关调用函数的信息。” 这可行。
Max Lybbert

它不完整,因为我不知道名称空间。这已经在我的代码中了。但是还是谢谢你。
总站

3

我还发现了一个名为的宏__PRETTY_FUNCTION__,可以解决问题。它给出了一个漂亮的函数名称(图:))。这就是我所需要的。

即它给我以下内容:

virtual bool mutex::do_unlock()

但我认为它不适用于其他编译器。


是的,PRETTY_FUNCTION是gcc特定的。
格雷格·罗杰斯

2

阿里解决方案略有不同。如果您希望代码仍然非常类似于

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的解决方案相同


1

看一下__cxa_demangle您可以找到的cxxabi.h


根据收到的消息,我已弃用它。
总站

您在哪里找到该消息?我只是谷歌搜索它,它似乎受到支持,没有证据表明它已被弃用。
阿里2010年

也许在::名称空间中已弃用。使用abi :: __ cxa_demangle不会收到警告。您正在使用什么gcc?
2011年

1
// 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;
  }

11
警告!缓冲区必须由malloc分配或指定为NULL。不要在堆栈上分配它。请参阅下面的代码。
阿里2010年

1

接受的解决方案 [1]作品大多良好。我发现至少有一种情况(我不会称其为“极端情况”),在这种情况下它没有报告我的预期……并带有参考文献。

对于这些情况,我在底部找到了另一个解决方案。

有问题的情况(使用type[1]中的定义):

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&

根据需要(至少由我来说)

代码 。由于专业化问题,它必须在包含的标头中,而不是在单独编译的源中。例如,请参见对模板函数的未定义引用

#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;
}

0

我一直想使用type_info,但是我敢肯定name()成员函数的结果是非标准的,并且不一定会返回任何可以转换为有意义结果的结果。
如果您坚持使用一个编译器,那么也许会有一个特定于编译器的功能可以满足您的需求。检查文档。


0

遵循Ali的解决方案,这是C ++ 11模板替代方案,最适合我的用法。

// type.h
#include <cstdlib>
#include <memory>
#include <cxxabi.h>

template <typename T>
std::string demangle() {
  int status = -4;

  std::unique_ptr<char, void (*)(void*)> res{
      abi::__cxa_demangle(typeid(T).name(), NULL, NULL, &status), std::free};
  return (status == 0) ? res.get() : typeid(T).name();
}

用法:

// main.cpp
#include <iostream>

namespace test {
    struct SomeStruct {};
}

int main()
{
    std::cout << demangle<double>() << std::endl;
    std::cout << demangle<const int&>() << std::endl;
    std::cout << demangle<test::SomeStruct>() << std::endl;

    return 0;
}

将打印:

double                                                                        
int                                                                           
test::SomeStruct
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.