Two Different Ways Of Implement Internal Transitions

There are two different ways to implement internal transitions. One is using the normal transition table in a state machine and setting the Next state to none. The advantage in this approach is to make it easier to glance through all transitions as they can be all described in same location.

The other is using the internal transition table, which is specialized in describing internal transitions only. This approach allows us to reuse internal transitions along with the reuse of states. Also it can evaluate internal transitions prior to normal transitions.

#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 Sm1_:msmf::state_machine_def<Sm1_>
    {
        struct State1_:msmf::state_machine_def<State1_>
        {
             // Guards
            struct InternalGuard1 {
                template <class Event, class Fsm, class SourceState, class TargetState>
                bool operator()(Event const&, Fsm&, SourceState&, TargetState&) const 
                {
                    std::cout << "Internal Transition Table's Guard1" << std::endl;
                    return false;
                }
            };
            struct InternalGuard2 {
                template <class Event, class Fsm, class SourceState, class TargetState>
                bool operator()(Event const&, Fsm&, SourceState&, TargetState&) const 
                {
                    std::cout << "Internal Transition Table's Guard2" << std::endl;
                    return false;
                }
            };
 
            // Internal Transition table
            struct internal_transition_table:mpl::vector<
                //               Event   Action      Guard
                msmf::Internal < Event1, msmf::none, InternalGuard1 >,
                msmf::Internal < Event1, msmf::none, InternalGuard2 >
            > {};        
        };
 
        // Set initial state
        typedef State1_ initial_state;
 
         // Guards
        struct Guard1 {
            template <class Event, class Fsm, class SourceState, class TargetState>
            bool operator()(Event const&, Fsm&, SourceState&, TargetState&) const 
            {
                std::cout << "Transition Table's Guard1" << std::endl;
                return false;
            }
        };
        struct Guard2 {
            template <class Event, class Fsm, class SourceState, class TargetState>
            bool operator()(Event const&, Fsm&, SourceState&, TargetState&) const 
            {
                std::cout << "Transition Table's Guard2" << std::endl;
                return false;
            }
        };
 
        // Transition table
        struct transition_table:mpl::vector<
            //          Start    Event   Next        Action      Guard
            msmf::Row < State1_, Event1, msmf::none, msmf::none, Guard1 >,
            msmf::Row < State1_, Event1, msmf::none, msmf::none, Guard2 >
        > {};
    };
 
    // back-end
    typedef msm::back::state_machine<Sm1_> Sm1;
 
    void test()
    {
        Sm1 sm1;
        sm1.start(); 
        std::cout << "> Send Event1" << std::endl;
        sm1.process_event(Event1());
    }
}
 
int main()
{
    test();
    return 0;
}
 
// Output:
//
// > Send Event1
// Internal Transition Table's Guard2
// Internal Transition Table's Guard1
// Transition Table's Guard2
// Transition Table's Guard1

Transition tables and internal transition tables are evaluated from the bottom row to the top row.

Add a New Comment