Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 [ 25 ] 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105

Holder<int>

Тоже, что и на рис. 3.2

GenScatterHierarchy <int,Holder>

GenScatterHierarchy <TYPELIST 2(string,Widget),Holder>

GenScatterHierarchy <int,Holder>

GenScatterHierarchy <TYPELIST.3(int,string,Wldget),Holder>

Widgetlnfo

Рис. 3.4. Класс Widgetlnfo наследует свойства класса Holder<int> дважды

Нам необходимо средство, позволяющее выбирать поля в конкретизации класса GenScatterHierarchy на основе позиционного индекса (positional index). Если бы мы могли ссылаться на каждое из этих двух полей, имеющих тип int, в списке типов (т.е. Field<0>(obj) и Field<l>(obj)), то неоднозначность была бы устранена.

Попробуем реализовать функцию доступа к полю по его позиции в списке типов. Нам нужно выполнить статическую диспетчеризацию для поля, имеющего индекс О, что соответствует голове списка типов, и поля с ненулевым индексом, соответствующего хвосту списка. Это довольно легко сделать с помощью небольшого шаблонного класса IntZType, определенного в главе 2. Напомним, что класс IntZType просто преобразовывает каждую отдельную целочисленную константу в отдельный тип. Кроме того, как показано ниже, для передачи полученного результата используется класс TypeZType.

template <class н, typename R>

inline R& FieldHelper(H& obj, TypeZType<R>, lntZType<0>) {

typename H::LeftBase& subobj = obj; return subobj;

template <class H, typename R, int i>

inline R& FieldHelper(H& obj, TypeZType<R> tt, lntZType<i>) {

typename H::RightBase& subobj = obj;

return FieldHelperCsubobj, tt, lntZType<i-l>());

класс FieldTraits описан в файле HierarchyGenerators.h template <int i, class н>

template Private;;FieldTraits<H>::At<i>::Result& Field(H& obj)



typedef typename Private::FieldTraits<H>::At<i>::Result Result;

return FieldHelperCobj, Type2Type<Result>(), lnt2Type<i>());

Остается понять, как написана такая реализация, но, к счастью, это достаточно легко объяснить. Всю работу выполняют две перегруженные функции FieldHelper. Первая из них получает параметр, имеющий тип lnt2Type<0>, а вторая - параметр ltn2Type<y o6oe целое число. Следовательно, первая перегруженная функция возвращает значение, соответствующее классу unit<Tl>&, а вторая- тип, имеющий указанный индекс в списке типов. Для того чтобы определить, какой тип следует вернуть, функция Field использует вспомогательный шаблонный класс FieldTraits. Функция Field возвращает этот тип функции FieldHelper через класс туре2туре.

Вторая перегруженная функция FieldHelper рекурсивно вызывает саму себя, передавая правого предка класса GenScatterHierarchy и тип lnt2Type<index-l>, поскольку поле N в списке типов является полем N-1 в хвосте этого списка, если N не равно нулю. (Действительно, случай, когда N равно нулю, обрабатьшается первой перегруженной функцией.)

Чтобы упростить интерфейс, нам нужно предусмотреть в классе Field две дополнительные функции: константные версии двух определенных выше функций Field. Эти функции очень похожи на свои неконстантные прототипы, но, в отличие от них, принимают и возвращают ссылки на константные типы.

Функция Field намного облегчает использование класса GenScatterHierarchy. Теперь можно написать следующий код.

widgetinfo obj;

int x = Field<0>(obj).value ; первый целый тип int x = Field<l>(obj).value ; второй целый тип

Шаблонный класс GenScatterHierarchy очень удобен для генерации сложных классов на основе списков простых типов. Класс GenScatterHierarchy можно применять для генерации виртуальных функций для каждого типа в списке. В главе 9, посвященной абстрактным фабрикам, этот класс используется для генерации абстрактных производящих функций, работающих со списками типов. В этой главе также показано, как реализуются иерархии, порожденные классом GenScatterHierarchy.

3.13.2. Генерация кортежей

Иногда возникает необходимость создать небольшую структуру, состоящую из безымянных полей, известную в некоторых языках (например, в языке ML) под названием кортеж (tuple). Создание кортежей на языке С++ впервые было описано в работе Якко Ярви (Jakko Jarvi, 1999а), а затем уточнено в работе (Jarvi and Powell, 1999b).

Что такое кортеж? Рассмотрим следующий пример.

template <class т> struct Holder {

т value ;

typedef GenScatterHierarchy< TYPELlST 3(int, int, int).



Holder> PointSO;

Работать с классом PointSO довольно трудно, поскольку после каждой функции доступа к полям необходимо указывать суффикс .value . Нам нужно создать структуру, похожую на класс GenScatterHierarchy, в которой функции доступа Field возвращают ссылки непосредственно на члены value . Это значит, что функция Field<n> должна возвращать не Holder<int>, а int&.

В библиотеке Loki определен класс Tuple, реализованный аналогично классу GenScatterHierarchy, но предоставляющий прямой доступ к полям. Этот класс работает следующим образом.

typedef Tuple<TYPELlST 3Cint, int, int)>

PointSo; PointSD pt; Field<0>(pt) = 0; Field<l>(pt) = 100; Field<2>(pt) = 300;

Кортежи полезны для создания небольших безымянных структур, не имеющих функций-членов. Например, с их помощью можно возвращать из функции сразу несколько значений.

Tuple<TYPELlST 3Cint, int, int)> GetwindowPlacementCwindow&);

Фиктивная функция Getwi ndowPl acement позволяет пользователям узнавать координаты окна и его положение в стеке окон, используя один вызов функции. При этом разработчик библиотеки не обязан предусматривать отдельную структуру для кортежа, состоящего из трех целых чисел.

Другие функции, работающие с кортежами, можно найти в файле Tuple.h библиотеки Loki.

3.13.3. Генерация линейных иерархий

Рассмотрим следующий простой шаблонный класс, определяющий интерфейс обработчика события. В нем определяется только функция-член OnEvent.

template <class Т> class EventHandler {

public:

virtual void OnEventCconst T&, int eventid) = 0; virtual void -EventHandlerC) {}

В классе EventHandler определен виртуальный деструктор, не представляющий для нас интереса, но тем не менее необходимый (причины будут указаны в главе 4).

Шаблонный класс GenScatterHierarchy можно использовать для того, чтобы распространить класс EventHandler на каждый тип в списке.

typedef GenScatterHierarchy <

TYPELlST 3(window, Button, ScrollBar), EventHandler

>

widgetEventHandler;



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 [ 25 ] 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105