Question

Je veux obtenir le nom de la chaîne (const char *) d'un type de modèle. Malheureusement, je n'ai pas accès à RTTI.

template< typename T >
struct SomeClass
{
    const char* GetClassName() const { return /* magic goes here */; }
}

SomeClass<int> sc;
sc.GetClassName();   // returns "int"

Est-ce possible? Je ne peux pas trouver un moyen et je suis sur le point de renoncer. Merci pour l'aide.

Était-ce utile?

La solution

Non et cela ne fonctionnera pas fiable avec typeid non plus. Il vous donnera une chaîne interne qui dépend de la mise en œuvre du compilateur. Quelque chose comme "int", mais aussi "i" est commun pour int.

Par ailleurs, si ce que vous voulez est de comparer uniquement si deux types sont les mêmes, vous n'avez pas besoin de les convertir en une chaîne en premier. Vous pouvez juste faire

template<typename A, typename B>
struct is_same { enum { value = false }; };

template<typename A>
struct is_same<A, A> { enum { value = true }; };

Et puis faire

if(is_same<T, U>::value) { ... }

Boost a déjà un tel modèle, et la prochaine norme C ++ aura std::is_same aussi.

Inscription manuelle des types

Vous pouvez vous spécialiser sur les types comme ceci:

template<typename> 
struct to_string {
    // optionally, add other information, like the size
    // of the string.
    static char const* value() { return "unknown"; }
};

#define DEF_TYPE(X) \
    template<> struct to_string<X> { \
        static char const* value() { return #X; } \
    }

DEF_TYPE(int); DEF_TYPE(bool); DEF_TYPE(char); ...

Ainsi, vous pouvez l'utiliser comme

char const *s = to_string<T>::value();

Bien sûr, vous pouvez également vous débarrasser de la définition principale du modèle (et ne conserver que la déclaration avant) si vous souhaitez obtenir une erreur de compilation si le type ne sait pas. Je viens ici pour inclus l'achèvement.

J'utilisé les données membres statiques de charbon const * précédemment, mais ils provoquent des problèmes complexes, comme les questions où mettre les déclarations d'entre eux, et ainsi de suite. spécialisations de classe comme résoudre la question ci-dessus facilement.

automatique, selon le GCC

Une autre approche consiste à compter sur internes du compilateur. Dans GCC, ce qui suit me donne des résultats raisonnables:

template<typename T>
std::string print_T() {
    return __PRETTY_FUNCTION__;
}

De retour pour std::string.

  

std::string print_T() [with T = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]

magie de substr entremêlées avec find vous donnera la représentation de chaîne que vous recherchez.

Autres conseils

La solution très simple: Il suffit de passer un objet de chaîne au constructeur de SomeClass qui dit ce que le type est.

Exemple:

#define TO_STRING(type) #type
SomeClass<int> s(TO_STRING(int));

Stockez et l'afficher dans la mise en œuvre de GetClassName.

solution Un peu plus compliqué, mais encore assez facile:

#define DEC_SOMECLASS(T, name) SomeClass<T> name;  name.sType = #T; 

template< typename T >
struct SomeClass
{
    const char* GetClassName() const { return sType.c_str(); }
    std::string sType;
};


int main(int argc, char **argv)
{
    DEC_SOMECLASS(int, s);
    const char *p = s.GetClassName();

    return 0;
}

Modèle solution non de type:

Vous pouvez également faire vos propres ids de type et ont une fonction pour convertir et de l'ID et la représentation de chaîne.

Ensuite, vous pouvez passer l'ID lorsque vous déclarez le type comme paramètre non type de modèle:

template< typename T, int TYPEID>
struct SomeClass
{
    const char* GetClassName() const { return GetTypeIDString(TYPEID); }
};


...

SomeClass<std::string, STRING_ID> s1;
SomeClass<int, INT_ID> s2;

Vous pouvez essayer quelque chose comme ça (c'est juste mise en garde du haut de ma tête, donc il peut y avoir des erreurs de compilation, etc ..)

template <typename T>
const char* GetTypeName()
{
    STATIC_ASSERT(0); // Not implemented for this type
}

#define STR(x) #x
#define GETTYPENAME(x) str(x) template <> const char* GetTypeName<x>() { return STR(x); }

// Add more as needed
GETTYPENAME(int)
GETTYPENAME(char)
GETTYPENAME(someclass)

template< typename T >
struct SomeClass
{
    const char* GetClassName() const { return GetTypeName<T>; }
}

Cela fonctionne pour tout type que vous ajoutez une ligne de GETTYPENAME(type) pour. Il a l'avantage que cela fonctionne sans modifier les types qui vous intéressent, et travaillera de concert avec intégré et les types de pointeur. Il présente l'inconvénient distinct que vous devez une ligne pour chaque type que vous souhaitez utiliser.

Sans utiliser le vous RTTI, intégré à devoir ajouter les informations vous quelque part, répondre ou dirkgently volonté de travailler de Brian R. Bondy. Avec ma réponse, vous avez trois endroits différents pour ajouter cette information:

  1. Au moment de la création d'un objet à l'aide SomeClass<int>("int")
  2. Dans la classe à l'aide dirkgently de compilation RTTI ou fonctions virtuelles
  3. Avec le modèle en utilisant ma solution.

Les trois fonctionnera, il est juste une question de savoir où vous allez finir avec les moins de maux de tête d'entretien de votre situation.

En vous n'avez pas accès à RTTI, ce que cela signifie que vous ne pouvez pas utiliser typeid (T) .name ()? Parce que c'est à peu près la seule façon de le faire avec l'aide du compilateur.

est-il très important pour les types d'avoir des noms uniques, ou sont les noms vont être persisté en quelque sorte? Si oui, vous devriez envisager de leur donner quelque chose de plus robuste que juste le nom de la classe comme déclarée dans le code. Vous pouvez donner deux classes le même nom non qualifié en les mettant dans différents espaces de noms. Vous pouvez aussi mettre deux classes avec le même nom (y compris la qualification d'espace de noms) dans deux DLL différentes sous Windows, vous devez donc l'identité de la DLL à inclure dans le nom ainsi.

Tout dépend de ce que vous allez faire avec les cordes, bien sûr.

Vous pouvez ajouter un peu de magie vous-même. Quelque chose comme:

#include <iostream>

#define str(x) #x
#define xstr(x) str(x)
#define make_pre(C) concat(C, <)
#define make_post(t) concat(t, >)

#define make_type(C, T) make_pre(C) ## make_post(T)
#define CTTI_REFLECTION(T, x)  static std::string my_typeid() \
                               { return xstr(make_type(T, x)); }


// the dark magic of Compile Time Type Information (TM)
#define CTTI_REFLECTION(x)  static const char * my_typeid() \
                                  { return xstr(make_type(T, x)); }

#define CREATE_TEMPLATE(class_name, type) template<> \
                                    struct class_name <type>{ \
                                        CTTI_REFLECTION(class_name, type) \
                                    }; 

// dummy, we'll specialize from this later
template<typename T> struct test_reflection;

// create an actual class
CREATE_TEMPLATE(test_reflection, int)

struct test_reflection {
  CTTI_REFLECTION(test_reflection)
};

int main(int argc, char* argv[])
{
    std::cout << test_reflection<int>::my_typeid();
}

Je vais faire l'inspecteur une fonction static (et donc non const).

Non, désolé.

Et RTTI ne sera même pas compiler si vous essayez de l'utiliser sur int.

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