[msm] access to root fsm

I have main fsm with two sub-levels. The problem is, the sub-fsms need data which is held in root fsm, like: struct SomeAction { template <typename EVT, typename FSM, typename SourceState, typename TargetState> void operator()(EVT&, FSM& fsm, SourceState&, TargetState&) const { // ... } }; Instance of fsm here might be root fsm as well as sub-fsm. I see two possible solutions: 1. use sm_ptr policy and store parent pointer in each sub-fsm In this case access might look like this: fsm.parent_->data; (access from 1-level fsm) fsm.parent_->parent_->data; (access from 2-level fsm) doesn't look so nice, besides, documentation states that using sm_ptr is deprecated. 2. recursively traverse all states, detect fsm's and store root pointer typedef RootFSM::stt stt; typedef boost::msm::back::generate_state_set<stt>::type all_states; boost::mpl::for_each<all_states, boost::msm::wrap<mpl::placeholders::_1> > (find_sub_fsm<stt>()); template <typename stt> struct find_sub_fsm { template <class StateType> void operator()(boost::msm::wrap<StateType>& state) const { // ??? // state.root_ = &g_root; // do the recurs } }; How to detect that StateType is sub-fsm? 3. Maybe, there is a easier way to communicate between sub-fsm's?

Hi,
I have main fsm with two sub-levels. The problem is, the sub-fsms need data which is held in root fsm, like:
Ah yes, a classical one, I really need to take care of this. I intend to provide something variadic but I had no time to do it yet :(
struct SomeAction { template <typename EVT, typename FSM, typename SourceState, typename TargetState> void operator()(EVT&, FSM& fsm, SourceState&, TargetState&) const { // ... } };
Instance of fsm here might be root fsm as well as sub-fsm. I see two possible solutions:
1. use sm_ptr policy and store parent pointer in each sub-fsm In this case access might look like this:
fsm.parent_->data; (access from 1-level fsm) fsm.parent_->parent_->data; (access from 2-level fsm)
doesn't look so nice, besides, documentation states that using sm_ptr is deprecated.
It's deprecated because it was a bad solution, and an ugly one too.
2. recursively traverse all states, detect fsm's and store root pointer
typedef RootFSM::stt stt; typedef boost::msm::back::generate_state_set<stt>::type all_states;
boost::mpl::for_each<all_states, boost::msm::wrap<mpl::placeholders::_1> > (find_sub_fsm<stt>());
template <typename stt> struct find_sub_fsm { template <class StateType> void operator()(boost::msm::wrap<StateType>& state) const { // ??? // state.root_ = &g_root; // do the recurs } };
How to detect that StateType is sub-fsm?
You have is_composite_state<StateType>::type but it's still quite a bit of metaprogramming with some enable_if. Something like (untested) template <class StateType> typename ::boost::enable_if<typename is_composite_state<T>::type,void>::type void operator()(boost::msm::wrap<StateType>& state) const // submachines template <class StateType> typename ::boost::disable_if<typename is_composite_state<T>::type,void>::type void operator()(boost::msm::wrap<StateType>& state) const // normal states
3. Maybe, there is a easier way to communicate between sub-fsm's?
The easiest would be eiher: - to call outer.get_state<SubFsm&>().data=... - pass in the constructor of SubFsm a pointer to the outer. 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. HTH, Christophe

On 16.07.2013 0:09, Christophe Henry wrote: Hi,
Hi,
I have main fsm with two sub-levels. The problem is, the sub-fsms need data which is held in root fsm, like:
Ah yes, a classical one, I really need to take care of this. I intend to provide something variadic but I had no time to do it yet :(
...
3. Maybe, there is a easier way to communicate between sub-fsm's?
The easiest would be eiher: - to call outer.get_state<SubFsm&>().data=... - pass in the constructor of SubFsm a pointer to the outer.
Indeed it would, but I'm too lazy to manually iterate all sub-fsm's to do the same thing... 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; };
HTH, Christophe

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
participants (3)
-
Alexander Mingalev
-
Christophe Henry
-
Sam Fisher