Можно ли получить имя char * из типа шаблона в C ++ - PullRequest
10 голосов
/ 27 февраля 2009

Я хочу получить имя строки (const char *) типа шаблона. К сожалению, у меня нет доступа к RTTI.

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

So

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

Возможно ли это? Я не могу найти способ и собираюсь сдаться. Спасибо за помощь.

Ответы [ 7 ]

13 голосов
/ 27 февраля 2009

Нет, и он не будет работать надежно с typeid. Это даст вам некоторую внутреннюю строку, которая зависит от реализации компилятора. Что-то вроде «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 уже имеет такой шаблон, и в следующем стандарте C ++ также будет 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, даст вам строковое представление, которое вы ищете.

3 голосов
/ 27 февраля 2009

Вы можете попробовать что-то вроде этого (предупреждение, что это только у меня в голове, поэтому могут быть ошибки компиляции и т. Д ..)

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 вам придется где-то добавлять информацию самостоятельно, либо ответ Брайана Р. Бонди, либо ответ Диргентли сработает. Наряду с моим ответом у вас есть три разных места для добавления этой информации:

  1. Во время создания объекта, используя SomeClass<int>("int")
  2. В классе с использованием RTTI времени компиляции dirkgently или виртуальных функций
  3. С шаблоном, использующим мое решение.

Все три сработают, это просто вопрос того, где вы получите меньше всего проблем с обслуживанием в вашей ситуации.

3 голосов
/ 27 февраля 2009

Действительно простое решение: Просто передайте строковый объект конструктору 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;
1 голос
/ 28 февраля 2009

Очень важно, чтобы типы имели уникальные имена, или имена будут как-то сохраняться? Если это так, вам следует подумать о том, чтобы дать им что-то более надежное, чем просто имя класса, как объявлено в коде. Вы можете дать двум классам одно и то же неквалифицированное имя, поместив их в разные пространства имен. Вы также можете поместить два класса с одинаковыми именами (включая квалификацию пространства имен) в две разные библиотеки DLL в Windows, поэтому вам необходимо указать идентификатор библиотеки DLL, который будет включен в имя.

Конечно, все зависит от того, что вы собираетесь делать со строками.

1 голос
/ 27 февраля 2009

Если у вас нет доступа к RTTI, значит ли это, что вы не можете использовать typeid (T) .name ()? Потому что это практически единственный способ сделать это с помощью компилятора.

0 голосов
/ 27 февраля 2009

Вы можете добавить немного магии самостоятельно. Что-то вроде:

#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).

0 голосов
/ 27 февраля 2009

Нет, извините.

И RTTI даже не скомпилируется, если вы попытаетесь использовать его в int.

...