Boost logo

Boost Users :

Subject: Re: [Boost-users] [msm] access to root fsm
From: Christophe Henry (christophe.j.henry_at_[hidden])
Date: 2013-07-23 01:44:28


> Finally, I took the second way.
>
>>
>> As both still form a cycle, better would be a pointer to an interface,
>> or even better, to a type_erasure type representing the minimum needed
>> interface of the outer fsm. A boost::function would do too. I personally
>> use type_erasure.
>
> in init_fsm struct I already have generic fsm type, how can type_erasure
> fit here?
>
> test code:
>
> #include <iostream>
> #include <assert.h>
>
> #include <boost/msm/back/state_machine.hpp>
> #include <boost/msm/front/state_machine_def.hpp>
> #include <boost/msm/front/functor_row.hpp>
>
> using boost::msm::front::Row;
>
> struct init {};
> struct level1 {};
> struct level2 {};
>
> struct Level2_: public boost::msm::front::state_machine_def<Level2_>
> {
> struct Empty : public boost::msm::front::state<> {};
>
> typedef boost::mpl::vector<Empty> initial_state;
>
> struct transition_table : public boost::mpl::vector<> {};
>
> int i_; // some data
> };
>
> typedef boost::msm::back::state_machine<Level2_> Level2;
>
> struct Level1_: public boost::msm::front::state_machine_def<Level1_>
> {
> struct Empty : public boost::msm::front::state<> {};
>
> typedef boost::mpl::vector<Empty> initial_state;
>
> struct transition_table : public boost::mpl::vector<
> Row < Empty, level2, Level2 >
> > {};
>
> int i_;
> };
>
> typedef boost::msm::back::state_machine<Level1_> Level1;
>
> struct Root_: public boost::msm::front::state_machine_def<Root_>
> {
> struct Empty : public boost::msm::front::state<> {};
>
> struct Init : public boost::msm::front::state<> {};
>
> struct InitAction
> {
> template <typename RootT>
> struct init_fsm
> {
> init_fsm(RootT* root) : root_(root)
> { }
>
> // sub-FSM
> template <typename T>
> typename boost::enable_if<typename
> boost::msm::back::is_composite_state<T>::type, void>::type
> operator()(boost::msm::wrap<T>&) const
> {
> typedef typename T::stt stt;
> typedef typename boost::msm::back::generate_state_set<stt>::type
> all_states;
>
> auto& fsm(root_->get_state<T&>()); fsm.i_ = root_->i_ + 1;
>
> std::cout << typeid(T).name() << std::endl;
>
> boost::mpl::for_each<all_states,
> boost::msm::wrap<boost::mpl::placeholders::_1> >
> (init_fsm<T>(&fsm));
> }
>
> // normal states
> template <typename T>
> typename boost::disable_if<typename
> boost::msm::back::is_composite_state<T>::type, void>::type
> operator()(boost::msm::wrap<T>&) const
> { }
>
> protected:
> RootT* root_;
> };
>
> template <typename EVT, typename FSM, typename SourceState, typename
> TargetState>
> void operator()(EVT&, FSM& fsm, SourceState&, TargetState&) const
> {
> typedef typename FSM::stt stt;
> typedef typename boost::msm::back::generate_state_set<stt>::type
> all_states;
>
> boost::mpl::for_each<all_states,
> boost::msm::wrap<boost::mpl::placeholders::_1> >
> (init_fsm<FSM>(&fsm));
> }
> };
>
> typedef boost::mpl::vector<Empty> initial_state;
>
> struct transition_table : public boost::mpl::vector<
> Row < Empty, init, Init, InitAction >,
> Row < Init, level1, Level1 >
> > {};
>
> int i_; // some data
> };
>
> typedef boost::msm::back::state_machine<Root_> fsm_t;
>
> int main()
> {
> fsm_t fsm;
> fsm.i_ = 0;
>
> fsm.start();
> fsm.process_event(init());
> fsm.process_event(level1());
> fsm.process_event(level2());
>
> assert(fsm.i_ == 0);
> assert(fsm.get_state<Level1&>().i_ == 1);
> assert(fsm.get_state<Level1&>().get_state<Level2&>().i_ == 2);
>
> return 0;
> };
>
>

There is nothing wrong with your code (on the contrary), you forward data
top-to-down. It works well as long as you just need to have the outer give
data to the inner. Fun starts when the inner needs more, for example call
some member or call process_event on the outer. This forces you to either
have a cycle outer->inner->outer :( or use some type erasing mechanism, the
simplest one being a boost::function, so that the inner does not know its
outer.
But when you need more, for example process_event of different events, it
becomes annoying. When this happens, a possibility is to use type_erasure,
define a concept map for process_event with all required events, something
like:

struct any_outer_concept :
 ::boost::mpl::vector<
      has_process_event<HandledEnum(event1 const&),
boost::type_erasure::_self>,
...
> {};
typedef boost::type_erasure::any<any_outer_concept> any_outer;

and add there whatever the inner needs from the outer.
Then in your InitAction, you "convert" the root_ to your any_outer_concept
and you break your cycle.
Ok you could make it more elegant, like templatize the concept map on the
possible events too but this is the simple version.

HTH,
Christophe


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net