Возможно ли получить имя char * из типа шаблона в С++
Я хочу получить имя строки (const char *) типа шаблона. К сожалению, у меня нет доступа к RTTI.
template< typename T >
struct SomeClass
{
const char* GetClassName() const { return /* magic goes here */; }
}
Итак,
SomeClass<int> sc;
sc.GetClassName(); // returns "int"
Возможно ли это? Я не могу найти способ и собираюсь сдаться. Спасибо за помощь.
Ответы
Ответ 1
Нет, и он не будет работать с типом. Это даст вам некоторую внутреннюю строку, которая зависит от реализации компилятора. Что-то вроде "int", но также "i" является общим для int
.
Кстати, если вы хотите сравнить только то, являются ли два типа одинаковыми, вам не нужно сначала преобразовывать их в строку. Вы можете просто сделать
template<typename A, typename B>
struct is_same { enum { value = false }; };
template<typename A>
struct is_same<A, A> { enum { value = true }; };
И затем сделайте
if(is_same<T, U>::value) { ... }
У Boost уже есть такой шаблон, и следующий С++ Standard будет иметь std::is_same
тоже.
Ручная регистрация типов
Вы можете специализироваться на таких типах:
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); ...
Итак, вы можете использовать его как
char const *s = to_string<T>::value();
Конечно, вы также можете избавиться от определения основного шаблона (и сохранить только объявление вперед), если вы хотите получить ошибку времени компиляции, если тип неизвестен. Я просто включил его здесь для завершения.
Раньше я использовал статические данные-члены char const *, но они вызывают некоторые сложные проблемы, такие как вопросы, где размещать объявления из них, и так далее. Классовые специализации, подобные выше, легко решают проблему.
Автоматически, в зависимости от GCC
Другой подход - полагаться на внутренние компоненты компилятора. В GCC следующее дает мне разумные результаты:
template<typename T>
std::string print_T() {
return __PRETTY_FUNCTION__;
}
Возврат для std::string
.
std::string print_T() [with T = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]
Некоторая substr
магия, смешанная с find
, даст вам строковое представление, которое вы ищете.
Ответ 2
Очень простое решение: Просто передайте строковый объект конструктору SomeClass, который говорит, что это за тип.
Пример:
#define TO_STRING(type) #type
SomeClass<int> s(TO_STRING(int));
Просто сохраните его и покажите в реализации GetClassName.
Чуть более сложное решение, но все же довольно просто:
#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< typename T, int TYPEID>
struct SomeClass
{
const char* GetClassName() const { return GetTypeIDString(TYPEID); }
};
...
SomeClass<std::string, STRING_ID> s1;
SomeClass<int, INT_ID> s2;
Ответ 3
Вы можете попробовать что-то вроде этого (предупреждение, что это просто с головы, так что могут быть ошибки компиляции и т.д.)
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>; }
}
Это будет работать для любого типа, для которого вы добавляете строку GETTYPENAME(type)
. Преимущество состоит в том, что он работает без изменения типов, которые вас интересуют, и будет работать со встроенными и указательными типами. У этого есть отличительный недостаток, что у вас должна быть линия для каждого типа, который вы хотите использовать.
Без использования встроенного RTTI вам нужно будет где-то добавить информацию, либо ответ Брайана Р. Бонди, либо dirkgently будет работать. Наряду с моим ответом у вас есть три разных места, чтобы добавить эту информацию:
- При создании объекта
SomeClass<int>("int")
- В классе, использующем dirkgently compile-time RTTI или виртуальные функции
- С помощью шаблона, использующего мое решение.
Все три будут работать, это просто вопрос о том, где вы в конечном итоге получите наименьшее количество головных болей в вашей ситуации.
Ответ 4
У вас нет доступа к RTTI, означает ли это, что вы не можете использовать typeid (T).name()? Потому что это почти единственный способ сделать это с помощью компилятора.
Ответ 5
Очень важно, чтобы типы имели уникальные имена, или имена будут как-то сохранены? Если это так, вы должны подумать о том, чтобы дать им нечто более надежное, чем просто имя класса, как указано в коде. Вы можете дать двум классам одинаковое неквалифицированное имя, помещая их в разные пространства имен. Вы также можете поместить два класса с тем же именем (включая квалификацию пространства имен) в две разные библиотеки DLL в Windows, поэтому вам нужно также указать идентификатор DLL для имени.
Все зависит от того, что вы собираетесь делать со строками, конечно.
Ответ 6
Вы можете добавить немного волшебства. Что-то вроде:
#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();
}
Я сделаю инспектором функцию static
(и, следовательно, не const
).
Ответ 7
Нет, извините.
И RTTI даже не будет компилироваться, если вы попытаетесь использовать его в int.