É possível obter um nome char * a partir de um tipo de modelo em C ++
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.
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:
- No momento da criação do objeto usando
SomeClass<int>("int")
- Na classe usando de dirkgently em tempo de compilação RTTI ou funções virtuais
- 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.