2008-11-11 32 views
69

Tôi hiện đang làm việc trên một số mã đăng nhập mà nghĩa vụ phải - trong số những thứ khác - in thông tin về chức năng gọi. Điều này tương đối dễ, tiêu chuẩn C++ có lớp type_info. Nó chứa tên của lớp/hàm/etc typeid'd. nhưng nó bị xáo trộn. Nó không phải là rất hữu ích. I E. typeid(std::vector<int>).name() trả về St6vectorIiSaIiEE.Unmangling kết quả của std :: type_info :: name

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> cho ví dụ trên. Nếu nó chỉ hoạt động đối với các lớp không phải mẫu, thì cũng tốt.

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

Trả lời

86

Được sự chú ý của câu hỏi/câu trả lời này 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 phiên bản 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 loại.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 file 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 

Cách 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; 
} 

It in:

Loại ptr_base: Base*
Loại pointee: Derived

Tested với g ++ 4.7.2, g ++ 4.9.0 20.140.302 (thử nghiệm), kêu vang ++ 3.4 (trunk 184.647), Clang 3,5 (trunk 202.594) 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++ 11 tính năng, đây là cách nó có thể được thực hiện trong C++ 98, file 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ừ tháng 8, 2013)

The accepted answer (as of Sep 7, 2013), khi cuộc gọi đến abi::__cxa_demangle() thành công, trả về một con trỏ tới một mảng cục bộ, được phân bổ ngăn xếp ... ouch!
Cũng lưu ý rằng nếu bạn cung cấp bộ đệm, abi::__cxa_demangle() giả sử nó được cấp phát trên heap. Phân bổ bộ đệm trên ngăn xếp là lỗi (từ tài liệu gnu): "Nếu output_buffer không đủ dài, nó sẽ được mở rộng bằng cách sử dụng realloc".Gọi realloc() trên con trỏ tới ngăn xếp ... ouch! (Xem thêm Igor Skochinsky 's comment bình luận.)

Bạn có thể dễ dàng xác minh cả hai lỗi này: 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 ngày 7 tháng 9 năm 2013) từ 1024 xuống nhỏ hơn, ví dụ 16, và đặt tên cho nó có tên không phải là dài hơn 15 (vì vậy realloc()không phải là đượ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, đầu ra sẽ là: garbage/nothing/program crash.
Để 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ó độ dài hơn 1 ký tự. Khi bạn chạy nó, chương trình gần như chắc chắn đổ vỡ khi nó cố gắng gọi realloc() với một con trỏ đến ngăn xếp.


(Câu trả lời cũ từ tháng 27, 2010)

thay đổi quan trọng thực hiện để KeithB's code: bộ đệm đã được hoặc được phân bổ bởi malloc hoặc quy định như NULL. KHÔNG phân bổ nó trên ngăn xếp.

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

Tôi không thể 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ã thậm chí sẽ 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; 
} 
+0

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

+0

Rất tiếc, xin lỗi. Cố định và cảm ơn! – Ali

+0

Bạn có thể giải thích tại sao bộ đệm không thể được cấp phát trên ngăn xếp không? Bởi vì cho đến nay nó đã làm việc tốt cho tôi. – terminus

4

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

0

Tôi đã luôn 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 member Tên() là không chuẩn và sẽ không nhất thiết trả lại 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ể một hàm trình biên dịch cụ thể sẽ làm những gì bạn muốn. Kiểm tra tài liệu.

1

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

+0

Tôi đã sử dụng, nó không được chấp nhận, theo thông báo tôi nhận được. – terminus

+0

Bạn tìm thấy thông báo đó ở đâu? Tôi chỉ googled nó và nó có vẻ được hỗ trợ, không có bằng chứng về việc không được chấp nhận. – Ali

+0

Có thể nó không được chấp nhận trong :: namespace. 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

14

Đây là những gì chúng tôi sử dụng. HAVE_CXA_DEMANGLE chỉ được đặt nếu có (các phiên bản gần đây của 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 
+5

Bạn cần bao gồm '#include '. – fuenfundachtzig

+0

Thú vị. Tôi có __cxa_demangle không có HAVE_CXA_DEMANGLE được định nghĩa – mkb

+0

@Matt Điều 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 HAVE_CXA_DEMANGLE nếu có sẵn. – KeithB

5

Không phải là giải pháp hoàn chỉnh, nhưng bạn có thể muốn xem xét một số định nghĩa của macro chuẩn (hoặc được hỗ trợ rộng rãi). 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); 
+3

Chưa kể __PRETTY_FUNCTION__. – CesarB

+0

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

+0

Ông đã đề cập đến nó là để gỡ lỗi, và tôi tuyên bố 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 trang trí. – luke

8

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

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

+0

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

+0

C++ filt không cắt nó, tôi cần công cụ này (hoặc hầu hết của nó) tại thời gian biên dịch, chủ yếu được thực hiện với các macro. – terminus

+4

Liên kết đến type_strings.cpp có vẻ bị hỏng. – StackedCrooked

3

Tôi cũng tìm thấy macro có tên là __PRETTY_FUNCTION__. Nó cho một tên hàm khá (hình :)). Đây là những gì tôi cần.

I.e. nó mang lại cho tôi những điều sau đây:

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.

+0

Có, __PRETTY_FUNCTION__ là gcc cụ thể. –

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; 
    } 
+8

CẢNH BÁO! Bộ đệm phải được phân bổ 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 dưới đây. – Ali

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 này để thay thế

Typeid(bla).name() (chỉ khác nhau về vốn chữ cái đầu tiên)

sau đó bạn có thể quan tâm đến việc 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 ở lại giống như trong giải pháp của Ali

10

Boost lõi chứa một demangler. Thanh toán 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> 
} 

Đó là về cơ bản chỉ là một wrapper cho abi::__cxa_demangle, như đã được đề xuất trước đó.

+1

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

Các vấn đề liên quan