Exit Point Pseudo State

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:
fig15.pngfig16.png
by the exit pseudo state in the following diagrams:
fig17.pngfig18.png

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:
fig19.png
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
Add a New Comment