паттерн посетитель (Visitor)
От: Abyx Россия  
Дата: 06.06.11 19:35
Оценка: :))
Понадобилось реализовать паттерн Visitor, и что-то не получается придумать как сделать так чтоб было и просто и удобно и эффективно %)
(GoF читал, Александреску читал, сам думал )

Значится есть иерархия классов
struct visitor;

struct node
{
   virtual void accept(visitor* v) = 0;
};

struct some : node
{
   virtual void accept(visitor* v);
};

struct other : node
{
   virtual void accept(visitor* v);
};


и хочется делать таких посетителей, чтоб без кучи перегрузок виртуальных методов, например как в boost.variant
struct test_visitor
{
   void operator()(some&) { std::cout << "some visited\n"; }
   void operator()(other&) { std::cout << "other visited\n"; }
   template<class T> void operator()(T&) { std::cout << "i-dunno-what visited\n"; }
};

int main()
{
   some o;
   node* n = &o;

   test_visitor vis;
   apply_visitor(vis, n);
}


код visitor'а и apply_visitor можно сделать таким:

struct visitor
{
   virtual void visit(some&) = 0;
   virtual void visit(other&) = 0;
};

template<class Vis>
inline void apply_visitor(Vis& vis, node* n)
{
   struct visitor_impl : visitor
   {
      virtual void visit(some& n) { vis(n); }
      virtual void visit(other& n) { vis(n); }

      visitor_impl(Vis& vis) : vis(vis) {}
      Vis& vis;
   };

   visitor_impl impl(vis);
   n->accept(&impl);
}

void some::accept(visitor* v) { v->visit(*this); } // тут может быть нетривиальный код обхода структуры
void other::accept(visitor* v) { v->visit(*this); }


и всё бы тут вобщем-то и хорошо, но расстраивает то, что тут надо два раза перечислять классы — в visitor и в visitor_impl

хорошо бы как-нибудь шаблоны применить, чтоб этого избежать — к примеру visitor::visit совсем не обязательно должна быть виртуальной, она вполне может быть шаблонной

код полностью: http://codepad.org/RjBdzVAh
In Zen We Trust
visitor pattern
Re: паттерн посетитель (Visitor)
От: Abyx Россия  
Дата: 06.06.11 22:33
Оценка:
применив немножко MPL, получил следующее:

#include <boost/mpl/vector.hpp>
#include <boost/mpl/find.hpp>
#include <boost/mpl/distance.hpp>
#include <boost/mpl/begin_end.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/size.hpp>

struct visitor
{
    typedef boost::mpl::vector<
        some,
        other
    >::type types;

    template<class Impl>
    visitor(Impl* impl) : impl(impl), onVisit(&ImplWrap<Impl>::on_visit) {}

    template<typename T>
    void visit(T& obj)
    {
        typedef typename boost::mpl::find<types, T>::type iter;
        const int idx = boost::mpl::distance<boost::mpl::begin<types>::type, iter>::type::value; //iter::pos::value; ???
        onVisit(impl, obj, idx);
    }

    template<class Impl>
    struct ImplWrap
    {
        typedef void (*call_visit_fn_t)(Impl& impl, node& el);

        template<int idx>
        static void call_visit(Impl& impl, node& obj)
        {
            impl(static_cast<boost::mpl::at_c<types, idx>::type&>(obj));
        }

        template<int idx>
        static bool fill_item(call_visit_fn_t* table)
        {
            table[idx - 1] = &call_visit<idx - 1>;
            return fill_item<idx - 1>(table);
        }

        template<>
        static bool fill_item<0>(call_visit_fn_t*)
        {
            return 0;
        }

        static void on_visit(void* impl, node& obj, int typeIdx)
        {
            static const size_t tableSize = boost::mpl::size<types>::value;

            static call_visit_fn_t table[tableSize];
            static bool tableInitialized = fill_item<tableSize>(table);
            
            table[typeIdx](*(Impl*)impl, obj);
        }
    };

    void* impl;
    void (*onVisit)(void* impl, node& obj, int typeIdx);
};

template<class Vis>
inline void apply_visitor(Vis& vis, node* n)
{
    visitor impl(&vis);
    n->accept(&impl);
}


gcc это компилить отказывается (и за специализации fill_item внутри класса) (тот который на codepad.org), а MSVC2010 компилит нормально

выглядит это страшно, хоть и без макросов, но всёравно страшно.
может можно как-то упростить, с тем же С++0х (которое есть в MSVC)
In Zen We Trust
Re: паттерн посетитель (Visitor)
От: A13x США  
Дата: 07.06.11 07:10
Оценка:
Здравствуйте, Abyx, Вы писали:

A>Понадобилось реализовать паттерн Visitor, и что-то не получается придумать как сделать так чтоб было и просто и удобно и эффективно %)

...
A>хорошо бы как-нибудь шаблоны применить, чтоб этого избежать — к примеру visitor::visit совсем не обязательно должна быть виртуальной, она вполне может быть шаблонной

Покури над ключевыми словами "double dispatch" в описании паттерна посетитель.
Re[2]: паттерн посетитель (Visitor)
От: Abyx Россия  
Дата: 07.06.11 07:24
Оценка:
Здравствуйте, A13x, Вы писали:

A>Здравствуйте, Abyx, Вы писали:


A>>Понадобилось реализовать паттерн Visitor, и что-то не получается придумать как сделать так чтоб было и просто и удобно и эффективно %)

A>...
A>>хорошо бы как-нибудь шаблоны применить, чтоб этого избежать — к примеру visitor::visit совсем не обязательно должна быть виртуальной, она вполне может быть шаблонной

A>Покури над ключевыми словами "double dispatch" в описании паттерна посетитель.


и что?
In Zen We Trust
Re[3]: паттерн посетитель (Visitor)
От: MasterZiv СССР  
Дата: 07.06.11 08:32
Оценка: 1 (1)
On 07.06.2011 11:24, Abyx wrote:
> и что?

Да и ничего, на самом деле. Это ж известный дизадвантаж
посетителя, в каталоге паттернов он есть:
Если нужно добавлять визитуемые классы, то придётся
добавлять операции в визитор. И ничего тут особенно
не сделаешь, только разве макросы тебе помогут
как-то упростить ситуацию.
Posted via RSDN NNTP Server 2.1 beta
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.