Question

J'écris des classes de modèles pour analyser des fichiers de données texte. La grande majorité des erreurs d'analyse seront probablement dues à des erreurs dans le fichier de données, qui ne sont pour la plupart pas écrites par les programmeurs, et donc besoin d'un bon message sur la raison pour laquelle l'application n'a pas pu être chargée, par exemple quelque chose comme:

  

Erreur lors de l'analyse de example.txt. La valeur (& Quot; notaninteger & Quot;) de [MySectiom] Key n'est pas un int valide

Je peux définir les noms de fichier, de section et de clé à partir des arguments passés à la fonction de modèle et aux vars de membre de la classe. Toutefois, je ne sais pas comment obtenir le nom du type que la fonction de modèle tente de convertir. à.

Mon code actuel ressemble à ceci, avec des spécialisations pour les chaînes simples et autres:

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érerais ne pas avoir à créer de surcharges spécifiques pour chaque type d'utilisation des fichiers de données, car il en existe beaucoup ...

De plus, j'ai besoin d'une solution qui n'entraîne aucune surcharge d'exécution à moins qu'une exception ne se produise, c'est-à-dire qu'une solution de compilation complète est ce que je veux, car ce code s'appelle des tonnes de fois et les temps de chargement sont déjà un peu longs.

EDIT: Ok, voici la solution que j'ai proposée:

J'ai un types.h contenant 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;}

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 a défini le type avec lequel commencer).

L'éditeur de liens est alors capable de trouver la spécialisation de modèle appropriée tant qu'elle a été définie quelque part, ou de générer une erreur de l'éditeur de liens sinon afin que je puisse ajouter le type.

Était-ce utile?

La solution

La solution de Jesse Beder est probablement la meilleure, mais si vous n'aimez pas les noms que typeid vous donne (je pense que gcc vous donne des noms mutilé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 ensuite, utilisez-le comme

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

EDIT:

Vous pouvez également combiner les deux, changer name pour devenir une fonction qui appelle par défaut typeid(T).name(), puis se spécialiser uniquement dans les cas où cela n'est pas acceptable.

Autres conseils

La solution est

typeid(T).name()

qui retourne std :: type_info .

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

Lecture de cppreference.com :

  

Retourne une chaîne de caractères terminée par un caractère nul et définie par l'implémentation.   contenant le nom du type. Aucune garantie n'est donnée, dans   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 via c ++ filt -t pour être convertie en une forme lisible par l'homme.

Mais dans certains cas, gcc ne renvoie pas la chaîne correcte. Par exemple, sur ma machine, j’ai gcc avec -std=c++11 et dans la fonction de modèle "j" renvoie "unsigned int" pour <=>. C'est ce qu'on appelle un nom mutilé. Pour obtenir un nom de type réel, utilisez abi :: __ cxa_demangle () function (gcc seulement):

#include <string>
#include <cstdlib>
#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;
}

Comme mentionné par Bunkar, typeid (T) .nom est défini par l'implémentation.

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

Par exemple:

boost::typeindex::type_id<T>().pretty_name() // human readable

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

// in header
template<typename T>
struct TypeParseTraits
{ static const char* name; };

// in c-file
#define REGISTER_PARSE_TYPE(X) \
    template <> const char* TypeParseTraits<X>::name = #X

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

Ceci vous permet également de placer les instructions REGISTER_PARSE_TYPE dans un fichier C ++ ...

Pour reformuler la réponse d'Andrey:

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

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

#include <boost/type_index.hpp>
#include <iostream>

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

Je viens de le laisser là. Si quelqu'un en a toujours besoin, vous pouvez utiliser ceci:

template <class T>
bool isString(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 fera que CHECK type pas GET it et seulement pour 1 type ou 2.

Si vous voulez un nom joli, la solution de Logan Capaldo ne peut pas traiter une structure de données complexe: REGISTER_PARSE_TYPE(map<int,int>) et typeid(map<int,int>).name() me donne un résultat de St3mapIiiSt4lessIiESaISt4pairIKiiEEE

Une autre réponse intéressante utilisant unordered_map ou map provient de https: //fr.cppreference.com/w/cpp/types/type_index .

#include <iostream>
#include <unordered_map>
#include <map>
#include <typeindex>
using namespace std;
unordered_map<type_index,string> types_map_;

int main(){
    types_map_[typeid(int)]="int";
    types_map_[typeid(float)]="float";
    types_map_[typeid(map<int,int>)]="map<int,int>";

    map<int,int> mp;
    cout<<types_map_[typeid(map<int,int>)]<<endl;
    cout<<types_map_[typeid(mp)]<<endl;
    return 0;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top