Понадобилось реализовать паттерн 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
применив немножко 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)
Здравствуйте, Abyx, Вы писали:
A>Понадобилось реализовать паттерн Visitor, и что-то не получается придумать как сделать так чтоб было и просто и удобно и эффективно %)
...
A>хорошо бы как-нибудь шаблоны применить, чтоб этого избежать — к примеру visitor::visit совсем не обязательно должна быть виртуальной, она вполне может быть шаблонной
Покури над ключевыми словами "double dispatch"
в описании паттерна посетитель.
Здравствуйте, A13x, Вы писали:
A>Здравствуйте, Abyx, Вы писали:
A>>Понадобилось реализовать паттерн Visitor, и что-то не получается придумать как сделать так чтоб было и просто и удобно и эффективно %)
A>...
A>>хорошо бы как-нибудь шаблоны применить, чтоб этого избежать — к примеру visitor::visit совсем не обязательно должна быть виртуальной, она вполне может быть шаблонной
A>Покури над ключевыми словами "double dispatch" в описании паттерна посетитель.
и что?
On 07.06.2011 11:24, Abyx wrote:
> и что?
Да и ничего, на самом деле. Это ж известный дизадвантаж
посетителя, в каталоге паттернов он есть:
Если нужно добавлять визитуемые классы, то придётся
добавлять операции в визитор. И ничего тут особенно
не сделаешь, только разве макросы тебе помогут
как-то упростить ситуацию.
Posted via RSDN NNTP Server 2.1 beta