C ++ Récupère le nom du type dans le modèle

J’écris des classes de modèles pour parsingr certains fichiers de données textuelles et, de ce fait, la grande majorité des erreurs d’parsing sont dues à des erreurs dans le fichier de données, qui pour la plupart ne sont pas écrites par les programmeurs. un bon message sur la raison pour laquelle l’application n’a pas pu être chargée, par exemple:

Erreur d’parsing de example.txt. La valeur (“notaninteger”) de la clé [MySectiom] n’est pas un int valide

Je peux trouver les noms de fichier, de section et de clé à partir des arguments transmis à la fonction de modèle et aux vars membres de la classe, mais je ne sais pas comment obtenir le nom du type que la fonction de modèle essaie de convertir.

Mon code actuel ressemble, avec des spécialisations pour des chaînes simples et telles:

template T GetValue(const std::wssortingng &section, const std::wssortingng &key) { std::map::iterator it = map[section].find(key); if(it == map[section].end()) throw ItemDoesNotExist(file, section, key) else { try{return boost::lexical_cast(it->second);} //needs to get the name from T somehow catch(...)throw ParseError(file, section, key, it->second, TypeName(T)); } } 

Je ne devrais pas avoir à faire des surcharges spécifiques pour chaque type que les fichiers de données pourraient utiliser, car il y en a beaucoup …

Aussi, j’ai besoin d’une solution qui n’entraîne pas de surcharge d’exécution à moins qu’une exception ne se produise, c’est-à-dire une solution complètement compilée puisque ce code s’appelle des tonnes de fois et que le temps de chargement est déjà long.

EDIT: Ok c’est la solution que j’ai trouvée:

J’ai un types.h contenant les éléments suivants

 #pragma once template const wchar_t *GetTypeName(); #define DEFINE_TYPE_NAME(type, name) \ templateconst wchar_t *GetTypeName(){return name;} 

Ensuite, je peux utiliser la macro DEFINE_TYPE_NAME dans les fichiers cpp pour chaque type que je dois traiter (par exemple, dans le fichier cpp qui définit le type avec lequel commencer).

L’éditeur de liens est alors capable de trouver la spécialisation de modèle appropriée tant qu’il a été défini quelque part, ou de lancer une erreur de l’éditeur de liens pour que je puisse append le type.

La solution de Jesse Beder est probablement la meilleure, mais si vous n’aimez pas les noms de typeid (je pense que gcc vous donne des noms déchiquetés par exemple), vous pouvez faire quelque chose comme:

 template struct TypeParseTraits; #define REGISTER_PARSE_TYPE(X) template <> struct TypeParseTraits \ { static const char* name; } ; const char* TypeParseTraits::name = #X REGISTER_PARSE_TYPE(int); REGISTER_PARSE_TYPE(double); REGISTER_PARSE_TYPE(FooClass); // etc... 

Et puis l’utiliser comme

 throw ParseError(TypeParseTraits::name); 

MODIFIER:

Vous pouvez également combiner les deux, changer le name pour qu’il soit une fonction qui, par défaut, appelle typeid(T).name() et ne se spécialiser que dans les cas où cela n’est pas acceptable.

Une solution d’exécution est

 typeid(T).name() 

qui renvoie un nom (dépendant du compilateur, je crois) du type en question. Il ne sera pas appelé à moins qu’une exception soit levée (dans votre code), de sorte qu’il puisse répondre à vos critères.

typeid(T).name() est défini par l’implémentation et ne garantit pas une chaîne lisible par l’homme.

Lire cppreference.com :

Renvoie une chaîne de caractères à terminaison null définie par l’implémentation contenant le nom du type. Aucune garantie n’est donnée, en particulier, la chaîne renvoyée peut être identique pour plusieurs types et changer entre les invocations du même programme.

Avec des compilateurs tels que gcc et clang, la chaîne renvoyée peut être transmise à c ++ filt -t pour être convertie en une forme lisible par l’homme.

Mais dans certains cas, gcc ne renvoie pas de chaîne droite. Par exemple, sur ma machine, j’ai gcc -std=c++11 et à l’intérieur de la fonction template typeid(T).name() retourne "j" pour "unsigned int" . C’est ce qu’on appelle le nom mutilé. Pour obtenir un vrai nom de type, utilisez la fonction abi :: __ cxa_demangle () (gcc uniquement):

 #include  #include  #include  template std::ssortingng type_name() { int status; std::ssortingng tname = typeid(T).name(); char *demangled_name = abi::__cxa_demangle(tname.c_str(), NULL, NULL, &status); if(status == 0) { tname = demangled_name; std::free(demangled_name); } return tname; } 

Comme mentionné par Bunkar, le typeid (T) .name est défini par l’implémentation.

Pour éviter ce problème, vous pouvez utiliser la bibliothèque Boost.TypeIndex .

Par exemple:

 boost::typeindex::type_id().pretty_name() // human readable 

Comme une reformulation de la réponse d’Andrey:

La bibliothèque Boost TypeIndex peut être utilisée pour imprimer des noms de types.

Dans un modèle, ceci pourrait se lire comme suit

 #include  #include  template void printNameOfType() { std::cout < < "Type of T: " << boost::typeindex::type_id().pretty_name() < < std::endl; } 

La réponse de Logan Capaldo est correcte mais peut être légèrement simplifiée car il est inutile de spécialiser la classe à chaque fois. On peut écrire:

 // in header template struct TypeParseTraits { static const char* name; }; // in c-file #define REGISTER_PARSE_TYPE(X) \ template <> const char* TypeParseTraits::name = #X REGISTER_PARSE_TYPE(int); REGISTER_PARSE_TYPE(double); REGISTER_PARSE_TYPE(FooClass); // etc... 

Cela vous permet également de mettre les instructions REGISTER_PARSE_TYPE dans un fichier C ++ …

Je viens de le laisser là. Si quelqu’un en a encore besoin, alors vous pouvez l’utiliser:

 template  bool isSsortingng(T* t) { return false; } // normal case returns false template <> bool isString(char* t) { return true; } // but for char* or String.c_str() returns true . . . 

Cela ne vérifiera que le type pas GET it et seulement pour 1 type ou 2.