Sub Machine State

Composite state and sub-machine state

Boost.Msm supports composite state and sub-machine state. Consider the following diagrams. State1 is a composite state that has two sub states. They are simply mapped into the code.
fig26.png

Naming rule (example)

Before describing the code, I introduce the naming rule. Name_, any name with underscore, is a state's name that inherited state_machine_def class template.

        struct State1_:msmf::state_machine_def<State1_>

Name, any name without underscore, is a state's name that doesn't inherited it.
        struct State2:msmf::state<>

Name is also used Boost.Msm back-end templates that have the template argument Name_.
        typedef msm::back::state_machine<State1_> State1;

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>
#include <boost/static_assert.hpp>
 
namespace {
    namespace msm = boost::msm;
    namespace msmf = boost::msm::front;
    namespace mpl = boost::mpl;
 
    // ----- Events
    struct Event1 {};
    struct Event2 {};
    struct Event3 {};
 
    // ----- 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 {
                BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
                std::cout << "State1::on_entry()" << std::endl;
            }
            template <class Event,class Fsm>
            void on_exit(Event const&, Fsm&) const {
                BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
                std::cout << "State1::on_exit()" << std::endl;
            }
 
            struct SubState1:msmf::state<> {
                template <class Event,class Fsm>
                void on_entry(Event const&, Fsm&) const {
                    BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
                    std::cout << "SubState1::on_entry()" << std::endl;
                }
                template <class Event,class Fsm>
                void on_exit(Event const&, Fsm&) const {
                    BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
                    std::cout << "SubState1::on_exit()" << std::endl;
                }
            };
            struct SubState2:msmf::state<> {
                template <class Event,class Fsm>
                void on_entry(Event const&, Fsm&) const {
                    BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
                    std::cout << "SubState2::on_entry()" << std::endl;
                }
                template <class Event,class Fsm>
                void on_exit(Event const&, Fsm&) const {
                    BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, State1_>::value));
                    std::cout << "SubState2::on_exit()" << std::endl;
                }
            };
 
            // Set initial state
            typedef mpl::vector<SubState1> initial_state;
            // Transition table
            struct transition_table:mpl::vector<
                //          Start      Event   Next       Action      Guard
                msmf::Row < SubState1, Event2, SubState2, msmf::none, msmf::none >,
                msmf::Row < SubState2, Event3, SubState1, msmf::none, msmf::none >
                > {};
        };
        struct State2:msmf::state<>
        {
            template <class Event,class Fsm>
            void on_entry(Event const&, Fsm&) const {
                BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
                std::cout << "State2::on_entry()" << std::endl;
            }
            template <class Event,class Fsm>
            void on_exit(Event const&, Fsm&) const {
                BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
                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, Event1, 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 Event2()" << std::endl;
        osm.process_event(Event2());
        std::cout << "> Send Event1()" << std::endl;
        osm.process_event(Event1());
    }
}
 
int main()
{
    test();
    return 0;
}
// Output:
//
// State1::on_entry()
// SubState1::on_entry()
// > Send Event2()
// SubState1::on_exit()
// SubState2::on_entry()
// > Send Event1()
// SubState2::on_exit()
// State1::on_exit()
// State2::on_entry()

To implement composite states, inherits state_machine_def class template same as parent state machine.
        struct State1_:msmf::state_machine_def<State1_>

And then, connect to back-end state_machine class template.
        typedef msm::back::state_machine<State1_> State1;

In parent state machine's transition table, you can use back-end state name.
        // Transition table
        struct transition_table:mpl::vector<
            //          Start   Event   Next    Action      Guard
            msmf::Row < State1, Event1, State2, msmf::none, msmf::none >
        > {};

These are sub-machine state diagram examples. These diagrams have the same behavior as above diagram that uses composite state. Sub-machine states are more reusable than composite states. For example, you can reuse StateSub as State2's sub-machine state.
fig27.pngfig28.png
This code corresponds to above sub-machine state 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>
#include <boost/static_assert.hpp>
 
namespace {
    namespace msm = boost::msm;
    namespace msmf = boost::msm::front;
    namespace mpl = boost::mpl;
 
    // ----- Events
    struct Event1 {};
    struct Event2 {};
    struct Event3 {};
 
    // ----- State machine
    struct StateSub_:msmf::state_machine_def<StateSub_>
    {
        struct SubState1:msmf::state<> {
            template <class Event,class Fsm>
            void on_entry(Event const&, Fsm&) const {
                BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, StateSub_>::value));
                std::cout << "SubState1::on_entry()" << std::endl;
            }
            template <class Event,class Fsm>
            void on_exit(Event const&, Fsm&) const {
                BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, StateSub_>::value));
                std::cout << "SubState1::on_exit()" << std::endl;
            }
        };
        struct SubState2:msmf::state<> {
            template <class Event,class Fsm>
            void on_entry(Event const&, Fsm&) const {
                BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, StateSub_>::value));
                std::cout << "SubState2::on_entry()" << std::endl;
            }
            template <class Event,class Fsm>
            void on_exit(Event const&, Fsm&) const {
                BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, StateSub_>::value));
                std::cout << "SubState2::on_exit()" << std::endl;
            }
        };
 
        // Set initial state
        typedef mpl::vector<SubState1> initial_state;
        // Transition table
        struct transition_table:mpl::vector<
            //          Start      Event   Next       Action      Guard
            msmf::Row < SubState1, Event2, SubState2, msmf::none, msmf::none >,
            msmf::Row < SubState2, Event3, SubState1, msmf::none, msmf::none >
        > {};
    };
    typedef msm::back::state_machine<StateSub_> StateSub;
    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 {
                BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
                std::cout << "State1::on_entry()" << std::endl;
            }
            template <class Event,class Fsm>
            void on_exit(Event const&, Fsm&) const {
                BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
                std::cout << "State1::on_exit()" << std::endl;
            }
            struct Sub_:StateSub {};
            typedef Sub_ initial_state;
        };
        // Pick a back-end
        typedef msm::back::state_machine<State1_> State1;
        struct State2:msmf::state<>
        {
            template <class Event,class Fsm>
            void on_entry(Event const&, Fsm&) const {
                BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
                std::cout << "State2::on_entry()" << std::endl;
            }
            template <class Event,class Fsm>
            void on_exit(Event const&, Fsm&) const {
                BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
                std::cout << "State2::on_exit()" << std::endl;
            }
        };
        // Set initial state
        typedef State1 initial_state;
        // Transition table
        struct transition_table:mpl::vector<
            //          Start   Event   Next    Action      Guard
            msmf::Row < State1, Event1, 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 Event2()" << std::endl;
        osm.process_event(Event2());
        std::cout << "> Send Event1()" << std::endl;
        osm.process_event(Event1());
    }
}
 
int main()
{
    test();
    return 0;
}
 
// Output:
//
// State1::on_entry()
// SubState1::on_entry()
// > Send Event2()
// SubState1::on_exit()
// SubState2::on_entry()
// > Send Event1()
// SubState2::on_exit()
// State1::on_exit()
// State2::on_entry()

In the definition of State1_, embed StateSub as Sub_ using inheritance. And set it as an initial_state.

        struct State1_:msmf::state_machine_def<State1_>
        {
            template <class Event,class Fsm>
            void on_entry(Event const&, Fsm&) const {
                BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
                std::cout << "State1::on_entry()" << std::endl;
            }
            template <class Event,class Fsm>
            void on_exit(Event const&, Fsm&) const {
                BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
                std::cout << "State1::on_exit()" << std::endl;
            }
            struct Sub_:StateSub {};
            typedef Sub_ initial_state;
        };

Which fsm is accessible?

Event handlers can access a inner most Fsm that includes the state that has the event handler. Check BOOST_STATIC_ASSERT on above codes. Be careful that the Fsm of State1_::on_entry(Event const&, Fsm&) refers to OuterSm_, not State1_ because State1_ is included in OuterSm_.

Add a New Comment