Other considerations and tips
Native typeof support and emulation
Многие компиляторы уже поддерживают тип GCC и Metrowerks.
Игорь Чесноков открыл метод, позволяющий реализовать<typeof
>на VC-серии компиляторов. Он использует ошибку в компиляторе Microsoft, которая позволяет определить вложенный класс базы в классе, полученном из базы:
template<int ID> struct typeof_access
{
struct id2type;
};
template<class T, int ID> struct typeof_register : typeof_access
{
struct typeof_access::id2type
{
typedef T type;
};
};
typeof_register<T, compile-time-constant> register_type(const T&);
sizeof(register_type(some-type));
typedef typeof_access::id2type::type type;
Педер Холт адаптировал этот метод к VC7.0, где вложенный класс является шаблонным классом, который специализируется на производном классе.
В VC8.0 казалось, что все баги были исправлены, но Стивену Ватанабе удалось внедрить более строгую версию исправления VC7.0, которая позволяет поддерживать «тип» и здесь.
Для многих других компиляторов ни встроенная поддержка<typeof
>, ни описанный выше трюк не являются вариантом. Для таких компиляторов метод эмуляции является единственным способом реализации<typeof
>.
По приблизительным оценкам, на момент написания этой статьи введение стандарта<typeof
>,<auto
>и т.д. в стандарт C++ может произойти не скоро. Даже после того, как это будет сделано, пройдет некоторое время, прежде чем большинство компиляторов реализуют эту функцию. Но даже после этого всегда есть устаревшие компиляторы для поддержки (например, сейчас, в 2005 году, многие люди все еще используют VC6, долго после того, как VC7.x и даже бета-версия VC8.0 стали доступны).
Учитывая чрезвычайную полезность функции прямо сейчас, кажется, имеет смысл реализовать ее на библиотечном уровне.
Режим эмуляции кажется важным, даже если на каком-то конкретном компиляторе присутствует лучший вариант. Если автор библиотеки хочет разработать переносной код с использованием<typeof
>, ей необходимо использовать режим эмуляции и зарегистрировать свои типы и шаблоны. Те пользователи, у которых есть лучший вариант, все равно могут воспользоваться им, поскольку макросы регистрации определяются как no-op на таких компиляторах, в то время как пользователи, для которых эмуляция является единственным вариантом, будут использовать его.
Другое соображение касается пользователей VC7.1. Несмотря на то, что более удобный трюк<typeof
>доступен, следует рассмотреть возможность обновления до VC8, где эмуляция остается единственным вариантом.
Режим эмуляции может быть наложен на компиляторы, которые не используют его по умолчанию, определяя символ<BOOST_TYPEOF_COMPLIANT
>:
g++ -D BOOST_TYPEOF_COMPLIANT -I \boost\boost_1_32_0 main.cpp
The three participating parties
Пример Lambda из раздела «Мотивация» требует следующей регистрации:
#include BOOST_TYPEOF_INCREMENT_REGISTRATION_GROUP()
BOOST_TYPEOF_REGISTER_TEMPLATE(boost::tuples::tuple, 2);
BOOST_TYPEOF_REGISTER_TEMPLATE(boost::lambda::lambda_functor, 1);
BOOST_TYPEOF_REGISTER_TEMPLATE(boost::lambda::lambda_functor_base, 2);
BOOST_TYPEOF_REGISTER_TEMPLATE(boost::lambda::relational_action, 1);
BOOST_TYPEOF_REGISTER_TEMPLATE(boost::lambda::logical_action, 1);
BOOST_TYPEOF_REGISTER_TEMPLATE(boost::lambda::other_action, 1);
BOOST_TYPEOF_REGISTER_TYPE(boost::lambda::greater_action);
BOOST_TYPEOF_REGISTER_TYPE(boost::lambda::less_action);
BOOST_TYPEOF_REGISTER_TYPE(boost::lambda::and_action);
BOOST_TYPEOF_REGISTER_TEMPLATE(boost::lambda::placeholder, (int));
Может показаться, что цена за возможность обнаружить тип выражения слишком высока: требуется довольно большой объем регистрации. Однако обратите внимание, что вся вышеупомянутая регистрация производится только один раз, и после этого будет обработана любая комбинация зарегистрированных типов и шаблонов. Более того, эта регистрация обычно выполняется не конечным пользователем, а слоем поверх какой-либо библиотеки (в этом примере — Boost.Lambda).
Размышляя об этом, полезно рассмотреть три стороны: тип объекта, библиотеку (вероятно, построенную на принципе шаблонов выражения) и конечного пользователя. Тип объекта отвечает за регистрацию основных типов. Библиотека может регистрировать собственные типы и шаблоны.
В лучшем случае, если выражения всегда состоят только из фундаментальных типов и определяемых библиотекой типов и шаблонов, автор библиотеки может создать впечатление, что<typeof
>изначально поддерживается для ее библиотеки. С другой стороны, чем чаще выражения содержат определяемые пользователем типы, тем больше ответственность возлагается на конечного пользователя, и поэтому тем менее привлекательным становится этот подход.
Таким образом, соотношение определяемых пользователем типов в выражениях должно быть основным фактором, который следует учитывать при принятии решения о том, следует ли применять тип объекта.
Библиотека Typeof предварительно регистрирует основные типы. Для этих типов и для любых других типов/шаблонов, зарегистрированных библиотекой пользователя или конечным пользователем, поддерживается любая комбинация из следующего:
- указатели;
- ссылки (кроме верхнего уровня);
- Конст (кроме топ-уровня);
- Волатилес (кроме высшего уровня);
- массивы;
- Функции, указатели функций и ссылки;
- указания на функции членов;
- Указатели для членов данных.
Например, следующего типа:
int& (*)(const volatile char*, double[5], void(*)(short))
Поддерживается сразу и что-то вроде:
void (MyClass::*)(int MyClass::*, MyClass[10]) const
При этом он должен быть зарегистрирован<MyClass
>.
Библиотека Typeof также предоставляет регистрационные файлы для большинства классов STL. Эти файлы расположены в подкаталоге std и названы в честь соответствующих заголовков STL. Эти файлы не включены в тип системы и должны быть явно включены пользователем, по мере необходимости:
#include <boost/typeof/std/functional.hpp>
BOOST_AUTO(fun, std::bind2nd(std::less<int>(), 21));
What needs to be registered?
Можно воспользоваться компилятором при регистрации типов библиотеки Typeof. Несмотря на то, что в настоящее время нет прямой поддержки типа языка, компилятор знает, что такое тип выражения, и дает ошибку, если он сталкивается с выражением, которое не было обработано правильно. В контексте<typeof
>это сообщение об ошибке будет содержать подсказки о том, какие типы должны быть зарегистрированы в Библиотеке типов, чтобы<BOOST_TYPEOF
>работало.
struct X {};
template<typename A,bool B>
struct Y {};
std::pair<X,Y<int,true> > a;
BOOST_AUTO(a,b);
Следующее сообщение об ошибке от VC7.1
error C2504: 'boost::type_of::'anonymous-namespace'::encode_type_impl<V,Type_Not_Registered_With_Typeof_System>' : base
class undefined
with
[
V=boost::type_of::'anonymous-namespace'::encode_type_impl<boost::mpl::vector0<boost::mpl::na>,std::pair<X,Y<int,true>>>::V0,
Type_Not_Registered_With_Typeof_System=X
]
Проверяя это сообщение об ошибке, мы видим, что компилятор жалуется на<X
>.
BOOST_TYPEOF_REGISTER_TYPE(X);
Перекомпиляция, мы получаем новое сообщение об ошибке от VC7.1
error C2504: 'boost::type_of::'anonymous-namespace'::encode_type_impl<V,Type_Not_Registered_With_Typeof_System>' : base
class undefined
with
[
V=boost::type_of::'anonymous-namespace'::encode_type_impl<boost::mpl::vector0<boost::mpl::na>,std::pair<X,Y<int,true>>>::V1,
Type_Not_Registered_With_Typeof_System=Y<int,true>
]
Проверяя это сообщение об ошибке, мы видим, что компилятор жалуется<Y<int,true>
>. Поскольку<Y
>является шаблоном и содержит интегральные константы, мы должны проявлять большую осторожность при регистрации:
BOOST_TYPEOF_REGISTER_TEMPLATE(Y,(typename)(bool));
Хорошей идеей является поиск точного определения<Y
>, когда оно содержит интегральные константы. Для простых классов шаблонов, содержащих только имена типов, можно полагаться исключительно на ошибку компилятора.
Вышеприведенный код теперь компилируется.
Этот метод может быть использован для получения обзора того, какие типы должны быть зарегистрированы для данного проекта для поддержки<typeof
>.
Вложенные параметры шаблона шаблона не поддерживаются, например:
template<template<template<class> class> class Tpl>
class A;
Классы и шаблоны, вложенные в другие шаблоны, также не могут быть зарегистрированы из-за проблемы невоспитанного контекста. Это ограничение наиболее заметно в отношении стандартных итераторов в Dinkumware STL, которые реализованы в виде вложенных классов. Вместо этого инстанциации могут быть зарегистрированы:
BOOST_TYPEOF_REGISTER_TYPE(std::list<int>::const_iterator)