Отсутствие оператора typeof в C ++ 03? - PullRequest
7 голосов
/ 26 декабря 2010

Мне просто интересно, как в boost реализованы BOOST_TYPEOF (на C ++ 03), который представляется очень полезным инструментом.У кого-нибудь есть идеи?

Кроме того, я думаю, что сам C ++ 03 мог бы обеспечить оператор typeof, особенно если он уже имеет sizeof(expr), который должен знать type из expr также, иначе как он мог бы сказать нам size , не зная type ? Действительно ли возможно узнать размер , не зная тип выражения?

Если он не знает тип , то компилятор сообщает нам размер чего (если не тип )?Я имею в виду, что sizeof(unknowntype) не имеет смысла для компиляторов (и людей тоже)!

Ответы [ 5 ]

11 голосов
/ 26 декабря 2010

Он просто использует магию компилятора. Мол, 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:)

6 голосов
/ 26 декабря 2010

Из памяти, boost :: typeof реализован с помощью некоторых хаков ?:. Сначала вы начинаете с класса, который можно преобразовать в любой другой класс, например

class something {
public:
    template<typename T> operator const T&() {
        return *(T*)0;
    }
};

Правила?: Утверждают, что если обе стороны имеют одинаковый тип, то результатом будет этот тип. Иначе, если один тип может быть преобразован в другой тип, это тип результата. Таким образом, делая

true ? something() : expr;

тип результата (константная ссылка) тип expr, но expr фактически никогда не вычисляется, потому что он находится на ветви false. Затем вы передаете его в какое-то место, в котором уже есть аргумент, например аргументы функции.

template<typename T> void x(const T& t) {
    // T is the type of expr.
}

Это несколько сложнее, потому что из памяти C ++ 03 не имеет свертывания ссылок, поэтому он, вероятно, несколько более запутан, чем этот пример - скорее всего, с использованием SFINAE и признаков типа.

Как это преобразовать в фактический тип времени компиляции, который вы можете передать в шаблон, я понятия не имею. Что касается C ++ 03, предоставляющего decltype (), то с языком C ++ 03 гораздо больше проблем, таких как ODR и порядки объявления / определения, отсутствие семантики перемещения и т. Д., Которые намного хуже, чем отсутствие decltype.

0 голосов
/ 26 декабря 2010

Хотя это не совсем то же самое, что typeof, C ++ 0x имеет decltype.

0 голосов
/ 26 декабря 2010

Поскольку boost распространяется в виде исходного кода (а BOOST_TYPEOF - это реализация файла заголовка в любом случае), вы, конечно, можете просто взглянуть. В нем много условной компиляции, специфичной для компилятора, поэтому ответ заключается в том, что она зависит от компилятора.

0 голосов
/ 26 декабря 2010

Это определяется как:

#define BOOST_TYPEOF(Expr) \
boost::type_of::decode_begin<BOOST_TYPEOF_ENCODED_VECTOR(Expr) >::type

Я не знаю деталей, но, похоже, он ищет тип в какой-то таблице и расширяется до (запись таблицы) :: type

Нет, невозможно узнать размер, не зная типа. Просто никто не думал добавлять typeof в C ++ 03.

...