89 votes

C++ Obtenir le nom du type, modèle

Je suis en train d'écrire certaines classes de modèle pour parseing certains fichiers de données texte, et en tant que tel, il est likly la grande majorité des erreurs d'analyse vont être due à des erreurs dans le fichier de données, qui sont pour la plupart écrits par des programmeurs, et donc besoin d'un gentil message sur les raisons de l'application a échoué à charge par exemple quelque chose comme:

Erreur lors de l'analyse example.txt. Valeur ("notaninteger")de [MySectiom]Clé n'est pas valide int

Je peux travailler sur le fichier, l'article et les noms de clés à partir d'arguments passés à la fonction de modèle et membre de vars dans la classe, mais je ne suis pas sûr de la façon d'obtenir le nom du type de la fonction de modèle est d'essayer de convertir de.

Mon code ressemble, avec des spécialités pour de simples chaînes de caractères et tel:

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

Je préfère ne pas avoir à prendre des surcharges pour chaque type que les fichiers de données peuvent utiliser, puisqu'il y a des tas d'entre eux...

J'ai aussi besoin d'une solution qui n'engage pas de gestion d'exécution, sauf si une exception se produit, c'est à dire une compilation complète d'une solution en temps est ce que je veux puisque ce code est appelé tonnes de fois et de temps de chargement sont déjà à faire un peu long.

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

J'ai un types.h contient les éléments suivants

#pragma once
template<typename T> const wchar_t *GetTypeName();

#define DEFINE_TYPE_NAME(type, name) \
    template<>const wchar_t *GetTypeName<type>(){return name;}

Alors je peux utiliser le DEFINE_TYPE_NAME macro au rpc fichiers pour chaque type j'ai besoin de traiter (par exemple, dans le fichier cpp qui ont défini le type de départ).

L'éditeur de liens est alors en mesure de trouver la appropirate modèle de spécialisation, tant qu'il a été défini quelque part, ou de jeter un linker error autrement de sorte que je peux ajouter le type.

87voto

Jesse Beder Points 14026

Une solution d'exécution est

typeid(T).name()

qui renvoie un (compilateur-dépendante, je crois) nom du type en question. Il ne sera pas appelé, sauf si une exception est levée (dans le code), de sorte qu'il peut satisfaire à vos critères.

53voto

Bunkar Points 141

typeid(T).name() est définie par l'implémentation et ne garantit pas lisible par les humains à la chaîne.

La lecture cppreference.com :

Retourne une mise en œuvre défini de caractères terminée par null string contenant le nom du type. Aucune garantie n'est donnée, dans en particulier, la chaîne retournée peuvent être identiques pour plusieurs types et le changement entre les appels du même programme.

...

Avec les compilateurs comme gcc et clang, la chaîne retournée peuvent être transmis par le biais de c++filt -t être converti en une forme lisible.

Mais dans certains cas, gcc n'a pas de droit de retour de la chaîne. Par exemple sur ma machine j'ai le gcc avec -std=c++11 et à l'intérieur du modèle de la fonction typeid(T).name() retours "j" pour "unsigned int". C'est donc appelé déformés nom. Pour prendre un vrai nom de type, l'utilisation abi::__cxa_demangle() fonction (gcc uniquement):

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

template<typename T>
std::string type_name()
{
    int status;
    std::string 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;
}

44voto

Logan Capaldo Points 22145

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

template<typename T>
struct TypeParseTraits;

#define REGISTER_PARSE_TYPE(X) template <> struct TypeParseTraits<X> \
    { static const char* name; } ; const char* TypeParseTraits<X>::name = #x


REGISTER_PARSE_TYPE(int);
REGISTER_PARSE_TYPE(double);
REGISTER_PARSE_TYPE(FooClass);
// etc...

Et puis l'utiliser comme

throw ParseError(TypeParseTraits<T>::name);

EDIT:

Vous pouvez également combiner les deux, changement name à une fonction qui, par défaut, les appels typeid(T).name() et ensuite seulement de se spécialiser dans les cas où ce n'est pas acceptable.

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