
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