[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
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
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
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
3. Maybe, there is a easier way to communicate between sub-fsm's?
The easiest would be eiher:
- to call outer.get_state
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
().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
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
#include
#include #include using boost::msm::front::Row;
struct init {}; struct level1 {}; struct level2 {};
struct Level2_: public boost::msm::front::state_machine_def
{ 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; struct Level1_: public boost::msm::front::state_machine_def
{ 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; struct Root_: public boost::msm::front::state_machine_def
{ 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
::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
()); fsm.i_ = root_->i_ + 1; std::cout << typeid(T).name() << std::endl;
boost::mpl::for_each
boost::mpl::placeholders::_1 > (init_fsm<T>(&fsm)); } // normal states template <typename T> typename boost::disable_if
::type operator()(boost::msm::wrap<T>&) const { } protected: RootT* root_; };
template
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
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
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
().i_ == 1); assert(fsm.get_state ().get_state ().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
{}; typedef boost::type_erasure::any
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