Question

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

Y at-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, il n’est donc pas si important de ne pas l’éteindre, mais cela devrait être utile pour le débogage.

Était-ce utile?

La solution

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

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

#endif

Dans le fichier type.cpp (requiert 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

Utilisation:

#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) sous 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 procéder en C ++ 98: le fichier type.cpp est à présent:

#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 septembre 2013)

La réponse acceptée (à partir du 7 sept. 2013) , lorsque l'appel à abi::__cxa_demangle() a abouti, < strong> renvoie un pointeur sur un tableau local alloué par pile ... ouch!
Notez également que si vous fournissez un tampon, output_buffer suppose qu'il est alloué sur le tas. L'allocation du tampon sur la pile est un bogue (du gnu doc): & "Si realloc n'est pas assez long, il est développé avec realloc(). &"; . Appeler HAVE_CXA_DEMANGLE un pointeur sur la pile ... ouch! (Voir aussi Le gentil commentaire de Igor Skochinsky .

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 sept. 2013) de 1024 à une taille inférieure, par exemple 16, et attribuez-lui un nom non plus long que 15 (donc __GNUG__ n'est pas appelé ). Néanmoins, en fonction de votre système et des optimisations du compilateur, le résultat sera le suivant: ordures / rien / blocage du programme.
Pour vérifier le deuxième bogue: définissez la taille du tampon sur 1 et appelez-le avec un nom dont le nom est supérieur à 1 caractère. Lorsque vous l'exécutez, le programme se bloque presque certainement lorsqu'il tente d'appeler <=> avec un pointeur sur la pile.

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

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

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

Je n'ai pas trouvé <=>. Je vérifie <=> 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;
}

Autres conseils

Le noyau de boost 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>
}

Il s’agit essentiellement d’un wrapper pour abi::__cxa_demangle, comme cela a été suggéré précédemment.

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  

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

Si vous recherchez simplement un outil de démêlage, que vous utilisez par exemple. Vous pouvez utiliser pour modifier les éléments affichés dans un fichier journal. Consultez c++filt, qui vient avec binutils. Il peut démêler les noms de symboles C ++ et Java.

Ce n'est pas une solution complète, mais vous voudrez peut-être regarder ce que certaines des macros standard (ou largement supportées) 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);

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

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

I.e. cela me donne ce qui suit:

virtual bool mutex::do_unlock()

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

Une légère variation de la solution d'Ali. Si vous voulez que le code soit toujours très similaire à

typeid(bla).name(),

écrit ceci à la place

Typeid(bla).name() (ne diffère que par la première lettre majuscule)

alors vous pouvez être intéressé par ceci:

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 identique à celui de la solution d'Ali

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

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

La solution acceptée fonctionne mieux [1]. J'ai trouvé au moins un cas (et je n'appellerais pas cela un 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 (avec type_name<decltype(obj)>(), voir le 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 vous le souhaitez (au moins par moi)

Code . Il doit figurer 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;
}

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top