Domanda

Sto scrivendo alcune classi di modelli per parseing alcuni file di testo, e come tale è likly la grande maggioranza di analizzare gli errori saranno causa di errori nel file di dati, che sono per la maggior parte non scritti dai programmatori, e quindi hanno bisogno di un bel messaggio sul perché l'app non è riuscito a caricare ad esempioqualcosa di simile a:

Errore durante l'analisi example.txt.Valore ("notaninteger")di [MySectiom]Chiave non è valida int

Posso lavorare il file, la sezione e la chiave di nomi argomenti passati alla funzione di modello e membro vars in classe, ma io non sono sicuro di come ottenere il nome del tipo la funzione di modello sta cercando di convertire.

Il mio attuale codice simile, con specializzazioni per semplicemente stringhe e cosi:

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));
    }
}

Preferirei non essere sovraccarichi per ogni tipo di file di dati potrebbe, dato che ci sono un sacco di loro...

Inoltre ho bisogno di una soluzione che non comporta alcun sovraccarico di runtime a meno che non si verifica un'eccezione, vale a direcompletamente compilazione soluzione è quello che voglio dal momento che questo codice è chiamato tonnellate di volte e i tempi di caricamento sono già un po ' lungo.

EDIT:Ok questa è la soluzione che mi è venuta:

Ho un tipi.h contenente le seguenti

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

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

Quindi posso usare il DEFINE_TYPE_NAME macro nel file cpp per ogni tipo di cui ho bisogno per affrontare (ad esempio nel cpp file che definiscono il tipo all'inizio).

Il linker è quindi in grado di trovare il appropirate modello di specializzazione, come lungo come è stato definito da qualche parte, o gettare un errore di linker, altrimenti in modo che io possa aggiungere il tipo.

È stato utile?

Soluzione

Jesse Beder la soluzione è probabilmente il migliore, ma se non ti piacciono i nomi typeid ti dà (penso gcc ti dà i nomi alterati per esempio), si può fare qualcosa di simile:

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...

E quindi utilizzarlo come

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

EDIT:

Si potrebbe anche combinare i due, cambiare name per essere una funzione che, per impostazione predefinita chiamate typeid(T).name() e poi specializzati solo per quei casi in cui non è accettabile.

Altri suggerimenti

La soluzione è

typeid(T).name()

che restituisce std::type_info.

typeid(T).name() è l'attuazione definiti e non garantisce leggibile stringa.

La lettura cppreference.com :

Restituisce un'implementazione definito stringa con terminazione null carattere contenente il nome del tipo.Non si presta alcuna garanzia, in particolare, viene restituita una stringa può essere identiche per le diverse tipologie e cambiare tra le chiamate dello stesso programma.

...

Con compilatori come gcc e clang, viene restituita una stringa può essere trasportato tramite c++filt -t per essere convertiti al formato leggibile.

Ma in alcuni casi gcc non restituisce stringa giusta.Per esempio sulla mia macchina ho il gcc con -std=c++11 e all'interno della funzione del modello typeid(T).name() restituisce "j" per "unsigned int".Si chiama così storpiato il nome.Per ottenere un reale nome del tipo di utilizzo abi::__cxa_decodifica() funzione (gcc solo):

#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;
}

Come detto da Bunkar typeid(T).il nome è l'attuazione definiti.

Per evitare questo problema è possibile utilizzare Boost.TypeIndex biblioteca.

Per esempio:

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

La risposta di Logan Capaldo è corretto, ma può risultare leggermente semplificata, perché non è necessario specializzare la classe di ogni tempo.Si può scrivere:

// 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...

Questo permette anche di mettere il REGISTER_PARSE_TYPE istruzioni in un file di C++...

Come riformulare Andrey risposta:

Il Boost TypeIndex la libreria può essere utilizzata per stampare i nomi di tipi.

All'interno di un modello, questo potrebbe essere come segue

#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;
}

Ho semplicemente lasciarlo lì.Se qualcuno ha ancora bisogno, allora è possibile utilizzare questo:

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
.
.
.

Questo sarà solo tipo di CONTROLLO non farlo e solo per il tipo 1 o 2.

Se vuoi un pretty_name, Logan Capaldo, la soluzione non può occuparsi della complessa struttura dati: REGISTER_PARSE_TYPE(map<int,int>) e typeid(map<int,int>).name() mi dà un risultato di St3mapIiiSt4lessIiESaISt4pairIKiiEEE

C'è un altro interessante risposta utilizzando unordered_map o map viene da https://en.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;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top