c++ - para - manual de programacion android pdf
C++ Obtener el nombre del tipo en la plantilla (7)
Estoy escribiendo algunas clases de plantilla para analizar algunos archivos de datos de texto, y como tal, es probable que la gran mayoría de los errores de análisis se deban a errores en el archivo de datos, que en su mayoría no están escritos por programadores, por lo que necesitan un bonito mensaje sobre por qué la aplicación no se pudo cargar, por ejemplo, algo así como:
Error al analizar example.txt. El valor ("notaninteger") de la clave [MySectiom] no es una int válida
Puedo descifrar el archivo, la sección y los nombres clave de los argumentos pasados a la función de plantilla y miembros vars en la clase, sin embargo, no estoy seguro de cómo obtener el nombre del tipo al que intenta convertir la función de la plantilla.
Mi código actual se ve así, con especializaciones para cadenas sencillas y cosas por el estilo:
template<typename T> T GetValue(const std::wstring §ion, 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));
}
}
Preferiría no tener que realizar sobrecargas específicas para cada tipo que los archivos de datos podrían usar, ya que hay muchos de ellos ...
También necesito una solución que no incurra en sobrecarga de tiempo de ejecución a menos que ocurra una excepción, es decir, una solución de tiempo completamente compilada es lo que quiero ya que este código se llama toneladas de veces y los tiempos de carga ya son algo largos.
EDITAR: Ok, esta es la solución que se me ocurrió:
Tengo un types.h que contiene lo siguiente
#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 la macro DEFINE_TYPE_NAME en archivos cpp para cada tipo que necesito tratar (por ejemplo, en el archivo cpp que definió el tipo para comenzar).
El vinculador puede entonces encontrar la especialización de plantilla adecuada siempre que se haya definido en alguna parte, o bien arrojar un error de enlazador para que yo pueda agregar el tipo.
Como se menciona en Bunkar, typeid (T) .name es la implementación definida.
Para evitar este problema, puede usar la biblioteca Boost.TypeIndex .
Por ejemplo:
boost::typeindex::type_id<T>().pretty_name() // human readable
Como una reformulación de la respuesta de Andrey:
La biblioteca Boost TypeIndex se puede usar para imprimir nombres de tipos.
Dentro de una plantilla, esto podría leerse de la siguiente manera
#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;
}
La respuesta de Logan Capaldo es correcta, pero puede simplificarse marginalmente porque no es necesario especializar la clase cada vez. 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 le permite colocar las instrucciones REGISTER_PARSE_TYPE en un archivo C ++ ...
La solución de Jesse Beder es probablemente la mejor, pero si no te gustan los nombres que te da typeid (creo que gcc te da nombres destrozados, 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 luego usarlo como
throw ParseError(TypeParseTraits<T>::name);
EDITAR:
También podría combinar los dos, cambiar el name
para que sea una función que de forma predeterminada llama a typeid(T).name()
y luego solo se especializa para aquellos casos en que eso no sea aceptable.
Solo lo dejo ahí. Si alguien aún lo necesita, puede usar esto:
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 solo revisará el tipo no GET y solo para 1 tipo o 2.
Una solución de tiempo de ejecución es
typeid(T).name()
que devuelve un nombre (dependiente del compilador, creo) del tipo en cuestión. No se invocará a menos que se genere una excepción (en su código), por lo que puede satisfacer sus criterios.
typeid(T).name()
es la implementación definida y no garantiza una cadena legible por humanos.
Leyendo cppreference.com :
Devuelve una cadena de caracteres definida por la implementación que contiene el nombre del tipo. No se dan garantías, en particular, la cadena devuelta puede ser idéntica para varios tipos y cambiar entre invocaciones del mismo programa.
...
Con compiladores como gcc y clang, la cadena devuelta se puede canalizar a través de c ++ filt -t para convertirla a una forma legible por humanos.
Pero en algunos casos, gcc no devuelve la cadena derecha. Por ejemplo, en mi máquina tengo gcc whith -std=c++11
y la función de plantilla interna typeid(T).name()
devuelve "j"
para "unsigned int"
. Es llamado nombre destrozado. Para obtener el nombre de tipo real, use la función abi::__cxa_demangle() (solo gcc):
#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;
}