Estoy escribiendo algunas clases de plantilla para parseing algunas de texto de los archivos de datos, y como tal es likly la gran mayoría de los errores de análisis será debido a los errores en el archivo de datos, que son en su mayor parte no está escrito por los programadores, y por lo que necesita un buen mensaje acerca de por qué la aplicación no se pudo cargar por ejemplo, algo como:

Error de análisis example.txt. Valor («notaninteger»)de [MySectiom]Clave no es válida int

Puedo trabajar el archivo, en la sección de claves y nombres de los argumentos pasados a la función de la plantilla y de los estados vars en la clase, sin embargo no estoy seguro de cómo obtener el nombre de la tipo de la función de la plantilla está tratando de convertir a.

Mi código actual se parece, con especialidades para simplemente cadenas y 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));
    }
}

Prefiero no tener a sobrecargas para cada tipo de que los archivos de datos de usar, ya que hay un montón de ellos…

También necesito una solución que no incurre en ninguna sobrecarga de tiempo de ejecución, a menos que una excepción se produce, es decir, un completo tiempo de compilación solución es lo que quiero ya que este código es llamado miles de veces y los tiempos de carga ya están consiguiendo algo largo.

EDIT: Ok esta es la solución que se me ocurrió:

Tengo un tipos.h contiene los siguientes

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

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

Entonces puedo usar el DEFINE_TYPE_NAME de macros en archivos cpp para cada tipo debe tratar con (por ejemplo en el cpp de archivo que define el tipo para empezar).

El enlazador es capaz de encontrar el ambiente de la plantilla de la especialización como de largo como fue definida en algún lugar, o tirar un error del vinculador de otra manera para que yo pueda agregar el tipo.

  • en realidad no es relevante a su pregunta, pero es posible que desee utilizar el mapa.encontrar la(sección) cuando acceder a la sección así, a menos que intencionalmente quieren crear una sección vacía.
InformationsquelleAutor Fire Lancer | 2009-06-28

8 Comentarios

  1. 35

    Jesse Beder la solución es probablemente el mejor, pero si no te gustan los nombres typeid le da (creo gcc le da los nombres alterados por ejemplo), puedes hacer 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...

    Y, a continuación, utilizarlo como

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

    EDICIÓN:

    También puede combinar las dos cosas, cambiar name a ser una función que, por defecto, las llamadas typeid(T).name() y, a continuación, sólo se especializan para aquellos casos en los que no es aceptable.

    • Nota: Este código no compila si usted se olvida de definir REGISTER_PARSE_TYPE para un tipo de uso. He utilizado una táctica similar antes (en el código sin RTTI) y ha funcionado muy bien.
    • Me tuve que cambiar de nombre por fuera de la estructura de g++ 4.3.0 debido a un «error: no válido en la clase de inicialización de datos estáticos miembro de la no-tipo integral ‘const char *'»; y, por supuesto, la palabra clave ‘struct’ es necesario entre <> y TypeParseTraits y la definición debe finalizar con un punto y coma.
    • Bueno, la de dejar el punto y coma a cabo fue intencional, a fuerza de uso al final de la macro de invocación, pero gracias por las correcciones.
    • Obtengo el siguiente error: error: '#' is not followed by a macro parameter
    • eso es porque al final ‘#x’ debe ser ‘#X’ (en mayúsculas para que coincida con parámetro de macro) – voy a arreglar la respuesta.
  2. 60

    La solución es

    typeid(T).name()

    que devuelve std::type_info.

    • Tenga en cuenta que es compatible con la devolución de la misma cadena para cada tipo (aunque no creo que cualquier compilador haría eso).
    • O la devolución de una cadena diferente para el mismo tipo en diferentes ejecuciones… (de nuevo, no que yo creo que cualquier cuerdo compilador haría eso).
    • Me gustaría señalar cómo feo el nombre puede ser: typeid(simd::double3x4).name() = "N4simd9double3x4E". typeid(simd::float4).name() = "Dv4_f" C++17, Xcode 10.1.
  3. 40

    typeid(T).name() es definido por la implementación y no garantiza legible de la cadena.

    Lectura cppreference.com :

    Devuelve un definido por la implementación de caracteres terminada en null cadena
    contiene el nombre del tipo. No hay garantías, en
    en particular, la cadena devuelta puede ser idénticos para varios tipos y
    cambiar entre las invocaciones del mismo programa.

    Con los compiladores como gcc y clang, la cadena devuelta puede ser canalizada a través de c++filt -t para ser convertidos a formato legible.

    Pero en algunos casos gcc no volver a la derecha de la cadena. Por ejemplo en mi máquina tengo gcc con -std=c++11 y dentro de la plantilla de función typeid(T).name() devuelve "j" para "unsigned int". Él es llamado trastoca nombre. Para conseguir el verdadero nombre del tipo, uso
    abi::__cxa_demangle() función (gcc): en

    #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;
    }
    • Esto realmente funciona para clang así como gcc.
    • No es pérdida de memoria para tener free en if?
    • No, porque el puntero apunta a nullptr si el estado no es 0.
    • Me gustaría añadir que es probablemente la mejor manera de verificación para el gcc o el sonido metálico de la existencia y si no predeterminada a no hacer el demangling como se muestra aquí.
  4. 18

    Como se ha mencionado por Bunkar typeid(T).el nombre está definido por la implementación.

    Para evitar este problema, puede utilizar Boost.TypeIndex biblioteca.

    Por ejemplo:

    boost::typeindex::type_id<T>().pretty_name() //human readable
    • Esto es muy útil para encontrar plantillas de nombres de tipo cuando se llama a funciones. Funcionó bastante bien para mí.
    • Tenga en cuenta que pretty_name() o raw_name() todavía está definido por la implementación. En MSVC de una estructura; se obtendría : «estructura», mientras que en gcc/clang : «Un».
    • wow. boost de nuevo por la victoria. increíble lo que el impulso no compilador de apoyo (auto, regex, foreach, threads, static_assert, etc, etc… apoyo antes de que los compiladores de C++estándar de apoyo).
  5. 9

    La respuesta de Logan Capaldo es correcto, pero puede ser un poco más sencilla, ya que no es necesario especializar la clase en todo momento. Uno puede escribir:

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

    Esto también permite que usted para poner la REGISTER_PARSE_TYPE instrucciones en un archivo de C++…

  6. 8

    Como una reformulación de Andrey de respuesta:

    La Impulsar TypeIndex de la biblioteca se puede utilizar para imprimir los nombres de tipos.

    Dentro de una plantilla, esta se puede leer como sigue

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

    Me acaba de salir de allí.
    Si alguien todavía se necesita, entonces usted puede utilizar este:

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

    Esto sólo tipo de VERIFICACIÓN no conseguirlo, y sólo 1 o tipo 2.

  8. 1

    Si quieres un pretty_name, Logan Capaldo la solución no puede lidiar con la compleja estructura de datos: REGISTER_PARSE_TYPE(map<int,int>)
    y typeid(map<int,int>).name() me da un resultado de St3mapIiiSt4lessIiESaISt4pairIKiiEEE

    Hay otro interesante respuesta usando unordered_map o map viene de 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;
    }

Dejar respuesta

Please enter your comment!
Please enter your name here