We sometimes want to exit from sub-machine state. Boost.Msm supports entry and exit pseudo states, but doesn't support final states in sub-machines. You can replace the final state in the following diagrams:
by the exit pseudo state in the following diagrams:
Let's see the code corresponds to the above diagrams.
#include <iostream> #include <boost/msm/back/state_machine.hpp> #include <boost/msm/front/state_machine_def.hpp> #include <boost/msm/front/functor_row.hpp> namespace { namespace msm = boost::msm; namespace msmf = boost::msm::front; namespace mpl = boost::mpl; // ----- Events struct Event1 {}; // ----- State machine struct OuterSm_:msmf::state_machine_def<OuterSm_> { struct State1_:msmf::state_machine_def<State1_> { template <class Event,class Fsm> void on_entry(Event const&, Fsm&) const { std::cout << "State1::on_entry()" << std::endl; } template <class Event,class Fsm> void on_exit(Event const&, Fsm&) const { std::cout << "State1::on_exit()" << std::endl; } struct SubState1:msmf::state<> { template <class Event,class Fsm> void on_entry(Event const&, Fsm&) const { std::cout << "SubState1::on_entry()" << std::endl; } template <class Event,class Fsm> void on_exit(Event const&, Fsm&) const { std::cout << "SubState1::on_exit()" << std::endl; } }; struct Exit1:msmf::exit_pseudo_state<msmf::none> {}; // Set initial state typedef mpl::vector<SubState1> initial_state; // Transition table struct transition_table:mpl::vector< // Start Event Next Action Guard msmf::Row < SubState1, Event1, Exit1, msmf::none, msmf::none > > {}; }; struct State2:msmf::state<> { template <class Event,class Fsm> void on_entry(Event const&, Fsm&) const { std::cout << "State2::on_entry()" << std::endl; } template <class Event,class Fsm> void on_exit(Event const&, Fsm&) const { std::cout << "State2::on_exit()" << std::endl; } }; typedef msm::back::state_machine<State1_> State1; // Set initial state typedef State1 initial_state; // Transition table struct transition_table:mpl::vector< // Start Event Next Action Guard msmf::Row < State1::exit_pt <State1_::Exit1>, msmf::none, State2, msmf::none, msmf::none > > {}; }; // Pick a back-end typedef msm::back::state_machine<OuterSm_> Osm; void test() { Osm osm; osm.start(); std::cout << "> Send Event1()" << std::endl; osm.process_event(Event1()); } } int main() { test(); return 0; } // Output: // // State1::on_entry() // SubState1::on_entry() // > Send Event1() // SubState1::on_exit() // State1::on_exit() // State2::on_entry()
To implement exit point pseudo states, define the class that inherits exit_pseudo_state.
struct Exit1:msmf::exit_pseudo_state<msmf::none> {};
In the sub-machine, you can use it same as the normal state.
// Transition table struct transition_table:mpl::vector< // Start Event Next Action Guard msmf::Row < SubState1, Event1, Exit1, msmf::none, msmf::none > > {};
In the parent state machine, you can use the sub-machine's exit point pseudo state as follows:
SBEN::exit_pt<SFEN::SEPN>
SBEN:Sub-machine's back-end name
SFEN:Sub-machine's front-end name
SEPN:Sub-machine's exit point pseudo state name.
// Transition table struct transition_table:mpl::vector< // Start Event Next Action Guard msmf::Row < State1::exit_pt <State1_::Exit1>, msmf::none, State2, msmf::none, msmf::none > > {};
Event conversion at exit_pseudo_state
We often want to know sub-machine's exit status. Within UML specification, in order to realize this, we separate the exit point pseudo state.
e.g.) ExitSuccess and ExitFailure
Boost.Msm provides more flexible method. The exit_pseudo_state class template has a template parameter. This is a converted event. The parent state machine can access the event. See the diagrams as follows:
The event Before is converted to the event After, and parent state machine can access the event After. This means that we can pass any information from sub-machine to parent state machine. Of course the event Before has to be convertible to the event After. Typically, we prepare a conversion constructor for the class After.
Here is the code corresponds to the above diagrams.
#include <iostream> #include <boost/msm/back/state_machine.hpp> #include <boost/msm/front/state_machine_def.hpp> #include <boost/msm/front/functor_row.hpp> namespace { namespace msm = boost::msm; namespace msmf = boost::msm::front; namespace mpl = boost::mpl; // ----- Events struct Before { Before(int param_):param(param_) {} int param; }; struct After { After(Before const& b):param(b.param) {} int param; }; // ----- State machine struct OuterSm_:msmf::state_machine_def<OuterSm_> { struct State1_:msmf::state_machine_def<State1_> { struct SubState1:msmf::state<> {}; struct Exit1:msmf::exit_pseudo_state<After> {}; // Set initial state typedef mpl::vector<SubState1> initial_state; // Transition table struct transition_table:mpl::vector< // Start Event Next Action Guard msmf::Row < SubState1, Before, Exit1, msmf::none, msmf::none > > {}; }; struct State2:msmf::state<> { template <class Event,class Fsm> void on_entry(Event const& e, Fsm&) const { std::cout << "State2::on_entry()" << std::endl; std::cout << "Event param = " << e.param << std::endl; } }; typedef msm::back::state_machine<State1_> State1; // Set initial state typedef State1 initial_state; // Transition table struct transition_table:mpl::vector< // Start Event Next Action Guard msmf::Row < State1::exit_pt <State1_::Exit1>, After, State2, msmf::none, msmf::none > > {}; }; // Pick a back-end typedef msm::back::state_machine<OuterSm_> Osm; void test() { Osm osm; osm.start(); std::cout << "> Send Before(42)" << std::endl; osm.process_event(Before(42)); } } int main() { test(); return 0; } // Output: // // > Send Before(42) // State2::on_entry() // Event param = 42