Std :: type_info :: नाम के परिणाम को अनमैंगल करना


93

मैं वर्तमान में कुछ लॉगिंग कोड पर काम कर रहा हूं जो अन्य चीजों के अलावा - कॉलिंग फ़ंक्शन के बारे में जानकारी प्रिंट करना चाहता हूं। यह अपेक्षाकृत आसान होना चाहिए, मानक C ++ में एक type_infoवर्ग है। इसमें टाइपिड क्लास / फ़ंक्शन / आदि का नाम शामिल है। लेकिन यह आम है। यह बहुत उपयोगी नहीं है। यानी typeid(std::vector<int>).name()लौटता है St6vectorIiSaIiEE

क्या इसमें से कुछ उपयोगी उत्पादन करने का एक तरीका है? std::vector<int>उपरोक्त उदाहरण के लिए की तरह । यदि यह केवल गैर-टेम्पलेट कक्षाओं के लिए काम करता है, तो यह भी ठीक है।

समाधान gcc के लिए काम करना चाहिए, लेकिन बेहतर होगा कि मैं इसे पोर्ट कर सकूं। यह लॉगिंग के लिए है इसलिए यह इतना महत्वपूर्ण नहीं है कि इसे बंद नहीं किया जा सकता है, लेकिन यह डीबगिंग के लिए सहायक होना चाहिए।

जवाबों:


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

G ++ 4.7.2, g ++ 4.9.0 20140302 (प्रायोगिक), क्लैंग ++ 3.4 (ट्रंक 184647), क्लैंग 3.5 (ट्रंक 202594) लिनक्स 64 बिट और जी ++ 4.7.2 (मिंगव 32, 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 सितंबर 2013 से)

स्वीकार किए गए उत्तर (7 सितंबर, 2013 के अनुसार) , जब कॉल abi::__cxa_demangle()सफल होता है, तो एक पॉइंटर को एक स्थानीय, स्टैक आबंटित ऐरे ... रिटर्न के लिए लौटा देता है !
यह भी ध्यान दें कि यदि आप एक बफर प्रदान करते हैं, तो abi::__cxa_demangle()मान लें कि इसे ढेर पर आवंटित किया जाना है। स्टैक पर बफर आवंटित करना एक बग (ग्नू डॉक से) है: "यदि output_bufferलंबे समय तक पर्याप्त नहीं है, तो इसका उपयोग करके इसे विस्तारित किया जाता है realloc।" एक पॉइंटर को स्टैक पर कॉल realloc()करना ... ouch! ( इगोर स्कोकिंस्की की तरह की टिप्पणी भी देखें ।)

आप आसानी से इन कीड़ों के दोनों सत्यापित कर सकते हैं: सिर्फ बफर आकार स्वीकार किए जाते हैं जवाब में 1024 से छोटे कुछ करने के लिए, उदाहरण के लिए 16 को कम (7 सितं, 2013 के रूप में), और यह एक नाम के साथ कुछ देना नहीं 15 से अधिक समय (ताकि realloc()है नहीं कहा जाता है)। फिर भी, आपके सिस्टम और संकलक अनुकूलन के आधार पर, आउटपुट होगा: कचरा / कुछ भी नहीं / प्रोग्राम क्रैश।
दूसरी बग को सत्यापित करने के लिए: बफर आकार को 1 पर सेट करें और इसे उस चीज़ के साथ कॉल करें जिसका नाम 1 वर्ण से अधिक लंबा है। जब आप इसे चलाते हैं, तो प्रोग्राम लगभग आश्वस्त हो जाता है क्योंकि यह realloc()एक पॉइंटर को स्टैक के साथ कॉल करने का प्रयास करता है ।


(27 दिसंबर, 2010 से पुराना उत्तर)

कीथब के कोड में किए गए महत्वपूर्ण बदलाव : बफर को या तो मॉलोक द्वारा आवंटित किया जाना चाहिए या 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स्मृति का एक क्षेत्र, * लंबाई बाइट्स, जो में demangled नाम संग्रहीत किया जाता है की malloc, साथ आवंटित। यदि output_buffer लंबे समय तक पर्याप्त नहीं है, तो यह realloc का उपयोग करके विस्तारित किया जाता है। output_buffer इसके बजाय NULL हो सकता है; उस स्थिति में, ध्वस्त नाम को मैमोक के साथ आवंटित मेमोरी के क्षेत्र में रखा जाता है।
इगोर स्कोचिन्स्की

2
@IgorSkochinsky हां, मेरी पिछली टिप्पणी में एक टाइपो है लेकिन मैं उसे संपादित नहीं कर सकता। मैं क्या लिखना चाहता था: "पिछली बार जब मैंने जाँच abi::__cxa_demangleकी थी कि यह ढेर पर आवंटित होने की उम्मीद है " डॉक्टर को देखने के लिए बहुत बहुत धन्यवाद!
अली

1
ध्यान दें कि तकनीकी रूप से यह लीक हो सकता है यदि ret_valनिर्माण के दौरान फेंकता है। आप उस से बचाव के लिए स्कोप गार्ड का उपयोग कर सकते हैं।
GManNickG

3
यदि std::unique_ptr<char, decltype(&std::free)>आपके पॉइंटर के लिए हस्ताक्षर के रूप में उपयोग करना अधिक स्पष्ट होगा ।
माइंडवायरस वायरस 25'14

27

बूस्ट कोर में एक डैंगलर होता है। चेकआउट कोर / 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
अगर बढ़ावा एक विकल्प है, तो यह सबसे अच्छा तरीका है!
21

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>
फेनफुन्दाचट्ज़िग

दिलचस्प। मेरे पास __cxa_demangle बिना HAVE_CXA_DEMANGLE परिभाषित है
mkb

@ मैट का मेरे कहने का मतलब यह है कि ऑटोकॉन्फ़ पर आधारित हमारी बिल्ड प्रणाली, केवल HAVE_CXA_DEMANGLE को ही उपलब्ध कराती है।
कीथब नोव

19
चेतावनी! उपरोक्त कोड प्रोग्राम को क्रैश करने की संभावना है। बफर को या तो मॉलोक द्वारा आवंटित किया जाना चाहिए या NULL के रूप में निर्दिष्ट किया जाना चाहिए। इसे स्टैक पर आवंटित न करें। नीचे मेरा कोड देखें।
अली

बाहर देखो, Res वापस लौट सकते हैं :)
ज़िबरी

8

यहाँ, type_strings.hpp पर एक नज़र डालें, इसमें एक फ़ंक्शन होता है जो आपको चाहिए।

यदि आप बस एक डीमैन्गलिंग टूल की तलाश करते हैं, जिसका उपयोग आप लॉग फ़ाइल में दिखाए गए सामान को मसलने के लिए कर सकते हैं, तो एक नज़र डालें c++filt, जो कि बिनुटिल्स के साथ आता है। यह C ++ और Java प्रतीक नामों को अलग कर सकता है।


बस ध्यान दें, दोनों cxa_demange () (जो कोड का उपयोग करने से जुड़ा हुआ है) और cx ++ filt विशिष्ट हैं। ऐसा करने का कोई पोर्टेबल तरीका नहीं है।
कीथॉ

c ++ filt इसे नहीं काटता है, मुझे संकलन के समय इस सामान (या अधिकतर) की आवश्यकता है, ज्यादातर मैक्रोज़ के साथ किया जाता है।
टर्मिनस

4
Type_strings.cpp का लिंक टूटा हुआ लगता है।
स्टैकडॉक्ड

1
हाय @GregoryPakosz आपकी उपरोक्त टिप्पणी में github लिंक बहुत अधिक टूटी हुई लगती है :( चीयर्स
ओलिब्रे

बस एक महत्वपूर्ण FYI करें: abi::__cxa_demangle()और इसके ilk से GCC- <cxxabi.h> विशिष्ट नहीं हैं - वे जीसीसी केवल दूर के अतीत में हो सकते हैं, लेकिन इस पोस्ट के लेखन के समय तक, <cxxabi.h>एक अदद तदर्थ मानक था। इसलिए जब उत्तर का कोड लिंक DOI था, तो मैं इस मामले में प्रथम श्रेणी का समर्थन प्रदान करने वाले Clang के लिए प्रतिज्ञा कर सकता हूं ... qv, Clang के libcxxabiस्रोत से: संबंधित घोषणा, निहितार्थ, विशाल परीक्षण: git.io/vRTBo , git.io/vRTBh , git.io/vRTRf - परीक्षण कोड की टिप्पणियां क्लैग कार्यान्वयन को अधिक डीमैंग्लिंग, किसी तरह, बनाम जीसीसी के रूप में सक्षम करती हैं ।
फिश २२

4

यह कार्यान्वयन परिभाषित है, इसलिए यह ऐसा कुछ नहीं है जो पोर्टेबल हो। MSVC ++ में, नाम () अघोषित नाम है, और आपको सजाए जाने के लिए कच्चे_नाम () को देखना होगा।
यहाँ अंधेरे में बस एक छुरा, लेकिन gcc के तहत, आप demangle.h देखना चाह सकते हैं


3

संपूर्ण समाधान नहीं है, लेकिन आप यह देखना चाहते हैं कि मैक्रो के कुछ मानक (या व्यापक रूप से समर्थित) क्या हैं। मैक्रोज़ का उपयोग देखने के लिए लॉगिंग कोड में यह सामान्य है:

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);

4
PRETTY_FUNCTION का उल्लेख नहीं करना ।
सीजरबी

1
यह आपको जानकारी देगा कि आप कोड में कहां हैं। जो सवाल पूछ रहा था वह एक प्रकार का सुंदर नाम था, जैसे std :: वेक्टर।
कीथबी

उन्होंने कहा कि यह डिबगिंग के लिए था, और मैंने कहा कि यह एक पूर्ण समाधान नहीं था। अन्य मैक्रोज़ जैसे कि FUNCDNAME सजाए गए नाम को वापस कर देगा।
ल्यूक

वास्तव में, प्रश्न को फिर से पढ़ना, यह था "मैं वर्तमान में कुछ लॉगिंग कोड पर काम कर रहा हूं जो कि माना जाता है - अन्य चीजों के बीच - कॉलिंग फ़ंक्शन के बारे में जानकारी प्रिंट करें।" यह काम।
मैक्स लाइबबर्ट

यह पूरा नहीं है, क्योंकि मैं नाम स्थान नहीं जानता। यह मेरे कोड में पहले से ही है। लेकिन फिर भी धन्यवाद।
टर्मिनस

3

मुझे एक मैक्रो भी मिला __PRETTY_FUNCTION__, जिसे ट्रिक कहा जाता है । यह एक सुंदर फ़ंक्शन नाम देता है (आंकड़े :))। यह वही है जो मुझे चाहिए था।

यानी यह मुझे निम्नलिखित देता है:

virtual bool mutex::do_unlock()

लेकिन मुझे नहीं लगता कि यह अन्य संकलक पर काम करता है।


हां, PRETTY_FUNCTION विशिष्ट है।
ग्रेग रोजर्स

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 अली के समाधान के समान है


1

एक नज़र डालें __cxa_demangleजिस पर आप पा सकते हैं cxxabi.h


मैंने लिया, यह मेरे द्वारा प्राप्त संदेश के अनुसार, पदावनत है।
टर्मिनस

आपको वह संदेश कहां मिला? मैंने सिर्फ इसे देखा और इसका समर्थन किया जा रहा है, इसका कोई सबूत नहीं है।
अली

शायद यह :: नामस्थान में पदावनत है। Abi :: __ cxa_demangle का उपयोग करें और आपको चेतावनी नहीं मिलेगी। आप किस gcc का उपयोग कर रहे हैं?
1

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
चेतावनी! बफर को या तो मॉलोक द्वारा आवंटित किया जाना चाहिए या NULL के रूप में निर्दिष्ट किया जाना चाहिए। इसे स्टैक पर आवंटित न करें। नीचे मेरा कोड देखें।
अली

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 का उपयोग करना चाहता हूं, लेकिन मुझे यकीन है कि नाम () सदस्य फ़ंक्शन का परिणाम गैर-मानक है और जरूरी कुछ भी नहीं लौटाएगा जो कि सार्थक परिणाम में परिवर्तित हो सकता है।
यदि आप एक कंपाइलर से चिपके हुए हैं, तो हो सकता है कि एक कंपाइलर विशिष्ट फंक्शन हो जो आपको चाहिए। प्रलेखन की जाँच करें।


0

अली के समाधान के बाद, यहां 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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.