web-dev-qa-db-fra.com

Démêler le résultat de std :: type_info :: name

Je travaille actuellement sur un code de journalisation censé, entre autres, imprimer des informations sur la fonction d'appel. Cela devrait être relativement facile, le C++ standard a une classe type_info. Il contient le nom de la classe/fonction/etc de typeid'd. mais il est mutilé. Ce n'est pas très utile. C'est à dire. typeid(std::vector<int>).name() renvoie St6vectorIiSaIiEE.

Existe-t-il un moyen de produire quelque chose d'utile à partir de cela? Comme std::vector<int> Pour l'exemple ci-dessus. Si cela ne fonctionne que pour les classes non modèles, c'est bien aussi.

La solution devrait fonctionner pour gcc, mais ce serait mieux si je pouvais la porter. C'est pour la journalisation, donc ce n'est pas si important qu'il ne puisse pas être désactivé, mais cela devrait être utile pour le débogage.

82
terminus

Étant donné l'attention que cette question/réponse reçoit et les précieux commentaires de GManNickG , j'ai un peu nettoyé le code. Deux versions sont proposées: une avec des fonctionnalités C++ 11 et une autre avec uniquement des fonctionnalités C++ 98.

Dans le fichier tapez.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

Dans le fichier type.cpp (nécessite 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

Usage:

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

Il imprime:

Type de ptr_base: Base*
Type de pointe: Derived

Testé avec g ++ 4.7.2, g ++ 4.9.0 20140302 (expérimental), clang ++ 3.4 (trunk 184647), clang 3.5 (trunk 202594) sur Linux 64 bits et g ++ 4.7.2 (Mingw32, Win32 XP SP2 ).

Si vous ne pouvez pas utiliser les fonctionnalités C++ 11, voici comment cela peut être fait en C++ 98, le fichier type.cpp est maintenant:

#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


(Mise à jour du 8 sept. 2013)

La réponse acceptée (au 7 septembre 2013) , lorsque l'appel à abi::__cxa_demangle() est réussi, renvoie un pointeur vers une pile locale tableau alloué ... aïe!
Notez également que si vous fournissez un tampon, abi::__cxa_demangle() suppose qu'il est alloué sur le tas. L'allocation du tampon sur la pile est un bug (du doc ​​gnu): "Si output_buffer N'est pas assez long, il est développé en utilisant realloc." Appel de realloc() sur un pointeur vers la pile ... aïe! (Voir aussi Igor Skochinsky commentaire aimable de .=)

Vous pouvez facilement vérifier ces deux bogues: réduisez simplement la taille de la mémoire tampon dans la réponse acceptée (au 7 septembre 2013) de 1024 à quelque chose de plus petit, par exemple 16, et donnez-lui quelque chose avec un nom pas supérieur à 15 (donc realloc() est pas appelé). Pourtant, selon votre système et les optimisations du compilateur, la sortie sera: garbage/rien/plantage du programme.
Pour vérifier le deuxième bogue: définissez la taille de la mémoire tampon sur 1 et appelez-la avec quelque chose dont le nom dépasse 1 caractère. Lorsque vous l'exécutez, le programme se bloque presque certainement lorsqu'il essaie d'appeler realloc() avec un pointeur sur la pile.


(L'ancienne réponse du 27 décembre 2010)

Modifications importantes apportées à code KeithB : le tampon doit être alloué par malloc ou spécifié comme NULL. Ne l'allouez PAS sur la pile.

Il est également sage de vérifier ce statut.

Je n'ai pas trouvé HAVE_CXA_DEMANGLE. Je vérifie __GNUG__ Bien que cela ne garantisse pas que le code sera même compilé. Quelqu'un a une meilleure idée?

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

Boost core contient un démangleur. Commander 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>
}

C'est simplement un wrapper pour abi::__cxa_demangle, comme cela a été suggéré précédemment.

21
moof2k

C'est ce que nous utilisons. HAVE_CXA_DEMANGLE n'est défini que s'il est disponible (versions récentes de GCC uniquement).

#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  
11
KeithB

Ici, jetez un œil à type_strings.hpp il contient une fonction qui fait ce que vous voulez.

Si vous cherchez juste un outil de démêlage, que vous p. Ex. pourrait utiliser pour réduire les éléments affichés dans un fichier journal, jetez un œil à c++filt, qui vient avec binutils. Il peut démêler C++ et Java noms de symboles.

8

Ce n'est pas une solution complète, mais vous voudrez peut-être regarder ce que certaines des macros standard (ou largement prises en charge) définissent. Il est courant dans le code de journalisation de voir l'utilisation des macros:

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);
5
luke

C'est l'implémentation définie, donc ce n'est pas quelque chose qui va être portable. Dans MSVC++, name () est le nom non décoré, et vous devez regarder raw_name () pour obtenir celui décoré.
Juste un coup de couteau dans le noir ici, mais sous gcc, vous voudrez peut-être regarder demangle.h

4
Eclipse

J'ai également trouvé une macro appelée __PRETTY_FUNCTION__, qui fait l'affaire. Il donne un joli nom de fonction (chiffres :)). Voilà ce dont j'avais besoin.

C'est à dire. cela me donne ce qui suit:

virtual bool mutex::do_unlock()

Mais je ne pense pas que cela fonctionne sur d'autres compilateurs.

3
terminus

Une légère variation sur la solution d'ALi. Si vous souhaitez que le code reste très similaire à

typeid(bla).name(),

écrire ceci à la place

Typeid(bla).name() (ne diffère qu'en majuscule)

alors cela pourrait vous intéresser:

Dans le fichier 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 reste le même que dans la solution d'ALi

2
matzzz

Jeter un coup d'œil à __cxa_demangle que vous pouvez trouver sur cxxabi.h.

1
CesarB
// 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
Dan Dare

solution acceptée [1] fonctionne généralement bien. J'ai trouvé au moins un cas (et je ne l'appellerais pas un cas de coin) où il ne rapporte pas ce que j'attendais ... avec des références.

Pour ces cas, j'ai trouvé une autre solution, affichée en bas.

Cas problématique (en utilisant type comme défini dans [1]):

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

produit

Type of i is int
Type of ri is int

Solution (en utilisant type_name<decltype(obj)>(), voir code ci-dessous):

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

produit

Type of i is int
Type of ri is int&

comme souhaité (au moins par moi)

Code . Il doit être dans un en-tête inclus, pas dans une source compilée séparément, en raison de problèmes de spécialisation. Voir référence non définie à la fonction de modèle par exemple.

#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
sancho.s

J'ai toujours voulu utiliser type_info, mais je suis sûr que le résultat de la fonction membre name () n'est pas standard et ne retournera pas nécessairement tout ce qui peut être converti en un résultat significatif.
Si vous vous en tenez à un seul compilateur, il y a peut-être une fonction spécifique au compilateur qui fera ce que vous voulez. Consultez la documentation.

0
quamrana