Как реализовать BOOST_TYPEOF?

Здесь я хотел бы понять общую идею внедрения BOOST_TYPEOF. Я имею в виду, что код может быть в порядке, но я думаю, что код будет не простым, как в реальной реализации Boost. Поэтому я хотел бы понять идею реализации BOOST_TYPEOF. Использует ли он функции компилятора (некоторые API) для понимания типа выражения во время компиляции?

Ответ 1

В ядре Boost:: Typeof использует sizeof не оцениваемый контекст для преобразования типа выражения в целое число и затем преобразует его обратно в тип.

Рассмотрим:

template<int N> struct sizer { char value[N]; };

sizer<1> encode(char);
sizer<2> encode(unsigned char);
sizer<3> encode(signed char);
sizer<4> encode(bool);
...

template<int N> struct decode {};
template<> struct decode<1> { typedef char type; };
template<> struct decode<2> { typedef unsigned char type; };
template<> struct decode<3> { typedef signed char type; };
template<> struct decode<4> { typedef bool type; };

#define TYPEOF(expr) decode<sizeof(encode(expr))>::type

Теперь, учитывая выражение, которое оценивает любой тип char или bool, мы можем написать:

TYPEOF(expr) var = expr;

Boost:: Typeof по существу является расширением этой идеи, которая была первоначально изобретена Брайаном Паркером в 1997 году; см. Портативный оператор "typeof" для обсуждения и истории идеи.


Шаблоны - небольшая проблема для этой схемы; что-то простое, как std::pair дает квадрат пространства типов, даже до рекурсии. Boost:: Typeof решает это, кодируя тип шаблона и его типы параметров в последовательные слоты списка времени, связанного с компиляцией:

template<typename List> struct sizer {
    char item0[List::at<0>];
    char item1[List::at<1>];
    char item2[List::at<2>];
    ...
};

template<typename List> struct encode_type<List, char>: append<List, 1> {};
template<typename List> struct encode_type<List, unsigned char>: append<List, 2> {};
template<typename List, typename S, typename T>
struct encode_type<List, std::pair<S, T> >:
    encode_type<encode_type<append<List, 99>, S>, T> {};

template<typename Iter> struct decode_type<1, Iter> {
    typedef char type;
    typedef Iter iter;
};
template<typename Iter> struct decode_type<2, Iter> {
    typedef unsigned char type;
    typedef Iter iter;
};
template<typename Iter> struct decode_type<99, Iter> {
    typedef typename decode_type<Iter::next::value, Iter::next>::type S;
    typedef typename decode_type<Iter::next::value, Iter::next>::iter S_iter;
    typedef typename decode_type<S_Iter::next::value, S_Iter::next>::type T;
    typedef typename decode_type<S_Iter::next::value, S_Iter::next>::iter T_iter;
    typedef std::pair<S, T> type;
    typedef T_iter iter;
};

template<typename List, typename T>
sizer<typename encode_type<List, T>::type> encode(const T&);

template<typename List> struct decode {
    typedef typename decode_type<List::begin::value, List::begin>::type type; };

#define TYPEOF(expr) decode<list<
    sizeof(encode(expr).item0),
    sizeof(encode(expr).item1),
    sizeof(encode(expr).item2),
    ...
    > >::type

Это предполагает существующую реализацию связанного списка времени компиляции. Вы заметите, что декодер для std::pair потребляет столько элементов из итератора списка, сколько требуется для его типов параметров; это по существу прямой перевод эквивалентного функционального кода на языке с неперемещаемыми типами.

В двух строках, отмеченных как многоточие ..., мы ограничены фиксированным уровнем сложности в типах (т.е. количеством шаблонов и типов, составляющих тип, который мы хотим вывести). Boost:: Typeof имеет значение по умолчанию 50 для этого предела, но позволит вам уменьшить его для повышения эффективности или увеличить его для безумно сложных программ.

Ответ 2

Он основан на идее, что sizeof является оператором времени компиляции и может использоваться как аргумент шаблона. Таким образом мы можем назначить целое число для каждого типа и чтобы целое число можно было использовать для возврата к типу. Недостатком является то, что каждый тип должен регистрироваться вручную.

например:.

#include <boost/preprocessor/stringize.hpp>
#include <cstddef>
#include <iostream>

template<size_t> struct TypeId;

#define REGISTER_TYPE(T, id)                                    \
template<> struct TypeId<id> {                                  \
    char value[id];                                             \
    typedef T type;                                             \
    static char const* const name;                              \
};                                                              \
char const* const TypeId<id>::name = BOOST_PP_STRINGIZE(T);     \
TypeId<id> type_to_id(T);

#define TYPEID_(value) TypeId<sizeof(type_to_id(value))>
#define TYPEOF(value) typename TYPEID_(value)::type
#define TYPENAME(value) TYPEID_(value)::name

REGISTER_TYPE(int, 1)
REGISTER_TYPE(unsigned int, 2)
// and so on for all built-in types

int main() {
    int x;
    TYPEOF(x) y;
    std::cout << TYPENAME(y) << '\n';
}

Вывод:

./test
int