我目前正在编写一些日志记录代码,这些代码应该(除其他外)打印有关调用函数的信息。这应该是比较容易的,标准C++有一个 type_info 班级。这包含 typeid 类/函数/等的名称。但它已经损坏了。这不是很有用。IE。 typeid(std::vector<int>).name() 回报 St6vectorIiSaIiEE.

有没有办法从中产生有用的东西?喜欢 std::vector<int> 对于上面的例子。如果它只适用于非模板类,那也没关系。

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

有帮助吗?

解决方案

鉴于此问题/答案受到的关注以及来自的宝贵反馈 G曼尼克G, ,我稍微清理了代码。给出了两个版本:一种具有 C++11 功能,另一种仅具有 C++98 功能。

在文件中 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

在文件中 类型.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 中使用以下方法,文件 类型.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日更新)

接受的答案(截至 2013 年 9 月 7 日), ,当调用 abi::__cxa_demangle() 成功了, 返回指向本地堆栈分配数组的指针...哎哟!
另请注意,如果您提供缓冲区, abi::__cxa_demangle() 假设它分配在堆上。在堆栈上分配缓冲区是一个错误(来自 gnu 文档): “如果 output_buffer 不够长,使用扩展 realloc." 呼唤 realloc() 在指向堆栈的指针上...哎哟!(也可以看看 伊戈尔·斯科钦斯基的善意评论。)

您可以轻松验证这两个错误:只需将接受的答案中的缓冲区大小(截至 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;
}

其他提示

Boost核心包含一个demangler。结帐核心/ 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()

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

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

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找到。

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

接受的解决方案 [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&

根据需要(至少是我)

<强>代码 。 由于特化问题,它必须位于包含的标题中,而不是在单独编译的源中。例如,请参见未定义的模板功能参考

#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()成员函数的结果是非标准的,并且不一定会返回任何可以转换为有意义结果的内容。
如果您坚持使用一个编译器,那么可能有一个编译器特定的函数可以执行您想要的操作。查看文档。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top