Анимация
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

значения, и штриховка в обоих случаях должна оставаться одинаковой. В этом случае нужен симметричный мультиметод (symmetric multimetiiod), который не зависит от порядка следования его аргументов.

Симметрия применяется, только если типы обоих параметров идентичны (в нашем случае класс BaseLhs совпадает с классом BaseRhs, а LhsTypes совпадает с классом RhsTypes).

Класс StaticDispatcher, реализованный в рамках грубого подхода, является асимметричным. Это значит, что он не поддерживает симметричные мультиметоды. Допустим, например, что мы определяем следующие классы.

class HatchingExecutor {

public:

void Fire(Rectangle*, Rectangle*) void Fire(Rectangle*, Ellipse*)

Обработчик ошибок

void onError(Shape*, Shape*);

typedef StaticDispatcher <

Hatchi ngExecutor, Shape,

TYPELIST 3(Rectangle, Ellipse, Poly)

>

HatchingDispatcher;

Класс Hatchi ngDispatcher не срабатывает, если ему передать в качестве левого параметра класс Ellipse, а в качестве правого параметра - класс Rectangle. Даже несмотря на то, что с точки зрения класса HatchingExecutor не имеет никакого значения, какой из этих параметров является левым, а какой- правым, класс Hatchi ngDispatcher настаивает, чтобы объекты передавались в определенном порядке.

Поменяв аргументы местами и применив другую перегрузку в клиентском коде, можно HcnpaBHtb этот недостаток.

class HatchingExecutor {

public:

void Fire(Rectang1e*, Ellipse*);

обеспечиваем симметрию

void Fire(Enipse* Ihs, Rectangle* rhs)

переходим к функции Fire(Rectangle*, Ellipse*), меняя порядок аргументов Fire(rhs, Ihs);

Эта небольшая функция обеспечивает симметричность мультиметода.

В идеале класс StaticDispatcher должен был бы сам предоставлять эту возможность с помощью дополнительного булевского шаблонного параметра. Для этого нужно, чтобы в некоторых ситуациях класс StaticDispatcher менял порядок следования аргументов при обратном вызове. В каких именно ситуациях? Проанализируем предыдущий пример. Дополняя список шаблонных аргументов их значениями, заданными по умолчанию, мы получаем следующую конкретизацию.



typedef StaticDispatcher <

HatchingExecutor, Shape,

TYPELIST 2(Rectangle, Ellipse, Poly), класс TypesLhs Shape,

TYPELIST 2(Rectangle, Ellipse, Poly), класс TypesRhs void

>

Hatchi ngDi spacther;

В симметричном диспетчере можно применить следующий алгоритм выбора пар параметров: объединим первый тип из первого списка типов (класс TypeLhs) с каждым типом из второго списка (класс TypesRhs). Это порождает три комбинации: Rectangle-Rectangle, Rectangle-Ellipse и Rectangle-Poly. Затем объединим второй тип из списка TypesLhs с типами из списка TypesRhs. Однако, поскольку первая комбинащ1Я (Rectangle-Ellipse) уже бьша создана на первом этапе, на этот раз алгоритм начинается со второго элемента в списке TypesRhs. Теперь порождаются пары Ellipse-Ellipse и Ellipse-Poly. Те же рассуждения применяются на следующем щаге: объединение класса Poly из списка TypesLhs с типами из списка TypesRhs начинается с третьего элемента. На этом этапе порождается только одна комбинация - Pol у-Ро1 у.

Следуя этому алгоритму, мы реализуем функции только для полученных комбинаций.

class HatchingExecutor {

public:

void Fire(Rectangle&, Rectangle*); void Fire(Rectangle&, Ellipse*); void Fire(Rectangle*, Poly*); void Fire(Ellipse*, Ellipse*); void Fire(Ellipse*, Poly*); vois Fire(Poly*, Poly*); Обработчик ошибок void OnError(shape&, Shape*);

Класс StaticDispatcher должен распознавать комбинации, исключенные нами из описанного выще алгоритма, а именно: Ellipse-Rectangle, Poly-Rectangle и Poly-Ellipse. Для этих комбинаций класс StaticDispatcher должен менять аргументы местами, а в остальных случаях поступать, как и прежде.

Какое булевское условие позволяет определять, когда следует менять аргументы местами, а когда нет? Описанный выще алгоритм выбирает в списке TL2 лищь типы, имеюшие индексы, равные или превышающие индекс типа в списке tlI. Следовательно, условие формулируется следующим образом.

Если индекс типа и в списке TypesRhs меньше, чем индекс типа т в списке TypesLhs, аргументы следует поменять Ктестами.

Допустим, что типом т является класс Ellipse, а типом и - класс Rectangle. Тогда индекс типа т в списке TypesLhs равен 1, а индекс типа и в списке TypesThs равен 0. Следовательно, аргументы типа Ellipse и Rectangle перед вызовом функции Executor:: Fi re следует поменять местами.

В набор функциональных возможностей списков типов уже входит статический алгоритм indexOf, возвращающий позицию типа в списке. Таким образом, сформулировать условие перестановки параметров довольно легко.



Во-первых, мы должны добавить новый шаблонный параметр, который позволяет определить, является ли диспетчер симметричным. Затем нужно добавить небольшой шаблонный класс характеристик invocationTraits, который либо переставляет аргументы, либо оставляет их на месте перед вызовом функции-члена Executor: Fi re.

template <

class Executor,

bool symmetric,

class BaseLhs,

class TypesLhs,

class BaseRhs = BaseLhs,

class TypesRhs = TypesLhs,

typename ResultType = void

>

class StaticDispatcher {

template <bool swapArgs, class SomeLhs, class someRhs>

struct InvocationTraits

static void DoDispatchCsomeLhsA Ihs, someRhsA rhs, Executor* exec)

exec.FireClhs, rhs);

template <class SomeLhs, class SomeRhs>

struct invocationTraits<True, SomeLhs, someRhs>

static void DoDispatch(SomeLhs* Ihs, SomeRhs* rhs, Executor* exec)

exec.Fire(rhs, Ihs); меняем аргументы местами

public:

static void DispatchRhs(BaseLhs* Ihs, BaseRhs* rhs. Executor exec)

if (Head* p2 = dynamic cast<Head*>(*rhs)) {

enum { swapArgs = symmetric *&

IndexOf<Head, TypesRhs>::result < IndexOf<BaseLhs, TypesLhs>::result };

typedef lnvocationTraits<swapArgs, BaseLhs, Head> CallTraits;

return CallTraits::DoDispatch(lhs, *p2);

else {

return staticDispatcher<Executor, BaseLhs, NullType, BaseRhs, Tail>::DispatchRhs( Ihs, rhs, exec);

Поддержка симметрии немного усложняет класс StaticDispatch, но намного облегчает его использование.



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