Su código no funciona porque el preprocesador, responsable de buscar y expandir las macros que utiliza en su código, no tiene conocimiento del idioma en sí. Es solo un analizador de texto. Encuentra STRINGIFY (T) en la misma plantilla de función y la expande, mucho antes de darle un tipo a esa plantilla. Como resultado, siempre obtendrás "T" en lugar del nombre tipo que esperabas, desafortunadamente.
Como litb sugirió, (mal) He aplicado esta plantilla función `getTypeName' que devuelve el nombre de tipo se le pasa:
#include <iostream>
template <typename _Get_TypeName>
const std::string &getTypeName()
{
static std::string name;
if (name.empty())
{
const char *beginStr = "_Get_TypeName =";
const size_t beginStrLen = 15; // Yes, I know...
// But isn't it better than strlen()?
size_t begin,length;
name = __PRETTY_FUNCTION__;
begin = name.find(beginStr) + beginStrLen + 1;
length = name.find("]",begin) - begin;
name = name.substr(begin,length);
}
return name;
}
int main()
{
typedef void (*T)(int,int);
// Using getTypeName()
std::cout << getTypeName<float>() << '\n';
std::cout << getTypeName<T>() << '\n'; // You don't actually need the
// typedef in this case, but
// for it to work with the
// typeid below, you'll need it
// Using typeid().name()
std::cout << typeid(float).name() << '\n';
std::cout << typeid(T).name() << '\n';
return 0;
}
El código anterior produce la siguiente salida con -s bandera GCC ("tira de todos los símbolos de binario") activado:
float
void (*)(int, int)
f
PFviiE
Así que, como ves, getTypename() hace bastante mejor trabajo, a costa de esa cadena analizar fugly truco (lo sé, es condenadamente feo).
Algunos puntos a tener en cuenta:
- El código es GCC solamente. No sé cómo portarlo a otro compilador. Probablemente solo unos pocos tengan la facilidad para producir nombres de funciones tan bonitos, y por lo que busqué, MSVC++ no tiene uno, si se lo está preguntando.
- Si, en una nueva versión, los formatos de GCC
__PRETTY_FUNCTION__
son diferentes, la coincidencia de cadenas se puede romper y tendrá que arreglarlo. Por esta misma razón, también advierto que getTypeName() podría ser bueno para la depuración (y, aún así, tal vez ni siquiera es bueno para eso), pero es seguramente malo, malo y malo para otros fines como comparar dos tipos en una plantilla o algo así (no sé, solo adivinando lo que alguien podría pensar ...). Úselo únicamente para la depuración, y preferentemente no lo llame en versiones de lanzamiento (use macros para deshabilitar), para que no use __PRETTY_FUNCTION__
y, por lo tanto, el compilador no produzca la cadena para él.
- Definitivamente no soy un experto, y no estoy seguro de si algún tipo impar podría causar que la coincidencia de cadena falle. Me gustaría pedirle a las personas que lean esta publicación que comenten si saben de un caso así.
- El código usa una std :: string estática. Significa que, si se lanza alguna excepción desde su constructor o destructor, no hay forma de que llegue a un bloque catch y obtendrá una excepción no controlada. No sé si std :: strings puede hacer eso, pero ten cuidado, si lo hacen, estás potencialmente en problemas. Lo usé porque necesita un destructor para liberar la memoria. Sin embargo, podría implementar su propia clase para eso, asegurándose de que no se produzca ninguna excepción además de la falla de asignación (eso es bastante fatal, ¿no? Entonces ...), y devuelva una cadena C simple.
- Con typedefs, usted puede obtener algunos resultados extraños, como éste (por alguna razón, el sitio rompe el formato de este fragmento, así que estoy usando este enlace pegar): http://pastebin.com/f51b888ad
A pesar de estas desventajas, yo Me gustaría decir que seguro es rápido. Por segunda vez que busque un mismo nombre de tipo, le costará elegir una referencia a una cadena std :: global que contenga el nombre. Y, comparativamente con los métodos de especialización de plantillas sugeridos anteriormente, no hay nada más que declarar además de la propia plantilla, por lo que es mucho más fácil de usar.
La resolución de la plantilla se activa * long * después de que el preprocesador hace su trabajo. De todos modos, las plantillas son mucho más que la sustitución de texto (bueno, ni siquiera es una sustitución de texto), por lo que cambiar el orden de las operaciones no resolvería su problema. –
El preprocesador se activa antes de casi * todo *. De ahí el nombre ** pre ** - procesador. –
He visto personas hacer 'template char const * get_type_name() {return __PRETTY_FUNCTION__; } 'y luego extrae' T = ... 'de la cadena. –