240 likes | 441 Views
Использование библиотеки Boost.Mpl для построения программной архитектуры. Колотаев А. В. Содержание. Краткий обзор библиотеки Boost.Mpl Понятие метафункции, placeholder expression, лямбда-выражение
E N D
Использование библиотеки Boost.Mpl для построения программной архитектуры Колотаев А. В.
Содержание • Краткий обзор библиотеки Boost.Mpl • Понятие метафункции, placeholder expression, лямбда-выражение • Средства Boost.Mpl: последовательности (mpl::vector, mpl::list), алгоритмы (mpl::fold), виды (mpl::joint_view, mpl::filter_view) • Использование Boost.Mpl • Описание проекта • Поддержка приема CRTP • Реализация паттерна Состояние
Библиотека Boost.Mpl Доступна по адресу www.boost.org\libs\mpl\index.html
Метафункция Def.Класс или шаблон класса с публично доступным типом-членом type. template <class T> struct add_pointer { typedef T * type; }; Вызов: typedef add_pointer<int>::type pInt; // pInt == int*
Специализация метафункции Первичный шаблон, предоставленный разработчиком библиотеки: template <class T> struct func { typedef generic_implementationtype; }; Специализация пользователем библиотеки для своего типа: template <class Y, class Z> struct func<MyClass<Y,Z> > { typedef specialisation_for_my_classtype; };
Tag dispatching //--------------------------------------- library code template <class T> struct sequence_tag{ typedef typename T :: sequence_tag type; }; template <class T> struct library_func_impl; template <class T> struct library_func : library_func_impl<typename sequence_tag<T>::type> // ::type {}; //--------------------------------------- user code struct my_sequence_tag; struct MySequenceA{ typedef my_sequence_tag sequence_tag; /*...*/ }; struct MySequenceB{ typedef my_sequence_tag sequence_tag; /*...*/ }; template <> struct library_func_impl<my_sequence_tag> { typedef my_implementationtype; };
Класс метафункции Def.Класс метафункции – класс или шаблон класса с публично доступной вложенной метафункцией apply. #include <boost/mpl/apply.hpp> namespace mpl= boost::mpl; struct add_pointer_f{ template <class T> struct apply: add_pointer<T> {}; }; template <class Func, class T> struct twice// twice(Func, T) == Func(Func(T)) : mpl::apply<Func, typename mpl::apply<Func, T>::type> {}; int **p = twice<add_pointer_f, int>::type(0);
Placeholder expression Def. Placeholderexpression –это либо заместитель, либо специализация шаблона класса, у которого хотя бы один аргумент placeholder expression. #include <boost/mpl/placeholders.hpp> using namespace mpl::placeholders; typedef twice<add_pointer_f, _1> add_pointer_twice; int **p = mpl::apply<add_pointer_twice, int>::type(0); Позволяет формировать новые метафункции, связывая с параметрами существующих метафункций некоторые значения Позволяет формировать новые метафункции как композицию существующих метафункций Превращение обычных шаблонов классов в placeholder expression typedef mpl::apply<std::vector<_>, T>::type vector_of_T; Легкая интеграция метапрограмм и существующих шаблонов
Лямбда-выражения Def.Лямбда-выражение – это метаданные, которые могут быть вызваны при помощи mpl::apply. Формы лямбда-выражения • Класс метафункции • Placeholder expression
Последовательности • Произвольного доступа • mpl::vector, mpl::deque, • Однонаправленные • mpl::list • Ассоциативные • mpl::map, mpl::set typedef mpl::vector<int, char, short, long, float, double> types; // найти положение long в types typedef mpl::find<types, long>::typelong_pos; // разыменовать итератор typedef mpl::deref<long_pos>::typex; // проверяем, что получили ожидаемый результат BOOST_STATIC_ASSERT((boost::is_same<x,long>::value));
empty_base A inherit_2<…, ….> B C inherit_2<…, ….> D inherit_2<…, ….> inherit_2<…, ….> X Алгоритм mpl::fold struct A{}; struct B{}; struct C{}; struct D{}; struct bases_for_X: mpl::vector<A,B,C,D> {}; template <class First, class Second> struct inherit_2: First, Second {}; struct X : mpl::fold< bases_for_X, mpl::empty_base, inherit_2<_, _> >::type {};
Задание цепочки действий struct empty_updator{ void update(double){} }; template <class Tail, class Head> struct inherit_and_define_update: Tail, Head { void update(double time) { Tail::update(time); Head::update(time); } }; struct X: mpl::fold<bases_for_X, empty_updator, inherit_and_define_update<_, _> >::type {}; Вызов X::update сведется к вызову A::update, B::update, C::update, D::update.
Views – адаптеры последовательностей • «Ленивые» • Неизменяемые struct bases_for_X : mpl::joint_view<mpl::vector<A,B>, mpl::vector<C,D> > {};// bases_for_X ~ mpl::vector<A,B,C,D> struct filtered_bases : mpl::filter_view<bases_for_X, boost::is_same<_, A> > {};// filtered_bases ~ mpl::vector<A> struct transformed: mpl::transform_view<bases_for_X, add_pointer<_> > {};// transformed ~ mpl::vector<A*,B*,C*,D*>
Curiously Recurring Template Pattern Def. Наследование класса X от специализации шаблона класса, принимающей аргументом класс X. struct X: base<X> { /*...*/ }; Доступ из базового класса к членам наследника осуществляется при помощи указателя this, приведенного к типу указателя на наследник. template <class Derived> struct BaseA { void method1() { self().baseB().method2(); } BaseA & baseA() { return *this; } private: Derived& self() { return static_cast<Derived&>(*this); } };
inherit_crtp struct MyComponent : BaseA<MyComponent> , BaseB<MyComponent, ParamForB> , BaseC<MyComponent> { /*...*/ }; При таком подходе список базовых классов (BaseA<…>, BaseB<…>, BaseC<…>)трудно использовать в качестве базовых для другой компоненты. struct bases : mpl::vector<BaseA<_>, BaseB<_,ParamForB>, BaseC<_> > {}; struct MyComponent : inherit_crtp<bases, MyComponent>::type {/*...*/};
«Вложенные» цепочки базовых классов struct damage_listeners: mpl::vector< HitPoints<_>, SmokeEmitter<_>, HitPrints<_> > {}; struct actions: mpl::vector< CheckValidPosition<_>, ProcessTargets<_>, UpdatePosition<_> > {}; struct bases: mpl::vector< BaseA<_>, OnDamage<_, damage_listeners>, OnUpdate<_, actions> > {};
Класс конфигурации Агрегирует параметры компоненты. Базовые классы для получения связанных типов обращаются в класс конфигурации компоненты. Классы конфигурации удобно наследовать друг от друга. Списки базовых классов объединяются, например, при помощи mpl::joint_view. Для того, чтобы в наследнике можно было переопределить некоторые метаданные, они должны быть представлены в виде лямбда-выражений, принимающих параметром класс конфигурации.
Пример: FSM (из Mpl Book) // concrete FSM implementation class player : public state_machine<player> { private: // the list of FSM states enum states { Empty, Open, Stopped, Playing, Paused , initial_state = Empty }; // transition actions void start_playback(play const&); void open_drawer(open_close const&); void close_drawer(open_ close const&); void store_cd_info(cd_detected const&); void stop_playback(stop const&); void pause_playback(pause const&); void resume_playback(play const&); void stop_and_open(open_close const&); // continued on the next page
Пример: FSM (продолжение) // transition actions friend class state_machine<player>; typedef player p; // makes transition table cleaner // transition table struct transition_table : mpl::vector11< // Start Event Next Action // +---------+-------------+---------+---------------------+ row < Stopped , play , Playing , &p::start_playback >, row < Stopped , open_close , Open , &p::open_drawer >, // +---------+-------------+---------+---------------------+ row < Open , open_close , Empty , &p::close_drawer >, // +---------+-------------+---------+---------------------+ row < Empty , open_close , Open , &p::open_drawer >, row < Empty , cd_detected , Stopped , &p::store_cd_info >, // +---------+-------------+---------+---------------------+ row < Playing , stop , Stopped , &p::stop_playback >, row < Playing , pause , Paused , &p::pause_playback >, row < Playing , open_close , Open , &p::stop_and_open >, // +---------+-------------+---------+---------------------+ row < Paused , play , Playing , &p::resume_playback >, row < Paused , stop , Stopped , &p::stop_playback >, row < Paused , open_close , Open , &p::stop_and_open > // +---------+-------------+---------+---------------------+ > {}; };
Использование реализованного State Pattern struct boat_cfg { struct states { // Идентификаторы состояний enum Id{ ACTIVE, HIDING, HIDDEN, APPEARING}; // События, инициирующие переходы // self().processAction(actions::Appear()); struct actions{ struct Appear{}; struct Hide{}; struct Leave{}; }; // базовые классы интерфейса абстрактного состояния struct bases: mpl::vector<update_base, collision_type_base> {}; // прокси-классы struct proxies: mpl::vector< proxy::PositionHolder<__>, proxy::MustHave <__>, proxy::ExplosionMaker<__> > {}; // интерфейс абстрактного состояния struct IState<__> base_type;
Использование реализованного State Pattern // описание состояния struct active { const static Id ID = ACTIVE; // метафункция, позволяющая описание состояния переопределять в //наследникахTHE_STATE_IS_OVERRIDABLE(active) template <class T> struct apply { typedef typename T:: states:: activetype; }; // базовые классы конкретного состояния struct bases: mpl::vector< CollisionTypeRobotGround<__>, Active<__> > {}; // класс конкретного состояния typedef State<__, active> state_type; };
Использование реализованного State Pattern struct hiding{ /*...*/ }; struct hidden{ /*...*/ }; struct appearing{ /*...*/ }; // начальное состояние typedef hiddeninitial; // таблица переходов struct Transitions: mpl::vector< fsm::row<hidden, actions::Appear, appearing>, fsm::row<appearing, actions::Leave, active>, fsm::row<active, actions::Hide, hiding>, fsm::row<hiding, actions::Leave, hidden> > {};
Диаграмма сгенерированного конкретного состояния
Возможности данной реализации паттерна • Независимость базовых классов, от того, являются ли они базовыми для класса состояния или основного объекта • Простая реализация вложенных состояний