Unmangling wynik std:: type info:: name

Obecnie pracuję nad kodem logowania, który ma m.in. drukować informacje o wywołującej funkcji. Powinno to być stosunkowo proste, standard C++ ma klasę type_info. Zawiera nazwę klasy/funkcji/etc. ale jest pokaleczona. To nie jest zbyt przydatne. Tzn. typeid(std::vector<int>).name() zwraca St6vectorIiSaIiEE.

Czy jest sposób, aby stworzyć coś użytecznego z tego? Jak std::vector<int> dla powyższego przykładu. Jeśli działa tylko dla klas innych niż template, to w porządku też.

Rozwiązanie powinno działać dla gcc, ale byłoby lepiej, gdybym mógł go przenieść. Jest do logowania, więc nie jest tak ważne, że nie można go wyłączyć, ale powinno być pomocne przy debugowaniu.

Author: Jeffrey Bosboom, 2008-11-11

13 answers

Biorąc pod uwagę uwagę, jaką otrzymuje to pytanie / odpowiedź , oraz cenne opinie od GManNickG , trochę wyczyściłem kod. Dostępne są dwie wersje: jedna z funkcjami C++11 i druga z funkcjami C++98.

W pliku wpisz.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

W pliku wpisz.cpp (wymaga 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

Użycie:

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

Drukuje:

Typ ptr_base: Base*
Rodzaj pointee: Derived

Testowane z g++ 4.7.2, g++ 4.9.0 20140302 (experimental), clang++ 3.4 (trunk 184647), clang 3.5 (trunk 202594) na Linuksie 64 bit i g++ 4.7.2 (mingw32, Win32 XP SP2).

Jeśli nie możesz używać funkcji C++11, Oto jak można to zrobić w C++98, typu plik .cpp jest teraz:

#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


(Aktualizacja z września 8, 2013)

Zaakceptowana odpowiedź (stan na Wrzesień 7, 2013), gdy wywołanie abi::__cxa_demangle() powiedzie się, zwraca wskaźnik do Lokalnej Tablicy alokowanej przez stos ... AUĆ!
Zauważ również, że jeśli podasz bufor, abi::__cxa_demangle() zakłada, że zostanie on przydzielony na stercie. Przydzielanie bufora na stos jest błędem (z gnu doc): "jeśli output_buffer nie jest wystarczająco długi, jest rozszerzany za pomocą realloc." wywołanie realloc() na wskaźniku do stosu ... AUĆ! (Zobacz też miły komentarz Igora Skoczyńskiego.)

Możesz łatwo zweryfikować oba te błędy: po prostu zmniejsz rozmiar bufora w przyjmuje odpowiedź (od 7 września 2013) od 1024 do czegoś mniejszego, na przykład 16, i nadaje mu coś o nazwie nie dłuższej niż 15(więc realloc() jest nie nazywa). Mimo to, w zależności od systemu i optymalizacji kompilatora, wynik będzie: garbage / nothing / program crash.
Aby zweryfikować drugi błąd: Ustaw rozmiar bufora na 1 i wywołaj go z czymś, którego nazwa jest dłuższa niż 1 znak. Kiedy go uruchamiasz, program prawie na pewno się zawiesza, ponieważ próbuje wywołać realloc() ze wskaźnikiem do stosu.


(stara odpowiedź z grudnia 27, 2010)

Ważne zmiany w kodzie Keithba: bufor musi być alokowany przez malloc lub określony jako NULL. nie przydzielaj go na stosie.

Dobrze jest sprawdzić również ten status.

Nie udało mi się znaleźć HAVE_CXA_DEMANGLE. Sprawdzam __GNUG__, chociaż nie gwarantuje to, że Kod się skompiluje. Każdy ma lepsze pomysł?

#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;
}
 120
Author: Ali,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:05

Boost core zawiera demangler. 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>
}

To w zasadzie tylko opakowanie dla abi::__cxa_demangle, jak sugerowano wcześniej.

 30
Author: moof2k,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-21 06:31:40

Tego właśnie używamy. HAVE_CXA_DEMANGLE jest ustawiane tylko, jeśli jest dostępne (tylko najnowsze wersje 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  
 14
Author: KeithB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-11 19:21:39

Tutaj, spójrz na type_strings.HPP zawiera funkcję, która robi to, co chcesz.

Jeśli szukasz narzędzia do demanglingu, którego np. możesz użyć do zmanipulowania rzeczy pokazanych w pliku dziennika, spójrz na c++filt, który jest dostarczany z binutils. Może demangle C++ i Java nazwy symboli.

 9
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-01 11:25:49

To implementacja zdefiniowana, więc nie jest to coś, co będzie przenośne. W MSVC++ nazwa() jest niedekorowaną nazwą i musisz spojrzeć na raw_name (), aby uzyskać dekorowaną nazwę.
To tylko dźgnięcie w ciemności, ale pod gcc, możesz chcieć spojrzeć na demangle.h

 5
Author: Eclipse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-11 19:06:14

Znalazłem również makro o nazwie __PRETTY_FUNCTION__, które robi sztuczkę. Daje ładną nazwę funkcji (figury:)). Tego potrzebowałem.

Czyli daje mi to:

virtual bool mutex::do_unlock()

Ale chyba nie działa na innych kompilatorach.

 3
Author: terminus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-17 23:54:56

Nie jest to kompletne rozwiązanie, ale warto przyjrzeć się definicjom niektórych standardowych (lub powszechnie obsługiwanych) makr. Często w kodzie logowania widać użycie makr:

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);
 2
Author: luke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-12 02:14:22

Mała wariacja na temat rozwiązania Ali. Jeśli chcesz, aby Kod był nadal bardzo podobny do

typeid(bla).name(),

Pisanie tego zamiast

Typeid(bla).name() (różni się tylko wielką literą)

W takim razie może cię to zainteresować:

W pliku Wpisz.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

Typ.cpp pozostaje taki sam jak w rozwiązaniu Ali

 2
Author: matzzz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-13 13:38:56

Spójrz na __cxa_demangle, które znajdziesz na cxxabi.h.

 1
Author: CesarB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-11 19:13:36
// 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
Author: Dan Dare,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-12 18:57:48

Przyjęte rozwiązanie [1] działa głównie dobrze. Znalazłem przynajmniej jeden przypadek (i nie nazwałbym go przypadkiem narożnym), w którym nie zgłasza tego, czego się spodziewałem... z referencjami.

W tych przypadkach znalazłem inne rozwiązanie, umieszczone na dole.

Problematyczny przypadek (używając {[5] } zgodnie z definicją w [1]):

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

Produkuje

Type of i is int
Type of ri is int

Rozwiązanie (używając type_name<decltype(obj)>(), patrz kod poniżej):

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

Produkuje

Type of i is int
Type of ri is int&

Zgodnie z życzeniem (przynajmniej przeze mnie)

Kod . Musi być w dołączonym nagłówku, a nie w osobno skompilowanym źródle, ze względu na problemy ze specjalizacją. Zobacz na przykład undefined reference to template function .

#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;
}
 1
Author: sancho.s ReinstateMonicaCellio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-20 09:26:23

Zawsze chciałem użyć type_info, ale jestem pewien, że wynik funkcji member name() jest niestandardowy i niekoniecznie zwróci coś, co można przekonwertować na znaczący wynik.
Jeśli trzymasz się jednego kompilatora, może istnieje specjalna funkcja kompilatora, która zrobi to, co chcesz. Sprawdź dokumentację.

 0
Author: quamrana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-11 19:02:31

Idąc za rozwiązaniem Ali, oto C++11 template alternatywa, która działała najlepiej w moim użyciu.

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

Użycie:

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

Wydrukuje:

double                                                                        
int                                                                           
test::SomeStruct
 0
Author: Alexis Paques,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-19 08:04:47