Pergunta

Eu estou escrevendo algumas classes de modelo para parseing alguns arquivos de dados de texto, e como tal, é likly a grande maioria dos erros de análise será devido a erros no ficheiro de dados, que são em sua maior parte não escritos por programadores, e por isso precisa de uma mensagem agradável sobre por que o aplicativo falhou ao carregar por exemplo, algo como:

Erro ao analisar example.txt. Valor ( "notaninteger") de [MySectiom] Key não é um int válida

Eu posso trabalhar fora o arquivo, seção e nomes-chave dos argumentos passados ??para a função de modelo e membro vars na classe, no entanto eu não tenho certeza de como obter o nome do tipo a função de modelo está tentando converter a.

Os meus olhares de código atual gosta, com especializações para cordas apenas simples e tal:

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

Id preferia não ter que fazer sobrecargas específicos para cada tipo que os arquivos de dados pode usar, uma vez que existem cargas deles ...

Também eu preciso de uma solução que não incorre em qualquer sobrecarga de tempo de execução a menos que ocorra uma exceção, ou seja, uma solução de tempo completamente compilação é o que eu quero uma vez que este código é chamado de toneladas de tempos e tempos de carregamento já estão ficando um pouco longo.

EDIT: Ok esta é a solução que eu vim com:

Eu tenho um types.h containg o seguinte

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

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

Então eu posso usar a macro DEFINE_TYPE_NAME no cpp para cada tipo I precisa lidar com (por exemplo, no arquivo cpp que definiu o tipo para começar).

O ligador é, então, capaz de encontrar a especialização de modelo justificativo, desde que ele foi definido em algum lugar, ou lançar um erro vinculador de outra forma para que eu possa adicionar o tipo.

Foi útil?

Solução

A solução de Jesse Beder é provavelmente o melhor, mas se você não faça como os nomes typeid lhe dá (acho gcc dá-lhe mutilado nomes, por exemplo), você pode fazer algo como:

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, em seguida, usá-lo como

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

EDIT:

Você também pode combinar os dois, a mudança name ser uma função que, por padrão chama typeid(T).name() e depois só se especializam para os casos em que não é aceitável.

Outras dicas

A solução é

typeid(T).name()

que retorna std :: type_info .

typeid(T).name() é a implementação definida e não garante corda legível.

cppreference.com :

Retorna uma cadeia de caracteres terminada em nulo definido implementação contendo o nome do tipo. Não há garantias são dadas, em particular, a string retornada pode ser idêntico para vários tipos e mudança entre as chamadas do mesmo programa.

...

Com compiladores, como gcc e clang, a string retornada pode ser canalizada através de c ++ filt -t para ser convertido para um formato legível.

Mas em alguns casos gcc não retorna corda direita. Por exemplo na minha máquina eu tenho gcc whith -std=c++11 e dentro função de modelo typeid(T).name() retornos "j" para "unsigned int". É assim chamado nome desconfigurado. Para obter o nome do tipo real, o uso abi :: __ cxa_demangle () função (gcc apenas):

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

Como mencionado por Bunkar typeid (t) é definido pela implementação .nome.

Para evitar esse problema, você pode usar Boost.TypeIndex biblioteca .

Por exemplo:

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

A resposta de Logan Capaldo está correta, mas pode ser marginalmente simplificada porque não é necessário especializar a classe de cada vez. Pode-se escrever:

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

Isso também permite que você coloque as instruções REGISTER_PARSE_TYPE em um arquivo C ++ ...

Como uma reformulação da resposta de Andrey:

O impulsionar TypeIndex biblioteca pode ser usada para imprimir nomes de tipos.

como dentro de um modelo, isso pode ler 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;
}

Eu só deixá-lo lá. Se alguém ainda vai precisar dele, então você pode usar isto:

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

Isso só irá verificar tipo não obtê-lo e apenas 1 tipo ou 2.

Se você gostaria de um pretty_name, solução de Logan Capaldo não pode lidar com estrutura de dados complexa: REGISTER_PARSE_TYPE(map<int,int>) e typeid(map<int,int>).name() me dá um resultado de St3mapIiiSt4lessIiESaISt4pairIKiiEEE

Há uma outra resposta interessante usando unordered_map ou map vem 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;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top