Search code examples
c++booststate-machineboost-msm

BOOST::MSM Checking for state transitions in a guard condition


I have a generic guard condition which I would like to conditionally prevent transitions under certain circumstances. Below is a somewhat C++/Pseudocode of what I would like to do.

bool operator()(Event const& evt, FSM & fsm, SourceState& src, TargetState& tgt )
{
     bool transition = (current_state != next_state);
     bool transitionAllowed = (x | y | z); //some custom condition
     return (transition && transitionAllowed);
}

I would like to know if the target state is different from the source state and based on that determine if we can allow a transitions based on other parameters. I haven't found much success with documentation so far.


Solution

  • It seems that your question contains two parts. One is how to check the current state and the next state. The other is how to check custom conditions.

    In order to checking the current state and the next state, you can use std::is_same meta function.

    Here is an example of a guard:

    struct Guard1 {
        template <class Event, class Fsm, class Source, class Target>
        bool operator()(Event const&, Fsm& fsm, Source& src, Target&) const {
            bool transition = !std::is_same<Source, Target>::value;
            bool transitionAllowed = x() || fsm.y || src.z;
            return transition && transitionAllowed;
        }
    };
    

    Here is the transition table:

    // Transition table
    struct transition_table:mpl::vector<
        //          Start   Event   Next    Action      Guard
        //          source and target is the same
        msmf::Row < State1, Event1, State1, msmf::none, Guard1 >,
    
        //          source and target is different
        msmf::Row < State1, Event2, State2, msmf::none, Guard1 >
    > {};
    

    The transition caused by Event1 has the same source and target states. Both are State1. The transition caused by Event2 has the different source and target states. The source state is State1, and the target state is State2.

    The former case std::is_same<Source, Target>::value returns true, and the latter case returns false. The variable transition is the nagation of the result.

    You can use this as the part of return value.

    In order to evaluate custom condition, you need to get values for evaluation from some sources. I wrote three examples of sources.

    • x() is global function. Of course you can use global varibles like this.
    • y is a member variable of the state machine.
    • z is a member variable of the source state, State1.

    Here is the complete code:

    #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 msm = boost::msm;
    namespace msmf = boost::msm::front;
    namespace mpl = boost::mpl;
    
    // You can test changing the value
    bool const example_value = true;
    
    struct Event1 {};
    struct Event2 {};
    
    // example of a condition
    bool x() { return example_value; }
    
    struct Sm_:msmf::state_machine_def<Sm_>
    {
        // States
        struct State1:msmf::state<> {
            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;
            }
            bool z = example_value; // example of a condition
        };
        struct State2:msmf::state<> {
            template <class Event,class Fsm>
            void on_entry(Event const&, Fsm&) {
                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;
            }
            int property;
        };
        // Set initial state
        typedef State1 initial_state;
    
        // Guards
        struct Guard1 {
            template <class Event, class Fsm, class Source, class Target>
            bool operator()(Event const&, Fsm& fsm, Source& src, Target&) const {
                bool transition = !std::is_same<Source, Target>::value;
                bool transitionAllowed = x() || fsm.y || src.z;
                return transition && transitionAllowed;
            }
        };
    
        // Transition table
        struct transition_table:mpl::vector<
            //          Start   Event   Next    Action      Guard
            //          source and target is the same
            msmf::Row < State1, Event1, State1, msmf::none, Guard1 >,
    
            //          source and target is different
            msmf::Row < State1, Event2, State2, msmf::none, Guard1 >
        > {};
        bool y = example_value; // example of a condition
    };
    
    // Pick a back-end
    typedef msm::back::state_machine<Sm_> Sm;
    
    int main() {
        Sm sm;
        sm.start();
    
        std::cout << "> Send Event1()" << std::endl;
        sm.process_event(Event1());
    
        std::cout << "> Send Event2()" << std::endl;
        sm.process_event(Event2());
    }
    

    You can change the values of the custom conditions.

    // You can test changing the value
    bool const example_value = true;
    

    If you set example_value as false, the custom condition is not satisfied.

    If custom condition is NOT satisfied, both Event1 and Event2 don't make transition. If custom condition is satisfied, Event1 doesn't make a transition because the source and the target states are the same. Event2 makes a transition.

    Here is the running demo.

    The case example_value = true

    https://wandbox.org/permlink/6qHcW9e6JX4QXAuH

    The case example_value = false

    https://wandbox.org/permlink/HxaGpAr90YLEc5l8