Pergunta

Eu quero obter o nome string (const char *) de um tipo de modelo. Infelizmente eu não tenho acesso a RTTI.

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

Assim

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

Isso é possível? Não consigo encontrar uma maneira e estou prestes a desistir. Obrigado pela ajuda.

Foi útil?

Solução

Não e não vai funcionar confiável, com typeid quer. Ele vai te dar alguma corda interna que depende da implementação do compilador. Algo como "int", mas também "i" é comum para int.

A propósito, se o que você quer é apenas para comparar se dois tipos são os mesmos, você não precisa convertê-los para uma cadeia em primeiro lugar. Você pode apenas fazer

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

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

E, em seguida, fazer

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

Impulso já tem um tal modelo, eo próximo padrão C ++ terá std::is_same também.

Registo manual de tipos

Você pode se especializar em 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); ...

Assim, você pode usá-lo como

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

É claro, você também pode se livrar da definição do modelo primário (e manter apenas a declaração para a frente), se você quiser obter um erro de tempo de compilação se o tipo não é conhecido. Eu só incluí aqui para a conclusão.

Eu costumava data-membros estático de const char * anteriormente, mas eles causam alguns problemas intrincados, como perguntas onde colocar declarações deles, e assim por diante. especializações classe como acima resolver o problema facilmente.

automática, dependendo GCC

Outra abordagem é contar com internos do compilador. No GCC, o seguinte me dá resultados razoáveis:

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

Voltando para std::string.

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

Alguns mágica substr misturados com find lhe dará a representação de string que você procurar.

Outras dicas

A solução muito fácil: Basta passar um objeto string para o construtor de SomeClass que diz que o tipo é.

Exemplo:

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

Simplesmente armazená-lo e exibi-lo na implementação de GetClassName.

solução um pouco mais complicado, mas ainda 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;
}

Template solução não digite:

Você também pode fazer seus próprios IDs do tipo e ter uma função para converter de e para o ID e a representação de cadeia.

Em seguida, você pode passar a ID quando você declarar o tipo como um parâmetro não-tipo de modelo:

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;

Você pode tentar algo como isto (aviso este é apenas fora do topo da minha cabeça, então pode haver erros de compilação, 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>; }
}

Isto irá funcionar para qualquer tipo que você adicione uma linha GETTYPENAME(type) para. Tem a vantagem que ele funciona sem modificar os tipos que você está interessado, e vai trabalhar com built-in e ponteiro tipos. Ele tem a desvantagem que você deve uma linha para cada tipo que você deseja usar.

Sem usar o built-in RTTI, você vai ter que adicionar as informações em algum lugar, quer a resposta de Brian R. Bondy ou trabalhar a vontade de dirkgently. Junto com a minha resposta, você tem três locais diferentes para adicionar essa informação:

  1. No momento da criação do objeto usando SomeClass<int>("int")
  2. Na classe usando de dirkgently em tempo de compilação RTTI ou funções virtuais
  3. Com o modelo usando a minha solução.

Todos os três vai funcionar, é apenas uma questão de onde você vai acabar com as dores de cabeça menos manutenção em sua situação.

Por que você não tem acesso a RTTI, isso significa que você não pode usar typeid (T) .name ()? Porque isso é praticamente a única maneira de fazer isso com a ajuda do compilador.

É muito importante para os tipos de ter nomes exclusivos, ou são os nomes vai ser persistentes de alguma forma? Se assim for, você deve considerar dar-lhes algo mais robusto do que apenas o nome da classe, conforme declarado no código. Você pode dar duas classes do mesmo nome não qualificado, colocando-os em diferentes namespaces. Você também pode colocar duas classes com o mesmo nome (incluindo qualificação namespace) em dois DLLs diferentes no Windows, então você precisa a identificação da DLL a ser incluído no nome também.

Tudo depende do que você vai fazer com as cordas, é claro.

Você pode adicionar um pouco de magia si mesmo. Algo 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();
}

Eu vou fazer o inspetor de uma função static (e, portanto, não const).

Não, desculpe.

E RTTI não vai mesmo compilar se você tentar usá-lo em int.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top