Пример шаблона посетителя
Я заинтересован в Typelists . По этому адресу http://drdobbs.com/184403813 приведен хороший пример использования списков типов для создания шаблона посетителя.
У меня есть два вопроса об этом примере. Мои два вопроса в конце этой темы.
Рассмотрим код ниже:
void SomeOperation(DocumentItem* p)
{
if (TextArea* pTextArea = dynamic_cast<TextArea*>(p))
{
... operate on a TextArea object ...
}
else if (VectorGraphics* pVectorGraphics =
dynamic_cast<VectorGraphics*>(p))
{
... operate on a VectorGraphics object ...
}
else if (Bitmap* pBitmap = dynamic_cast<Bitmap*>(p))
{
... operate on a Bitmap object ...
}
else
{
throw "Unknown type passed";
}
}
Недостатками этого кода по Александреску являются:
Помимо того, что в приведенном выше коде есть не совсем уродливый код, у него есть концептуальная проблема: он не может поймать во время компиляции "забыл обработать этот тип"
Итак, идут типовые списки:
#include<iostream>
class null_typelist {};
template <class H, class T>
struct typelist
{
typedef H head;
typedef T tail;
};
template<class T1, class T2=null_typelist, class T3=null_typelist, class T4=null_typelist> struct cons;
template <class T1>
struct cons<T1, null_typelist, null_typelist,null_typelist>
{
typedef typelist<T1, null_typelist> type;
};
template <class T1, class T2>
struct cons<T1, T2, null_typelist, null_typelist>
{
typedef typelist<T1, typelist<T2,null_typelist> > type;
};
template <class T1, class T2, class T3>
struct cons<T1, T2, T3, null_typelist>
{
typedef typelist<T1, typelist<T2, typelist<T3,null_typelist> > > type;
};
template <class T1, class T2, class T3, class T4>
struct cons
{
typedef typelist<T1, typelist<T2, typelist<T3,typelist<T4, null_typelist> > > > type;
};
template <class tlist> class AdHocVisitor;
template <class H, class T>
class AdHocVisitor< typelist<H, T> > : public AdHocVisitor<T>
{
public:
virtual void Visit(H*) = 0;
template <class SomeClass>
void StartVisit(SomeClass* p)
{
if (H* pFound = dynamic_cast<H*>(p))
{
Visit(pFound);
}
else
{
AdHocVisitor<T>::StartVisit(p);
}
}
};
template <class H>
class AdHocVisitor< typelist<H, null_typelist> >
{
public:
virtual void Visit(H*) = 0;
template <class SomeClass>
void StartVisit(SomeClass* p)
{
if (H* pFound = dynamic_cast<H*>(p))
{
Visit(pFound);
}
else
{
throw "Unknown type passed";
}
}
};
struct DocElement{virtual ~DocElement(){};};
struct TextArea: DocElement{};
struct Bitmap: DocElement{};
struct VectorGraphics: DocElement{};
int main()
{
typedef cons<TextArea,Bitmap,VectorGraphics>::type MyHierarchy;
DocElement *p = new Bitmap;
struct ConcreteVisitor : AdHocVisitor<MyHierarchy>
{
void Visit(TextArea* p){std::cout << "I'm a textarea" << "\n";}
void Visit(VectorGraphics* p){std::cout << "I'm a VectorGraphics" << "\n";}
void Visit(Bitmap* p){std::cout << "I'm a Bitmap" << "\n";}
};
ConcreteVisitor visitor;
visitor.StartVisit(p);
delete p;
std::cin.get();
}
1- У нас все еще есть dynamic_cast и виртуальная функция. Так что я не вижу пользы от введения типовых списков?
2- В конце этой статьи Александреску дает несколько советов по улучшению этого кода, но я не очень хорошо понимаю, как их реализовать, может кто-нибудь помочь мне в этом?
Спасибо
1 ответ
Что делать, если у вас есть 50 типов DocElement? В первом примере вам нужно 50 операторов if, а во втором вам просто нужно иметь список типов из 50 элементов.
Вы также можете подумать о том, что происходит, когда вы добавляете еще один DocElement. В первом примере вам нужно перейти и изменить операторы if-else. С помощью списков типов вы можете просто добавить новый тип в конец списка типов.
Код списка типов может показаться слишком трудоемким, но вы пишете его один раз, затем просто используете его, и вместо добавления ifs или case и кода (который может стать довольно большим со временем) вы просто добавляете типы в тип -список. С точки зрения обслуживания код списка типов намного лучше, чем огромный оператор switch или десятки или сотни ifs.
Что касается улучшений, я не знаю, я все еще жду, когда в VS будут включены шаблоны с переменным числом переменных и псевдонимы типов, чтобы я мог еще больше упростить код.
Всякий раз, когда я вижу огромную кучу повторяющегося кода, я начинаю думать о списках типов и метапрограммировании, пусть компилятор делает работу, а не скучающие программисты. И лучшая часть? Вы получаете нулевой штраф во время выполнения, он так же эффективен, как и ifs (если вы осторожны с встраиванием)