56 votes

Récupérer le nom d'une classe c++ par programme

Je me demandais s'il était possible en C++ de récupérer le nom d'une classe sous forme de chaîne sans avoir à le coder en dur dans une variable ou un getter. Je suis conscient qu'aucune de ces informations n'est utilisée au moment de l'exécution et qu'elles ne sont donc pas disponibles, mais existe-t-il des macros permettant de créer cette fonctionnalité ?

Edit : Il peut être utile de préciser que j'essaie en fait de récupérer le nom d'une classe dérivée, et que j'utilise Visual C++ 2008 Express Edition.

0 votes

Comme cela dépend du compilateur, avec quel compilateur travaillez-vous ?

0 votes

J'utilise visual c++ 2008 express, et je pense qu'il serait utile de préciser que j'essaie en fait de récupérer le nom d'une classe dérivée

0 votes

91voto

Konrad Rudolph Points 231505

Vous pouvez utiliser typeid :

#include <typeinfo>

std::cout << typeid(obj).name() << "\n";

Cependant, le nom du type n'est pas normalisé et peut différer d'un compilateur à l'autre (ou même d'une version à l'autre du même compilateur), et il n'est généralement pas lisible par l'homme parce qu'il est mutilé .

Sous GCC et clang (avec libstdc++ et libc++), vous pouvez démêler les noms à l'aide de la commande __cxa_demangle fonction (sur MSVC, le démêlage ne semble pas nécessaire) :

#include <cxxabi.h>
#include <cstdlib>
#include <memory>
#include <string>

std::string demangle(char const* mangled) {
    auto ptr = std::unique_ptr<char, decltype(& std::free)>{
        abi::__cxa_demangle(mangled, nullptr, nullptr, nullptr),
        std::free
    };
    return {ptr.get()};
}

Cela permettra toujours ne doit pas nécessairement être un nom lisible - par exemple, std::string est un nom de type pour le type réel, et son nom de type complet dans la libstdc++ actuelle est std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > Par contre, dans la libc++ actuelle, c'est std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > . "Prettifier" les alias de type n'est malheureusement pas trivial.

1 votes

Intéressant, je ne connaissais pas ça. Cela semble fonctionner assez bien. Cela me donne un peu plus de texte que ce que je voulais dans la réponse, mais cela semble fonctionner assez bien. Merci !

0 votes

Cela fonctionnera-t-il s'il n'y a pas de méthodes virtuelles dans la classe ? Je pensais que RTTI ne fonctionnait pas dans ce cas. Je suppose que tant que vous avez un destructeur virtuel, tout ira bien.

7 votes

@LeopardSkinPillBoxHat : Oui, cela fonctionnera (voir §5.2.8/3 et 4). Il s'agit d'une idée fausse courante selon laquelle typeid ne fonctionnera qu'avec des types polymorphes, probablement en raison de la similitude avec les fonctionnalités de RTTI. - En fait, l'utilisation de typeid sur les types statiques n'a pas besoin, et n'utilise pas, le RTTI. L'opérateur est évalué au moment de la compilation et le résultat est compilé (à proprement parler, il s'agit d'un détail d'implémentation, mais c'est la seule implémentation sensée).

35voto

fchen Points 381

Si vous voulez juste vérifier si c'est une certaine classe, alors

typeid(obj) == typeid(CSubClass)

fonctionnera toujours, quelles que soient les implémentations.

Sinon, un moyen pratique consiste à déclarer :

virtual const char* classname() { return "CMyClass";}

et mettre en œuvre par sous-classe.

8 votes

Facile, simple, compile. Faux, malheureusement. typeid() renvoie un typeinfo* un pointeur. Si deux typeinfo* Les pointeurs sont égaux, ils font référence au même type, mais s'ils sont inégaux, ils peuvent toujours faire référence au même type. C'est pourquoi il existe un std::type_index avec une sémantique appropriée. std::type_index(typeid(obj)) == std::type_index(typeid(CSubClass)) sera vrai si et seulement si les deux types sont égaux.

11voto

jbillfinger Points 114

El typeid(obj).name() thing donne toujours le type de la variable tel qu'il a été déclaré, et non le type réel (classe) de l'objet. Si la variable obj est assignée à une instance d'une sous-classe de la classe pour laquelle obj a été déclarée, typeid ne le révèle pas, malheureusement.

7 votes

Avec GCC 4.7.3, l'utilisation de typeid(*somePtr).name() me donne le nom des classes concrètes.

3 votes

J'ai rencontré le même problème, mais le commentaire de @stephelton m'a fait réaliser que je l'appelais sur un pointeur au lieu de l'objet ou de la référence réelle et qu'il retournait le type du pointeur ! Il suffit d'ajouter le * a tout arrangé.

0 votes

Je confirme les deux commentaires ci-dessus pour VS. Après BaseClass* ptr = new SubClass; Je trouve typeid(ptr).name() donne class BaseClass * y typeid(*ptr).name() donne class SubClass .

1voto

blongho Points 573

Qu'en est-il de ceci,

Testé sur Windows 10 avec Visual Studio 2019 (v142).

#include <iostream>
#include <typeinfo>
#include <string>

/**
 @author    blongho
 @fn        template<typename Object> std::string classNameOf()

 @brief     Determine the class name of an object

 @tparam    Object  Type of the object.

 @returns   A name of the class
 @date      2019-09-06
 */

template<typename Object>
std::string classNameOf() {
    std::string name = typeid(Object).name(); //* user defined types gives "class Type"*\ 
    size_t spacePosition = name.find_first_of(" ");
    if (spacePosition != std::string::npos) {
        return name.substr(spacePosition + 1, name.length());
    }
    return name; // mostly primitive types
}

class Person {
private:
    /* data */
public:
    Person() {};
    ~Person() {};

};

class Data
{
private:
    /* data */
public:
    Data() {};
    ~Data() {};

};

struct Type {};

int main() {
    std::cout << "Class name of Person() is \"" << classNameOf<Person>() << "\"\n";
    std::cout << "Class name of Data() is \"" << classNameOf<Data>() << "\"\n";
    std::cout << "Class name of Type() is \"" << classNameOf<Type>() << "\"\n";
    std::cout << "Class name of double is \"" << classNameOf<double>() << "\"\n";
    std::cout << "Class name of std::string is \"" << classNameOf<std::string>() << "\"\n";
    std::cout << "Class name of int is \"" << classNameOf<int>() << "\"\n";
    std::cout << "Class name of float is \"" << classNameOf<float>() << "\"\n";
    std::cout << "Class name of char is \"" << classNameOf<char>() << "\"\n";
    return 0;
}

Output

Class name of Person() is "Person"
Class name of Data() is "Data"
Class name of Type() is "Type"
Class name of double is "double"
Class name of std::string is "std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >"
Class name of int is "int"
Class name of float is "float"
Class name of char is "char"

Dans Ubuntu 18.04,

g++ -o test src/main.cpp
./test
Class name of Person() is "6Person"
Class name of Data() is "4Data"
Class name of Type() is "4Type"
Class name of double is "d"
Class name of std::string is "NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"
Class name of int is "i"
Class name of float is "f"
Class name of char is "c"

0voto

Franklin Yu Points 2567

Avec C++17, et une bibliothèque tierce vous pouvez maintenant obtenir le nom d'une classe comme suit

#include <iostream>
#include "nameof.hpp"

namespace test {
    class Object {};
}

int main() {
    constexpr auto obj_name = nameof::nameof_type<test::Object>();
    std::cout << obj_name << std::endl;
    // this prints "test::Object"
}

Cette méthode n'utilise que des informations de compilation, elle peut donc être constexpr . Notez qu'il n'est pas portable ; par exemple, le compilateur d'Intel n'est pas supporté.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X