|
Программирование >> Многопоточная библиотека с принципом минимализма
После уточнения класса Cyclicvisitor с помощью оператора typedef, скажем, классом Myvisitor, нам остается лищь применить макрос DEFlNE CYCLl<: vislTABLECMyvisi tor) в соответствующих Инспектируемых классах. typedef Cyclicvisitor < void, тип возвращаемого значения TYPELlST 3CDocElement, Paragraph, RastereitMap) > Myvisitor; class DocElement { public: virtual void visit(Myvisitor*) =0; class paragraph : public DocElement { public: DEFINE CYCLIC VISITABLE(MyVisitor); Вот и все! Как и в классической реализации щаблона visitor, предложенной группой GoF, нужно лищь быть дисциплинированным. Отличие заключается в том, что теперь вам придется держать в голове намного меньще информации. Для того чтобы сделать иерархию инспектируемой в рамках реализации простого щаблона visitor, нужно выполнить следующие действия. Сделать неполное объявление всех классов, входящих в иерархию. Написать оператор typedef для класса Cyclicvisitor, конкретизированного типом возвращаемого значения и списком инспектируемых типов. (Назовем этот класс Myvisitor.) Определить в базовом классе чисто виртуальную функцию visit. Добавить макрос DEFiNE CYCLlC visiTABLE(Myvisitor) в каждый класс иерархии или реализовать функцию Accept вручную. Сделать каждый конкретный инспектор наследником класса Myvisitor. Обновлять конкретизацию щаблонного класса Myvisitor (оператор typedef) каждый раз, когда в иерархию инспектируемых классов добавляется новый класс, и - увы! - компилировать ее снова. По сравнению с реализацией обычного щаблона visitor обобщенный подход более понятен. Программисту нужно лищь самостоятельно определить класс Myvisitor. С практической точки зрения лучще начинать с щаблона Acyclic visitor, который легче поддерживать, а на щаблон Visitor следует переходить лищь тогда, когда оптимизация становится действительно необходимой. Обобщенные компоненты позволяют легко экспериментировать - для этого нужно изменить лищь одно объявление. Остальной код остается без изменения. Детали реализации щаблона visitor хранятся в библиотеке. Нужно лищь наладить связь между клиентом и библиотекой, чтобы получить доступ к двум разным реализациям шаблона visitor. 10.6. Отладка вариантов Шаблон Visitor имеет много вариантов и настроек. В этом разделе мы покажем, как его можно применять в собственных приложениях. 10.6.1. Функция-ловушка Эта функция рассматривалась в разделе 10.2. Класс visitor может столкнуться с неизвестным типом, производным от базового класса (например, классом DocElement). В этом случае либо компилятор выдаст сообщение об ощибке, либо во время выполнения профаммы будут произведены какие-то действия, предусмотренные по умолчанию. Посмотрим, как рещается эта проблема в реализациях шаблонов visitor и Асу-clicvisitor с помощью обобщенных компонентов. Для обычного щаблона Visitor ситуация проста. Если базовый класс вашей иерархии входит в список типов, передаваемых классу CyclicVisitor, существует возможность реализовать функцию-ловушку. В противном случае возникнет ошибка компиляции. Эти две возможности иллюстрируются следующим кодом. неполные объявления, необходимые шаблону обычному visitor class DocElement; Базовый класс class Paragraph; class RasterBitmap; class vectorizedDrawing; typedef CyclicVisitor < void, Тип возвращаемого значения TYPELIST 3(Paragraph, RasterBitmap, VectorizedDrawing) > Strictvisitor; Операции перехвата не предусмотрены. при попытке инспектирования неизвестного типа возникает ошибка компиляции typedef CyclicVisitor < void, Тип возвращаемого значения TYPELlST 4(DocElement,Paragraph, RasterBitmap, Vectori zedDrawi ng) > NonStrictvisitor; объявляет функцию visit(DocElement&), которая будет вызываться при попытке проинспектировать неизвестный тип Все это довольно просто. Теперь рассмотрим функцию-ловушку в обобщенной реализации щаблона Acycliс visitor. Здесь применяется интересный прием. Хотя по существу перехват касается инспектирования неизвестного класса известным инспектором, проблема переворачивается: неизвестный инспектор инспектирует известный класс! Вернемся к реализации функции Acceptimpl для шаблона Acyclic visitor. template <typename R = void> class Basevisi table ... как и раньше ... template <class т> static ReturnType Acceptimpl(т& visited, Basevisitor* guest) if (visitor<T>* p = dynamic cast<visitor<T>*>(&guest)) return p->visit(visited); return ReturnTypeO; внимание! Допустим, мы добавляем в иерархию класса DocElement класс vectorizeDrawing, ничего не сообщая об этом конкретным инспекторам. При попьптсе проинспектировать класс VectorizeDrawing динамическое приведение к типу visitor<vectorizeDrawing> завершится аварийно, поскольку классу Visitor ничего не известно о классе VectorizeDrawing. Следовательно, код активирует альтернативную процедуру и вернет значение типа ReturnType, предусмотренное по умолчанию. Именно в этом месте вступает в действие функция-ловущка. Поскольку наща функция Acceptimpl содержит операцию return ReturnTypeO, простора для вариаций не остается. Здесь следует применить стратегию перехвата. template < typename R = void. template <typename, class> class CatchAll = DefaultCatchAll > class Basevisi table { ... как и раньше ... template <class т> static ReturnType Acceptimpl(т& visited, Basevisitor* guest) { if (visitor<T>* p = dynamic cast<visitor<T>*>(&guest)) { return p->visit(visited); Изменение return CatchAll<R, T>::OnUnknownvisitor(visited, guest); Стратегия CanchAll вполне соответствует требованиям щаблона. Она может возвращать значение по умолчанию или код ошибки, генерировать исключительную ситуацию, вызывать виртуальную функцию-член для инспектируемого объекта, пытаться выполнять динамическое приведение типов и т.д. Реализация функции onUn known-visitor значительно зависит от нужд конкретного приложения. В некоторых случаях необходимо, чтобы инспектирование было выполнено для всех типов, входящих в иерархию. В других случаях это относится лишь к некоторым типам, а остальные можно проигнорировать. В реализации шаблона Acyclic Visitor в основном применяется второй подход, поэтому по умолчанию стратегия CatchAll имеет следующий вид. template <class R, class visited> struct DefaultCatchAll static R Onunknownvisi tor(visited*, Basevisitor*) Здесь указываются действия, которые следует предпринять при несоответствии между инспектором и и инспектируемым объектом, обычно должно
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |