การยกเลิกการเชื่อมโยงผลลัพธ์ของ std :: type_info :: name


97

ฉันกำลังทำงานกับรหัสบันทึกบางอย่างที่ควรจะพิมพ์ข้อมูลเกี่ยวกับฟังก์ชันการโทร สิ่งนี้ควรจะค่อนข้างง่าย C ++ มาตรฐานมีtype_infoคลาส ซึ่งมีชื่อของ typeid'd class / function / etc แต่มันแหลกเหลว มันไม่มีประโยชน์มาก คือtypeid(std::vector<int>).name()ผลตอบแทนSt6vectorIiSaIiEE.

มีวิธีการผลิตสิ่งที่เป็นประโยชน์จากสิ่งนี้หรือไม่? เช่นเดียวstd::vector<int>กับตัวอย่างข้างต้น หากใช้ได้เฉพาะกับคลาสที่ไม่ใช่เทมเพลตก็ใช้ได้เช่นกัน

วิธีแก้ปัญหาควรใช้ได้กับ gcc แต่จะดีกว่าถ้าฉันสามารถพอร์ตได้ สำหรับการบันทึกจึงไม่สำคัญมากจนไม่สามารถปิดได้ แต่ควรมีประโยชน์สำหรับการดีบัก

คำตอบ:


119

เมื่อได้รับความสนใจจากคำถาม / คำตอบนี้และข้อเสนอแนะที่มีค่าจาก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

ทดสอบด้วย g ++ 4.7.2, g ++ 4.9.0 20140302 (ทดลอง), clang ++ 3.4 (trunk 184647), clang 3.5 (trunk 202594) บน Linux 64 บิตและ 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


(อัปเดตเมื่อวันที่ 8 ก.ย. 2556)

คำตอบที่ได้รับการยอมรับ (ณ วันที่ 7 กันยายน 2556)เมื่อการโทรabi::__cxa_demangle()สำเร็จจะส่งกลับตัวชี้ไปยังอาร์เรย์ที่จัดสรรแบบสแต็กภายในเครื่อง ... อุ๊ย!
โปรดทราบว่าหากคุณจัดเตรียมบัฟเฟอร์ให้abi::__cxa_demangle()ถือว่าบัฟเฟอร์ถูกจัดสรรบนฮีป การจัดสรรบัฟเฟอร์บนสแต็กเป็นจุดบกพร่อง (จากเอกสาร gnu): "ถ้าoutput_bufferไม่ยาวพอจะขยายโดยใช้realloc" เรียกrealloc()ตัวชี้ไปที่กอง ... อุ๊ย! (ดูความคิดเห็นของIgor Skochinskyด้วย)

คุณสามารถตรวจสอบข้อบกพร่องทั้งสองนี้ได้อย่างง่ายดาย: เพียงแค่ลดขนาดบัฟเฟอร์ในคำตอบที่ยอมรับ (ณ วันที่ 7 กันยายน 2013) จาก 1024 เป็นขนาดที่เล็กกว่าเช่น 16 และตั้งชื่อที่มีความยาวไม่เกิน 15 (ดังนั้นrealloc()คือไม่เรียก) อย่างไรก็ตามขึ้นอยู่กับระบบของคุณและการปรับแต่งคอมไพลเลอร์ผลลัพธ์จะเป็น: ขยะ / ไม่มีอะไร / โปรแกรมขัดข้อง
ในการตรวจสอบข้อบกพร่องที่สอง: ตั้งค่าขนาดบัฟเฟอร์เป็น 1 และเรียกมันด้วยชื่อที่ยาวกว่า 1 อักขระ เมื่อคุณเรียกใช้โปรแกรมเกือบจะล้มเหลวอย่างแน่นอนเนื่องจากพยายามเรียกrealloc()ด้วยตัวชี้ไปที่สแต็ก


(คำตอบเก่าเมื่อวันที่ 27 ธันวาคม 2553)

การเปลี่ยนแปลงที่สำคัญที่ทำกับรหัสของ 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

2
จากเอกสาร : output_bufferขอบเขตของหน่วยความจำที่จัดสรรด้วย malloc ที่มีความยาว * ไบต์ซึ่งจะจัดเก็บชื่อแบบแยกส่วน หาก output_buffer ยาวไม่เพียงพอจะขยายโดยใช้ realloc output_buffer อาจเป็น NULL แทน ในกรณีนั้นชื่อ demangled จะถูกวางไว้ในพื้นที่ของหน่วยความจำที่จัดสรรด้วย malloc
Igor Skochinsky

2
@IgorSkochinsky ใช่มีการพิมพ์ผิดในความคิดเห็นก่อนหน้านี้ แต่ฉันไม่สามารถแก้ไขได้ สิ่งที่ฉันอยากจะเขียน: "ครั้งที่แล้วฉันตรวจสอบแล้วabi::__cxa_demangleคาดว่าจะได้รับการจัดสรรในฮีป " ขอบคุณมากสำหรับการค้นหาเอกสาร!
Ali

1
โปรดทราบว่าในทางเทคนิคสิ่งนี้สามารถรั่วไหลได้หากมีการret_valขว้างปาระหว่างการก่อสร้าง คุณสามารถใช้ตัวป้องกันขอบเขตเพื่อป้องกันสิ่งนั้นได้
GManNickG

3
ถ้าอาจจะชัดเจนกว่าที่จะใช้std::unique_ptr<char, decltype(&std::free)>เป็นลายเซ็นสำหรับตัวชี้ของคุณ
mindvirus

29

Boost core มี 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>
}

โดยพื้นฐานแล้วเป็นเพียงกระดาษห่อหุ้ม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

น่าสนใจ. ฉันมี __cxa_demangle โดยไม่ได้กำหนด HAVE_CXA_DEMANGLE
mkb

@Matt สิ่งที่ฉันต้องการจะพูดก็คือระบบสร้างของเราที่ใช้ autoconf จะตั้งค่า HAVE_CXA_DEMANGLE เท่านั้นหากมี
KeithB

19
คำเตือน! โค้ดด้านบนมีแนวโน้มที่จะทำให้โปรแกรมขัดข้อง บัฟเฟอร์จะต้องถูกจัดสรรโดย malloc หรือระบุเป็น NULL อย่าจัดสรรไว้ในสแต็ก ดูรหัสของฉันด้านล่าง
อาลี

ระวัง res สามารถคืนค่า NULL :)
Zibri

9

ดูที่type_strings.hpp ที่นี่มีฟังก์ชันที่ทำสิ่งที่คุณต้องการ

หากคุณเพียงแค่มองหาเครื่องมือ demangling ซึ่งคุณสามารถใช้เพื่อทำลายสิ่งต่างๆที่แสดงในไฟล์บันทึกได้ลองดูc++filtซึ่งมาพร้อมกับ binutils สามารถแยกชื่อสัญลักษณ์ C ++ และ Java


โปรดทราบว่าทั้ง cxa_demange () (ซึ่งรหัสที่เชื่อมโยงกับการใช้งาน) และ cx ++ filt เป็น gcc ไม่มีวิธีพกพาในการทำเช่นนี้
KeithB

c ++ filt ไม่ได้ตัดมันฉันต้องการสิ่งนี้ (หรือส่วนใหญ่) ในเวลาคอมไพล์ส่วนใหญ่ทำด้วยมาโคร
ปลายทาง

4
ดูเหมือนว่าลิงก์ไปยัง type_strings.cpp จะเสีย
StackedCrooked

1
สวัสดี @GregoryPakosz ลิงก์ github ในความคิดเห็นด้านบนของคุณดูเหมือนจะเสียด้วย :( ไชโย
oHo

เป็นเพียง FYI ที่สำคัญ: abi::__cxa_demangle()และ ilk จาก<cxxabi.h> ไม่เฉพาะ GCC - อาจเป็น GCC เท่านั้นในอดีตอันไกลโพสต์ แต่ในช่วงเวลาแห่งการเขียนโพสต์นี้เป็น<cxxabi.h>มาตรฐานเฉพาะกิจที่ยึดมั่น ดังนั้นในขณะที่ลิงก์รหัสของคำตอบคือ DOI ฉันสามารถรับรองได้ว่า Clang ให้การสนับสนุนระดับเฟิร์สคลาสในกรณีนี้… qv จากlibcxxabiแหล่งที่มาของ Clang : การปฏิเสธตามลำดับการทดสอบขนาดใหญ่: git.io/vRTBo , git.io/vRTBh , git.io/vRTRf - ความคิดเห็นของโค้ดทดสอบระบุว่าการใช้งาน Clang มีความสามารถในการแยกส่วนมากกว่าอย่างใดเทียบกับ GCC
fish2000

5

มีการกำหนดการใช้งานดังนั้นจึงไม่ใช่สิ่งที่จะพกพาได้ ใน MSVC ++ ชื่อ () เป็นชื่อที่ไม่ได้ตกแต่งและคุณต้องดูที่ raw_name () เพื่อรับชื่อที่ตกแต่ง
แค่แทงในความมืดที่นี่ แต่ภายใต้ gcc คุณอาจต้องการดูdemangle.h


3

ฉันยังพบมาโครที่เรียกว่า__PRETTY_FUNCTION__ซึ่งเป็นเคล็ดลับ มันให้ชื่อฟังก์ชั่นสวย ๆ (ตัวเลข :)) นี่คือสิ่งที่ฉันต้องการ

กล่าวคือให้สิ่งต่อไปนี้แก่ฉัน:

virtual bool mutex::do_unlock()

แต่ฉันไม่คิดว่ามันใช้ได้กับคอมไพเลอร์อื่น ๆ


ใช่PRETTY_FUNCTIONเป็น gcc เฉพาะ
Greg Rogers

2

ไม่ใช่วิธีแก้ปัญหาที่สมบูรณ์ แต่คุณอาจต้องการดูว่ามาโครมาตรฐาน (หรือได้รับการสนับสนุนอย่างกว้างขวาง) กำหนดอะไรบ้าง เป็นเรื่องปกติในการบันทึกรหัสเพื่อดูการใช้มาโคร:

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);

4
ไม่ต้องพูดถึงPRETTY_FUNCTION
CesarB

1
ข้อมูลนี้จะให้ข้อมูลเกี่ยวกับตำแหน่งที่คุณอยู่ในโค้ด สิ่งที่ถามคือชื่อประเภทที่น่ารักเช่น std :: vector
KeithB

เขาบอกว่ามันใช้สำหรับการดีบักและฉันบอกว่ามันไม่ใช่วิธีแก้ปัญหาที่สมบูรณ์ มาโครอื่น ๆ เช่นFUNCDNAMEจะส่งคืนชื่อที่ตกแต่ง
luke

ที่จริงแล้วอ่านคำถามอีกครั้งคือ "ฉันกำลังทำงานกับรหัสบันทึกบางอย่างที่ควรจะพิมพ์ข้อมูลเกี่ยวกับฟังก์ชันการโทร" นี้ได้ผล
Max Lybbert

ยังไม่สมบูรณ์เนื่องจากฉันไม่รู้จักเนมสเปซ นี่คือทั้งหมดในรหัสของฉัน แต่ยังไงก็ตามขอบคุณ
ปลายทาง

2

การเปลี่ยนแปลงเล็กน้อยในการแก้ปัญหาของ 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


1

ลองดูที่ซึ่งคุณสามารถพบได้ที่__cxa_demanglecxxabi.h


ฉันรับมันเลิกใช้แล้วตามข้อความที่ฉันได้รับ
ปลายทาง

คุณพบข้อความนั้นที่ไหน ฉันเพิ่ง googled และดูเหมือนว่าจะได้รับการสนับสนุนไม่มีหลักฐานว่าถูกเลิกใช้งาน
อาลี

อาจจะเลิกใช้ใน :: namespace ใช้ abi :: __ cxa_demangle แล้วคุณจะไม่ได้รับคำเตือน คุณใช้ gcc อะไร
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
คำเตือน! บัฟเฟอร์จะต้องถูกจัดสรรโดย malloc หรือระบุเป็น NULL อย่าจัดสรรไว้ในสแต็ก ดูรหัสของฉันด้านล่าง
Ali

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.