Анимация
JavaScript
|
Главная Библионтека ханизм двойной диспетчеризации, описанный в главе 11, применяет это важное свойство для распознавания информации о типах. Widget ScrollBar
Рис. 3.1. Простая иерархия классов Для упорядочения набора необходима соответствующая функция. У нас уже есть статический механизм распознавания наследования, детально описанный в главе 2 Напомним, что в нащем распоряжении есть удобный макрос SUPERSUBCLASS(Т, и), возвращающий значение true, если класс и является производным от класса Т. Мы просто объединим механизм распознавания наследования со списками типов. Применение к спискам типов полноценного алгоритма сортировки невозможно, поскольку у нас нет отнощения полной упорядоченности между типами. По этой причине для классов невозможно создать эквивалент оператора сравнения operator<. Следовательно, нам придется использовать особенный алгоритм, переставляющий производные классы вперед и не изменяющий отнощения между другими классами. DerivedToFront Входные данные: список типов TList Результат: определение внутреннего типа Result Если список TList состоит из единственного типа NullType то класс Result является типом NullType. Иначе найти в списке TList: :Tail класс самого нижнего уровня, производный от класса TList: :Head. Сохранить его во временной переменной TheMostDerived. Заменить класс TheMostDeri ved в списке TList: :Tail списком TList: :Head, получив в результате список L. Сконструировать результат в виде списка типов, головой которого является класс TheMostDeri ved, а хвостом - список L. Во время применения этого алгоритма к списку типов производные типы перемещаются на верщину, а базовые типы заталкиваются на дно. В этом описании нет одной детали - алгоритма обнаружения производного класса самого нижнего уровня в заданном списке типов. Поскольку макрос SUPERSUBCLASS во время компиляции порождает булевское значение, для решения этой задачи оказывается полезным небольшой шаблонный класс Select (также описанный в главе 2). Напомним, что этот шаблонный класс выбирает один из двух типов в зависимости от значения булевской константы. Алгоритм TheMostDeri ved получает на вход список типов и тип Base, а возвращает - наиболее глубоко вложенный класс, производный от типа Base, находящийся в списке (или, возможно, сам класс Base, если производных типов в списке нет). MostDerived Входные данные: список типов TList, тип т Результат: определение внутреннего типа Result Если список TList состоит из единственного типа NullType то класс Result является типом Т. Иначе применить алгоритм MostDerived к списку TList: :Tail и типу Base. Получить тип Candidate. Если тип TList: :Head является производным от типа candidate, то результатом является тип TList: :Head. Иначе результатом является тип candidate. Реализация алгоритма MostDerived выглядит следующим образом, template <class TList, class т> struct MostDerived; template <class T> struct MostDerived<NullType, т> { f typedef T Result; template <class Head, class Tail, class т> struct MostDerived<Typelist<Head, Tail>, т> { private: typedef typename MostDerived<Tail, T>::Result Candidate; public: typedef typename Select< SUPERSUBCLASS(Candidate, Head), Head, candidate>::Result Result; Алгоритм DerivedToFront использует алгоритм MostDerived в качестве элементарного алгоритма. Вот его реализация. template <class т> struct DerivedToFDront; tempi ateo struct DertivedToFront<Nul1туре> { typedef NullType Result; template <class Head, class Tail> struct DerivedToFront< Typelist<Head, Tai1> > private: typedef typename MostDerived<Tai1, Head>::Resu1t TheMostDerived; typedef typename Rep1ace<Tai1, TheMostDerived, Head>::Resu1t L; public: typedef Type1ist<TheMostDerived, L> Result; Эта сложная манипуляция со списком типов обладает замечательной силой. Преобразование Deri vedToFront эффективно автоматизирует обработку типов, которую иначе пришлось бы долго и тшательно реализовывать вручную. Это напоминает автоматическую поддержку параллельных иерархий классов, не так ли? 3.13. Генерация класса на основе списка типов Если до сих пор списки классов казались интересными, интригующими или даже ужасными, то это только цветочки. В этом разделе дается определение основных конструкций для генерации кода с помощью списков типов. Это означает, что мы вообще больше не будем писать профаммы, а заставим компилятор делать это за нас. Такие конструкции основаны на одной из самых мощных особенностей языка С++, которой нет ни в одном другом языке, - шаблонных шаблонных параметрах (template template parameters). До сих пор манипуляции со списками типов не порождали никакого кода. В результате обработки возникал новый список типов, типы или числовые статические константы (как в классе Length). Перейдем к изучению процесса генерации реального кода, т.е. к тем сущностям, которые оставляют свой след в скомпилированном коде. Объекты списков типов и здесь не нужны. Они не имеют никакого значения во время выполнения профаммы и никаких функциональных возможностей. При работе со списками типов особо важна возможность генерировать код на основе списка типов. Прикладные профаммисты иногда должны наполнять класс каким-то кодом - сигнатурами виртуальных функций, объявлениями переменных или реализациями функций. Способ этого заполнения определяется списком типов. Попытаемся автоматизировать этот процесс. Поскольку в языке С++ нет статических итераций или рекурсивных макросов, задача добавления кода для каждого типа, содержащегося в списке, довольно сложна. Можно применить частичную шаблонную специализацию в сочетании с описанными выше алгоритмами, но реализация этого решения будет слишком запутанной и сложной. Решить эту задачу нам поможет библиотека Loki. 3.13.1. Генерация распределенных иерархий Мощные шаблоны библиотеки Loki облегчают задачу построения классов путем применения каждого типа, содержащегося в списке типов, к основному шаблону, предоставленному пользователем. Таким образом, запутанный процесс распределения типов, указанных в списке, по профамме пользователя инкапсулирован в библиотеке. Пользователь должен лишь определить простой шаблон с одним параметром. Этот шаблонный библиотечный класс называется GetScatterHierarchy. Несмотря на то что его определение достаточно просто, этот класс невероятно эффективен. Вот его определение. Это одна из ситуаций, когда предпочтительнее сначала описать идею и лишь затем - ее потенциальные приложения (в отличие от обычного порядка решения задач). 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 |