Пример шаблона посетителя - PullRequest
1 голос
/ 07 апреля 2011

Я заинтересован в Typelists.По этому URL 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 Ответ

4 голосов
/ 07 апреля 2011

Что если у вас есть 50 типов DocElement?В первом примере вам нужно 50 операторов if, а во втором вам просто нужно иметь список типов из 50 элементов.

Вы также можете подумать о том, что происходит, когда вы добавляете другой DocElement.В первом примере вам нужно пойти и изменить операторы if-else.С помощью списков типов вы можете просто добавить новый тип в конец списка типов.

Код списка типов может показаться слишком сложным, но вы пишете его один раз, а затем просто используете его.и вместо добавления ifs или case и кода (который может стать довольно большим со временем) вы просто добавляете типы в список типов.С точки зрения обслуживания код списка типов намного лучше, чем огромный оператор switch или десятки или сотни ifs.

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

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

...