Gỡ kết quả của std :: type_info :: name


93

Tôi hiện đang làm việc trên một số mã ghi nhật ký được cho là - trong số những thứ khác - in thông tin về chức năng gọi. Điều này sẽ tương đối dễ dàng, C ++ tiêu chuẩn có một type_infolớp. Nó chứa tên của classid'd class / function / etc. nhưng nó bị lật tẩy. Nó không hữu ích lắm. Tức là typeid(std::vector<int>).name()trở lại St6vectorIiSaIiEE.

Có cách nào để tạo ra thứ gì đó hữu ích từ điều này không? Giống như std::vector<int>ví dụ trên. Nếu nó chỉ hoạt động cho các lớp không phải khuôn mẫu, điều đó cũng tốt.

Giải pháp sẽ hoạt động cho gcc, nhưng sẽ tốt hơn nếu tôi có thể chuyển nó. Nó dùng để ghi nhật ký nên không quan trọng đến mức không thể tắt được, nhưng nó sẽ hữu ích cho việc gỡ lỗi.

Câu trả lời:


117

Với sự chú ý mà câu hỏi / câu trả lời này nhận được và phản hồi có giá trị từ GManNickG , tôi đã xóa mã một chút. Hai phiên bản được đưa ra: một với các tính năng C ++ 11 và một phiên bản khác chỉ có các tính năng C ++ 98.

Trong tệp 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

Trong tệp type.cpp (yêu cầu 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

Sử dụng:

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

Nó in:

Loại ptr_base: Base*
Loại điểm:Derived

Đã thử nghiệm với g ++ 4.7.2, g ++ 4.9.0 20140302 (thử nghiệm), clang ++ 3.4 (trung kế 184647), clang 3.5 (trung kế 202594) trên Linux 64 bit và g ++ 4.7.2 (Mingw32, Win32 XP SP2).

Nếu bạn không thể sử dụng các tính năng của C ++ 11, đây là cách nó có thể được thực hiện trong C ++ 98, tệp type.cpp bây giờ là:

#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


(Cập nhật từ ngày 8 tháng 9 năm 2013)

Câu trả lời được chấp nhận (kể từ ngày 7 tháng 9 năm 2013) , khi cuộc gọi đến abi::__cxa_demangle()thành công, trả về một con trỏ đến một mảng được phân bổ ngăn xếp cục bộ ... ouch!
Cũng lưu ý rằng nếu bạn cung cấp bộ đệm, hãy abi::__cxa_demangle()giả sử nó được cấp phát trên heap. Việc phân bổ bộ đệm trên ngăn xếp là một lỗi (từ gnu doc): "Nếu output_bufferkhông đủ dài, nó sẽ được mở rộng bằng cách sử dụng realloc." Đang gọi realloc()một con trỏ tới ngăn xếp ... ouch! (Xem thêm bình luận tử tế của Igor Skochinsky .)

Bạn có thể dễ dàng xác minh cả hai lỗi: chỉ cần giảm kích thước bộ đệm trong câu trả lời được chấp nhận (tính đến 07 tháng 9 năm 2013) từ 1024 đến một cái gì đó nhỏ hơn, ví dụ 16, và cung cấp cho nó một cái gì đó với một tên không dài hơn 15 (do đó realloc()không được gọi). Tuy nhiên, tùy thuộc vào hệ thống của bạn và tối ưu hóa trình biên dịch, kết quả đầu ra sẽ là: rác / không có gì / sự cố chương trình.
Để xác minh lỗi thứ hai: đặt kích thước bộ đệm thành 1 và gọi nó bằng tên có tên dài hơn 1 ký tự. Khi bạn chạy nó, chương trình gần như chắc chắn bị treo khi nó cố gắng gọi realloc()bằng con trỏ tới ngăn xếp.


(Câu trả lời cũ từ ngày 27 tháng 12 năm 2010)

Những thay đổi quan trọng được thực hiện đối với mã của KeithB : bộ đệm phải được cấp phát bởi malloc hoặc được chỉ định là NULL. KHÔNG phân bổ nó trên ngăn xếp.

Cũng nên kiểm tra trạng thái đó.

Tôi không tìm thấy HAVE_CXA_DEMANGLE. Tôi kiểm tra __GNUG__mặc dù điều đó không đảm bảo rằng mã sẽ được biên dịch. Bất cứ ai có một ý tưởng tốt hơn?

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

Lưu ý rằng điều này cần #include <cxxabi.h>. Nếu không làm việc tuyệt vời, cảm ơn.
jterrace

2
Từ tài liệu : output_bufferMột vùng bộ nhớ, được cấp phát bằng malloc, có độ dài * byte, nơi lưu trữ tên đã tách tên. Nếu output_buffer không đủ dài, nó sẽ được mở rộng bằng cách sử dụng realloc. bộ đệm đầu ra có thể là NULL; trong trường hợp đó, tên được phân loại được đặt trong vùng bộ nhớ được cấp phát bằng malloc.
Igor Skochinsky

2
@IgorSkochinsky Vâng, có một lỗi đánh máy trong nhận xét trước đây của tôi nhưng tôi không thể chỉnh sửa điều đó. Điều tôi muốn viết: "Lần trước tôi đã kiểm tra abi::__cxa_demangledự kiến ​​nó sẽ được phân bổ trên heap. " Cảm ơn bạn rất nhiều vì đã tra cứu tài liệu!
Ali

1
Lưu ý rằng về mặt kỹ thuật điều này có thể bị rò rỉ nếu ret_valném trong quá trình xây dựng. Bạn có thể sử dụng bộ bảo vệ phạm vi để bảo vệ khỏi điều đó.
GManNickG

3
Nếu có lẽ sẽ rõ ràng hơn để sử dụng std::unique_ptr<char, decltype(&std::free)>làm chữ ký cho con trỏ của bạn.
mindvirus

27

Lõi tăng cường chứa một bộ tách sóng. Checkout 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>
}

Về cơ bản nó chỉ là một trình bao bọc abi::__cxa_demangle, như đã được đề xuất trước đây.


1
Nếu tăng là một lựa chọn, đây là cách tốt nhất để đi!
hbobenicio

13

Đây là những gì chúng tôi sử dụng. OF_CXA_DEMANGLE chỉ được đặt nếu có (chỉ các phiên bản GCC gần đây).

#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
Bạn cần phải bao gồm #include <cxxabi.h>.
fuenfundachtzig

Hấp dẫn. Tôi có __cxa_demangle mà không xác định được OF_CXA_DEMANGLE
mkb

@Matt Ý tôi muốn nói là hệ thống xây dựng của chúng tôi, dựa trên autoconf, chỉ đặt OF_CXA_DEMANGLE nếu có.
KeithB

19
CẢNH BÁO! Đoạn mã trên có khả năng làm cho chương trình bị sập. Bộ đệm phải được cấp phát bởi malloc hoặc được chỉ định là NULL. KHÔNG phân bổ nó trên ngăn xếp. Xem mã của tôi bên dưới.
Ali

coi chừng, res có thể trả về NULL :)
Zibri

8

Ở đây, hãy xem type_strings.hpp, nó chứa một hàm thực hiện những gì bạn muốn.

Nếu bạn chỉ tìm kiếm một công cụ gỡ rối, mà bạn có thể sử dụng để xử lý nội dung hiển thị trong tệp nhật ký, hãy xem c++filt, công cụ này đi kèm với binutils. Nó có thể gỡ bỏ các tên ký hiệu C ++ và Java.


Chỉ cần lưu ý, cả cxa_demange () (mà mã được liên kết để sử dụng) và cx ++ filt đều là gcc cụ thể. Không có cách di động để làm điều này.
KeithB

c ++ filt không cắt nó, tôi cần thứ này (hoặc hầu hết nó) lúc biên dịch, chủ yếu được thực hiện với macro.
ga cuối

4
Liên kết tới type_strings.cpp dường như bị hỏng.
StackedCrooked,

1
Xin chào @GregoryPakosz Liên kết github trong nhận xét ở trên của bạn dường như cũng bị hỏng :( Chúc mừng
olibre

Chỉ là một FYI quan trọng: abi::__cxa_demangle()và ilk của nó <cxxabi.h> không dành riêng cho GCC - chúng có thể chỉ dành cho GCC trong quá khứ xa xôi, nhưng vào thời điểm viết bài này, <cxxabi.h>là một tiêu chuẩn đặc biệt cố định. Vì vậy, trong khi liên kết mã của câu trả lời là DOI, tôi có thể đảm bảo cho Clang cung cấp hỗ trợ hạng nhất trong trường hợp này… qv, từ libcxxabinguồn của Clang : khai báo tương ứng, impl, kiểm tra lớn: git.io/vRTBo , git.io/vRTBh , git.io/vRTRf - nhận xét của mã thử nghiệm lưu ý rằng việc triển khai Clang có khả năng gỡ rối nhiều hơn , bằng cách nào đó, so với GCC.
fish2000

4

Việc triển khai nó được xác định, vì vậy nó không phải là thứ gì đó sẽ di động được. Trong MSVC ++, name () là tên chưa được trang trí và bạn phải nhìn vào raw_name () để có được tên được trang trí.
Chỉ là một cú đâm trong bóng tối ở đây, nhưng dưới gcc, bạn có thể muốn xem xét sự phá sản.h


3

Không phải là một giải pháp hoàn chỉnh, nhưng bạn có thể muốn xem những gì một số macro tiêu chuẩn (hoặc được hỗ trợ rộng rãi) xác định. Thông thường trong mã ghi nhật ký để xem việc sử dụng các macro:

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);

4
Chưa kể PRETTY_FUNCTION .
CesarB

1
Điều này sẽ cung cấp cho bạn thông tin về vị trí của bạn trong mã. Câu hỏi đặt ra là một cái tên khá đẹp của một loại, như std :: vector.
KeithB

Anh ấy đề cập nó là để gỡ lỗi và tôi đã nói rằng nó không phải là một giải pháp hoàn chỉnh. Các macro khác như FUNCDNAME sẽ trả về tên được trang trí.
luke

Trên thực tế, đọc lại câu hỏi, đó là "Tôi hiện đang làm việc trên một số mã ghi nhật ký được cho là - trong số những thứ khác - in thông tin về chức năng gọi." Những công việc này.
Max Lybbert

Nó không hoàn chỉnh, vì tôi không biết vùng tên. Đây là tất cả đã có trong mã của tôi. Nhưng dù gì cũng cảm ơn.
ga cuối

3

Tôi cũng tìm thấy một macro được gọi __PRETTY_FUNCTION__, nó thực hiện thủ thuật. Nó cung cấp một tên chức năng khá đẹp (số liệu :)). Đây là những gì tôi cần.

Tức là nó mang lại cho tôi những điều sau:

virtual bool mutex::do_unlock()

Nhưng tôi không nghĩ rằng nó hoạt động trên các trình biên dịch khác.


Có, PRETTY_FUNCTION dành riêng cho gcc.
Greg Rogers

2

Một biến thể nhỏ về giải pháp của Ali. Nếu bạn muốn mã vẫn rất giống với

typeid(bla).name(),

viết cái này thay thế

Typeid(bla).name() (chỉ khác nhau ở chữ cái đầu tiên viết hoa)

thì bạn có thể quan tâm đến điều này:

Trong tệp 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 vẫn giống như trong giải pháp của Ali


1

Hãy xem __cxa_demanglemà bạn có thể tìm thấy tại cxxabi.h.


Tôi đã lấy, nó không được chấp nhận, theo thông báo tôi nhận được.
ga cuối

Bạn tìm thấy tin nhắn đó ở đâu? Tôi chỉ truy cập vào nó và nó dường như được ủng hộ, không có bằng chứng về việc bị phản đối.
Ali

Có thể nó không được chấp nhận trong :: không gian tên. Sử dụng abi :: __ cxa_demangle và bạn sẽ không nhận được cảnh báo. Bạn đang sử dụng gcc nào?
onitake,

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
CẢNH BÁO! Bộ đệm phải được cấp phát bởi malloc hoặc được chỉ định là NULL. KHÔNG phân bổ nó trên ngăn xếp. Xem mã của tôi bên dưới.
Ali

1

Các giải pháp được chấp nhận [1] hoạt động chủ yếu là tốt. Tôi đã tìm thấy ít nhất một trường hợp (và tôi sẽ không gọi nó là trường hợp góc) trong đó nó không báo cáo những gì tôi mong đợi ... với tài liệu tham khảo.

Đối với những trường hợp đó, tôi đã tìm thấy một giải pháp khác, được đăng ở dưới cùng.

Trường hợp có vấn đề (sử dụng typenhư được định nghĩa trong [1]):

int i = 1;
cout << "Type of " << "i" << " is " << type(i) << endl;
int & ri = i;
cout << "Type of " << "ri" << " is " << type(ri) << endl;

sản xuất

Type of i is int
Type of ri is int

Giải pháp (sử dụng type_name<decltype(obj)>(), xem mã bên dưới):

cout << "Type of " << "i" << " is " << type_name<decltype(i)>() << endl;
cout << "Type of " << "ri" << " is " << type_name<decltype(ri)>() << endl;

sản xuất

Type of i is int
Type of ri is int&

như mong muốn (ít nhất là của tôi)

. Nó phải nằm trong một tiêu đề được bao gồm, không phải trong một nguồn được biên dịch riêng biệt, do các vấn đề chuyên môn. Ví dụ: xem tham chiếu không xác định đến hàm mẫu .

#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

Tôi luôn muốn sử dụng type_info, nhưng tôi chắc chắn rằng kết quả của hàm thành viên name () là không chuẩn và sẽ không nhất thiết trả về bất kỳ thứ gì có thể được chuyển đổi thành kết quả có nghĩa.
Nếu bạn đang gắn bó với một trình biên dịch, có thể có một chức năng cụ thể của trình biên dịch sẽ làm những gì bạn muốn. Kiểm tra tài liệu.


0

Sau giải pháp của Ali, đây là giải pháp thay thế mẫu C ++ 11 hoạt động tốt nhất cho cách sử dụng của tôi.

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

Sử dụng:

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

Sẽ in:

double                                                                        
int                                                                           
test::SomeStruct
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.