Entry Point Pseudo State

There are three ways to implement UML's entry point pseudo states.

1.Using msm::front::entry_pseudo_state, msm::front::entry_pt and boost::any.
fig22.png
2.Using msm::front::entry_pseudo_state, msm::front::entry_pt and outer event.
fig23.png
3.Using msm::front::explicit_entry, msm::front::direct and none.
fig24.png

I recommend the 1st approach because this approach maps the model into code directly. Boost.Msm supports boost::any as Event from version 1.51.0. If you can use this version or newer, using the 1st approach is the best. If you cannot use it, you can choose the 2nd way or the 3rd way based on the judgment criterion as follows:
If the sub-machine wants to know the trigger event of entry pseudo state, choose the 2nd way. Otherwise, choose the 3rd way. The 2nd way introduces the dependency that is from sub-machine to parent state machine. On the other hand the 3rd way doesn't introduce that.

These are implemantations correspond to the above diagram.

1.Using msm::front::entry_pseudo_state, msm::front::entry_pt and boost::any

#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 OuterEvent {};
    struct Event1 {};
 
    // ----- State machine
    struct OuterSm_:msmf::state_machine_def<OuterSm_>
    {
        struct State1:msmf::state<>
        {
            template <class Event,class Fsm>
            void on_entry(Event const&, Fsm&) const {
                std::cout << "State1::on_entry()" << std::endl;
            }
        };
        struct State2_:msmf::state_machine_def<State2_>
        {
            struct SubState1:msmf::state<> {
                template <class Event,class Fsm>
                void on_entry(Event const&, Fsm&) const {
                    std::cout << "SubState1::on_entry()" << std::endl;
                }
            };
            struct SubState2:msmf::state<> {
                template <class Event,class Fsm>
                void on_entry(Event const&, Fsm&) const {
                    std::cout << "SubState2::on_entry()" << std::endl;
                }
            };
            struct Entry1:msmf::entry_pseudo_state<> {}; // === entry_pseudo_state
            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 < Entry1,    boost::any, SubState2, msmf::none, msmf::none >, // === boost::any
                msmf::Row < SubState1, Event1,     Exit1,     msmf::none, msmf::none >,
                msmf::Row < SubState2, Event1,     Exit1,     msmf::none, msmf::none >
                > {};
        };
 
        typedef msm::back::state_machine<State2_> State2;
 
        // Set initial state
        typedef State1 initial_state;
        // Transition table
        struct transition_table:mpl::vector<
            //          Start   Event       Next               Action      Guard
            msmf::Row < State1, OuterEvent, State2::entry_pt                             // === entry_pt
                                            <State2_::Entry1>, msmf::none, msmf::none >
        > {};
    };
 
    // Pick a back-end
    typedef msm::back::state_machine<OuterSm_> Osm;
 
    void test()
    {        
        Osm osm;
        osm.start(); 
 
        std::cout << "> Send OuterEvent()" << std::endl;
        osm.process_event(OuterEvent());
    }
}
 
int main()
{
    test();
    return 0;
}

2.Using msm::front::entry_pseudo_state, msm::front::entry_pt and outer event

#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 OuterEvent {};
    struct Event1 {};
 
    // ----- State machine
    struct OuterSm_:msmf::state_machine_def<OuterSm_>
    {
        struct State1:msmf::state<>
        {
            template <class Event,class Fsm>
            void on_entry(Event const&, Fsm&) const {
                std::cout << "State1::on_entry()" << std::endl;
            }
        };
        struct State2_:msmf::state_machine_def<State2_>
        {
            struct SubState1:msmf::state<> {
                template <class Event,class Fsm>
                void on_entry(Event const&, Fsm&) const {
                    std::cout << "SubState1::on_entry()" << std::endl;
                }
            };
            struct SubState2:msmf::state<> {
                template <class Event,class Fsm>
                void on_entry(Event const&, Fsm&) const {
                    std::cout << "SubState2::on_entry()" << std::endl;
                }
            };
            struct Entry1:msmf::entry_pseudo_state<> {};  // === entry_pseudo_state 
            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 < Entry1,    OuterEvent, SubState2, msmf::none, msmf::none >, // === OuterEvent
                msmf::Row < SubState1, Event1,     Exit1,     msmf::none, msmf::none >,
                msmf::Row < SubState2, Event1,     Exit1,     msmf::none, msmf::none >
                > {};
        };
 
        typedef msm::back::state_machine<State2_> State2;
 
        // Set initial state
        typedef State1 initial_state;
        // Transition table
        struct transition_table:mpl::vector<
            //          Start   Event       Next               Action      Guard
            msmf::Row < State1, OuterEvent, State2::entry_pt                            // === entry_pt
                                            <State2_::Entry1>, msmf::none, msmf::none >
        > {};
    };
 
    // Pick a back-end
    typedef msm::back::state_machine<OuterSm_> Osm;
 
    void test()
    {        
        Osm osm;
        osm.start(); 
 
        std::cout << "> Send OuterEvent()" << std::endl;
        osm.process_event(OuterEvent());
    }
}
 
int main()
{
    test();
    return 0;
}

Entry1's incoming event has to be convertible to Entry1's outgoing event. In the case of this example, they are both OuterEvent.

        // Transition table
        struct transition_table:mpl::vector<
            //          Start   Event       Next               Action      Guard
            msmf::Row < State1, OuterEvent, State2::entry_pt                            // === entry_pt
                                            <State2_::Entry1>, msmf::none, msmf::none >
        > {};
            // Transition table
            struct transition_table:mpl::vector<
                //          Start      Event       Next       Action      Guard
                msmf::Row < Entry1,    OuterEvent, SubState2, msmf::none, msmf::none >, // === OuterEvent
                msmf::Row < SubState1, Event1,     Exit1,     msmf::none, msmf::none >,
                msmf::Row < SubState2, Event1,     Exit1,     msmf::none, msmf::none >
                > {};

Be careful about an important exception. msm::front::none can be converted from any events, however, you cannot use it as Entry1's outgoing event. It can be compiled, but behave unexpectedly.
            // Transition table
            struct transition_table:mpl::vector<
                //          Start      Event       Next       Action      Guard
                msmf::Row < Entry1,    msmf::none, SubState2, msmf::none, msmf::none >, // === NG none is prohibited
                msmf::Row < SubState1, Event1,     Exit1,     msmf::none, msmf::none >,
                msmf::Row < SubState2, Event1,     Exit1,     msmf::none, msmf::none >
                > {};

3.Using msm::front::explicit_entry, msm::front::direct and none

#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 OuterEvent {};
    struct Event1 {};
 
    // ----- State machine
    struct OuterSm_:msmf::state_machine_def<OuterSm_>
    {
        struct State1:msmf::state<>
        {
            template <class Event,class Fsm>
            void on_entry(Event const&, Fsm&) const {
                std::cout << "State1::on_entry()" << std::endl;
            }
        };
        struct State2_:msmf::state_machine_def<State2_>
        {
            struct SubState1:msmf::state<> {
                template <class Event,class Fsm>
                void on_entry(Event const&, Fsm&) const {
                    std::cout << "SubState1::on_entry()" << std::endl;
                }
            };
            struct SubState2:msmf::state<> {
                template <class Event,class Fsm>
                void on_entry(Event const&, Fsm&) const {
                    std::cout << "SubState2::on_entry()" << std::endl;
                }
            };
            struct Entry1:msmf::state<>, msmf::explicit_entry<> {}; // === explicit_entry
            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 < Entry1,    msmf::none, SubState2, msmf::none, msmf::none >, // === none
                msmf::Row < SubState1, Event1,     Exit1,     msmf::none, msmf::none >,
                msmf::Row < SubState2, Event1,     Exit1,     msmf::none, msmf::none >
                > {};
        };
 
        typedef msm::back::state_machine<State2_> State2;
 
        // Set initial state
        typedef State1 initial_state;
        // Transition table
        struct transition_table:mpl::vector<
            //          Start   Event       Next               Action      Guard
            msmf::Row < State1, OuterEvent, State2::direct                               // === direct
                                            <State2_::Entry1>, msmf::none, msmf::none >
        > {};
    };
 
    // Pick a back-end
    typedef msm::back::state_machine<OuterSm_> Osm;
 
    void test()
    {        
        Osm osm;
        osm.start(); 
 
        std::cout << "> Send OuterEvent()" << std::endl;
        osm.process_event(OuterEvent());
    }
}
 
int main()
{
    test();
    return 0;
}

You might think why initial_state typedef of SubState1 uses mpl::vector. It is a same meaning as the following code as far as the mpl::vector has one element.

        typedef SubState1 initial_state;

The reason of use the mpl::vector is a workaround.
See this discussion. http://permalink.gmane.org/gmane.comp.lib.boost.devel/228677
This bug will be fixed in Boost.1.50.0.
Add a New Comment