Он просто использует магию компилятора. Мол, GCC's __typeof__
. Для компиляторов, которые не предоставляют такой магии, он предоставляет эмуляцию, которая может определять тип некоторых выражений, но завершается неудачей с совершенно неизвестными типами.
Возможная реализация может состоять в том, чтобы иметь список функций, которые принимают выражение данного типа, а затем отправлять из этого типа в число, используя шаблон класса. Чтобы шаблон функции возвращал число как объект времени компиляции, мы помещаем его в измерение массива
template<typename> struct type2num;
template<int> struct num2type;
template<typename T> typename type2num<T>::dim &dispatch(T const&);
Затем он переходит от этого номера к типу, чтобы наш EMUL_TYPEOF
мог напрямую назвать тип. Таким образом, чтобы зарегистрировать тип, мы пишем
#define REGISTER_TYPE(T, N) \
template<> \
struct type2num<T> { \
static int const value = N; \
typedef char dim[N]; \
}; \
template<> \
struct num2type<N> { typedef T type; }
Имея это на месте, вы можете написать
#define EMUL_TYPEOF(E) \
num2type<sizeof dispatch(E)>::type
Всякий раз, когда вам нужно зарегистрировать тип, вы пишете
REGISTER_TYPE(int, 1);
REGISTER_TYPE(unsigned int, 2);
// ...
Конечно, теперь вы обнаружите, что вам нужен механизм для принятия vector<T>
, когда вы не знаете заранее T
, и тогда он становится произвольным комплексом. Вы можете создать систему, в которой числа означают больше, чем просто тип. Это может сработать:
#define EMUL_TYPEOF(E) \
build_type<sizeof dispatch_1(E), sizeof dispatch_2(E)>::type
Это может обнаружить типы, такие как int
, а также типы, такие как shared_ptr<int>
- другими словами, типы, которые не являются специализациями шаблонов классов, и специализации шаблонов классов с одним аргументом шаблона, выполняя какое-то систематическое отображение
- Если первое число дает 1, второе число указывает тип; в противном случае
- первое число указывает шаблон, а второе число - аргумент шаблона первого типа
Итак, это становится
template<int N, int M>
struct build_type {
typedef typename num2tmp<N>::template apply<
typename num2type<M>::type>::type type;
};
template<int N>
struct build_type<1, N> {
typedef num2type<N>::type type;
};
Нам также нужно изменить шаблон dispatch
и разделить его на две версии, показанные ниже, вместе с REGISTER_TEMP1
для регистрации шаблонов с одним аргументом
template<typename T> typename type2num<T>::dim1 &dispatch_1(T const&);
template<typename T> typename type2num<T>::dim2 &dispatch_2(T const&);
#define REGISTER_TYPE(T, N) \
template<> \
struct type2num<T> { \
static int const value_dim1 = 1; \
static int const value_dim2 = N; \
typedef char dim1[value_dim1]; \
typedef char dim2[value_dim2]; \
}; \
template<> \
struct num2type<N> { typedef T type; }
#define REGISTER_TMP1(Te, N) \
template<typename T1> \
struct type2num< Te<T1> > { \
static int const value_dim1 = N; \
static int const value_dim2 = type2num<T1>::value_dim2; \
typedef char dim1[value_dim1]; \
typedef char dim2[value_dim2]; \
}; \
template<> struct num2tmp<N> { \
template<typename T1> struct apply { \
typedef Te<T1> type; \
}; \
}
Регистрация шаблона std::vector
и обоих вариантов int
теперь выглядит как
REGISTER_TMP1(std::vector, 2);
// ... REGISTER_TMP1(std::list, 3);
REGISTER_TYPE(int, 1);
REGISTER_TYPE(unsigned int, 2);
// ... REGISTER_TYPE(char, 3);
Вы, вероятно, также хотите зарегистрировать несколько номеров для каждого типа, по одному номеру для каждой комбинации const / volatile или может потребоваться более одного номера для типа для записи *
, &
и тому подобное. Вы также хотите поддерживать vector< vector<int> >
, поэтому вам нужно более одного номера для аргумента шаблона, чтобы build_type
вызывал сам себя рекурсивно. Поскольку вы можете создать произвольный длинный список целых чисел, вы можете в любом случае закодировать что-либо в эту последовательность, так что это зависит только от вашего творчества в том, как представлять эти вещи.
В конце концов, вы, вероятно, реализуете BOOST_TYPEOF:)