Pregunta

Quiero obtener el nombre de la cadena (const char *) de un tipo de plantilla. Por desgracia no tengo acceso a RTTI.

template< typename T >
struct SomeClass
{
    const char* GetClassName() const { return /* magic goes here */; }
}

Entonces

SomeClass<int> sc;
sc.GetClassName();   // returns "int"

¿Es esto posible? No puedo encontrar una manera y estoy a punto de renunciar. Gracias por la ayuda.

¿Fue útil?

Solución

No y no va a funcionar fiable con typeid tampoco. Se le dará un poco de cuerda interna que depende de la implementación del compilador. Algo así como "int", sino también "i" es común para int.

Por cierto, si lo que quiere es sólo para comparar si dos tipos son los mismos, no es necesario convertirlos a una cadena en primer lugar. Usted sólo puede hacer

template<typename A, typename B>
struct is_same { enum { value = false }; };

template<typename A>
struct is_same<A, A> { enum { value = true }; };

Y a continuación, hacer

if(is_same<T, U>::value) { ... }

Boost ya cuenta con una plantilla de este tipo, y el siguiente C ++ estándar habrá std::is_same también.

Registro manual de tipos

Usted puede especializarse en los tipos como este:

template<typename> 
struct to_string {
    // optionally, add other information, like the size
    // of the string.
    static char const* value() { return "unknown"; }
};

#define DEF_TYPE(X) \
    template<> struct to_string<X> { \
        static char const* value() { return #X; } \
    }

DEF_TYPE(int); DEF_TYPE(bool); DEF_TYPE(char); ...

Por lo tanto, se puede usar como

char const *s = to_string<T>::value();

Por supuesto, también puede deshacerse de la definición de plantilla primaria (y mantener sólo la declaración hacia adelante) si desea obtener un error de tiempo de compilación si el tipo no se conoce. Yo sólo incluido en este documento para su conclusión.

Me emplearon datos a miembros estáticos de const char * anteriormente, pero pueden causar algunos problemas complejos, como las preguntas de dónde poner las declaraciones de ellos, y así sucesivamente. especializaciones de clase como por encima de resolver el problema fácilmente.

, dependiendo de GCC

automáticas

Otro enfoque es confiar en los elementos internos del compilador. En GCC, lo que sigue me da resultados razonables:

template<typename T>
std::string print_T() {
    return __PRETTY_FUNCTION__;
}

Volviendo por std::string.

  

std::string print_T() [with T = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]

Algunos magia substr entremezclado con find le dará la representación de cadena busca.

Otros consejos

La solución muy fácil: Sólo tiene que pasar un objeto de cadena al constructor de SomeClass que dice lo que el tipo es.

Ejemplo:

#define TO_STRING(type) #type
SomeClass<int> s(TO_STRING(int));

Simplemente almacenarlo y mostrarlo en la implementación de GetClassName.

solución ligeramente más complicado, pero sigue siendo bastante fácil:

#define DEC_SOMECLASS(T, name) SomeClass<T> name;  name.sType = #T; 

template< typename T >
struct SomeClass
{
    const char* GetClassName() const { return sType.c_str(); }
    std::string sType;
};


int main(int argc, char **argv)
{
    DEC_SOMECLASS(int, s);
    const char *p = s.GetClassName();

    return 0;
}

solución no tipo de plantilla:

También puede hacer sus propios identificadores de tipo y tienen una función para convertir desde y hacia el ID y la representación de cadena.

A continuación, puede pasar el ID cuando se declara el tipo como un parámetro de plantilla no tipo:

template< typename T, int TYPEID>
struct SomeClass
{
    const char* GetClassName() const { return GetTypeIDString(TYPEID); }
};


...

SomeClass<std::string, STRING_ID> s1;
SomeClass<int, INT_ID> s2;

Puede intentar algo como esto (advirtiendo que esto es sólo la parte superior de la cabeza, lo que puede haber errores de compilación, etc ..)

template <typename T>
const char* GetTypeName()
{
    STATIC_ASSERT(0); // Not implemented for this type
}

#define STR(x) #x
#define GETTYPENAME(x) str(x) template <> const char* GetTypeName<x>() { return STR(x); }

// Add more as needed
GETTYPENAME(int)
GETTYPENAME(char)
GETTYPENAME(someclass)

template< typename T >
struct SomeClass
{
    const char* GetClassName() const { return GetTypeName<T>; }
}

Esto funciona para cualquier tipo que se agrega una línea de GETTYPENAME(type). Tiene la ventaja de que funciona sin necesidad de modificar los tipos que le interesan, y trabajará con una función y tipos de puntero. Se tiene la desventaja de que es necesario una línea para cada tipo que desea utilizar.

Sin utilizar el built-in de RTTI, vas a tener que agregar la información usted mismo en alguna parte, ya sea la respuesta de Brian R. Bondy o dirkgently del funcionarán. Junto con mi respuesta, tiene tres lugares diferentes de agregar esa información:

  1. En el momento de la creación de objetos utilizando SomeClass<int>("int")
  2. En la clase utilizando dirkgently de tiempo de compilación RTTI o funciones virtuales
  3. Con la plantilla usando mi solución.

Los tres estarán trabajar, es sólo una cuestión de dónde se va a terminar con los dolores de cabeza menos mantenimiento en su situación.

Por usted no tiene acceso a RTTI, ¿significa que no se puede utilizar typeid .name (T) ()? Debido a que es más o menos la única manera de hacerlo con la ayuda del compilador.

¿Es muy importante para los tipos tengan nombres únicos, o van a ser persistido alguna manera los nombres? Si es así, usted debe considerar darles algo más robusto que el nombre de la clase que se declara en el código. Puede dar dos clases de la misma nombre no calificado poniéndolos en diferentes espacios de nombres. También se puede poner dos clases con el mismo nombre (incluyendo la calificación de espacio de nombres) en dos diferentes archivos DLL en Windows, por lo que necesita la identidad de la DLL para ser incluido en el nombre también.

Todo depende de lo que vas a hacer con las cuerdas, por supuesto.

Se puede añadir un poco de magia a sí mismo. Algo así como:

#include <iostream>

#define str(x) #x
#define xstr(x) str(x)
#define make_pre(C) concat(C, <)
#define make_post(t) concat(t, >)

#define make_type(C, T) make_pre(C) ## make_post(T)
#define CTTI_REFLECTION(T, x)  static std::string my_typeid() \
                               { return xstr(make_type(T, x)); }


// the dark magic of Compile Time Type Information (TM)
#define CTTI_REFLECTION(x)  static const char * my_typeid() \
                                  { return xstr(make_type(T, x)); }

#define CREATE_TEMPLATE(class_name, type) template<> \
                                    struct class_name <type>{ \
                                        CTTI_REFLECTION(class_name, type) \
                                    }; 

// dummy, we'll specialize from this later
template<typename T> struct test_reflection;

// create an actual class
CREATE_TEMPLATE(test_reflection, int)

struct test_reflection {
  CTTI_REFLECTION(test_reflection)
};

int main(int argc, char* argv[])
{
    std::cout << test_reflection<int>::my_typeid();
}

Voy a hacer el inspector una función static (y por lo tanto no const).

No, lo siento.

Y RTTI ni siquiera compilará si intenta usarlo en int.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top