Search code examples
c++boostboost-statechart

Boost Statechart: Return to Previous State


I've got a state machine wherein if I enter a particular state, sometimes I need to have a regular transition to another state and other times I need to return to a previous state.

For example, having states A B C, say that transition S moves state A to C and from B to C. I need that a transition T moves C to A when S occurred in state A and C to B when it occurred in state B.

In the code below, transition S occurs in state B, and thus I would like for transition T to return to state B (whereas currently, it returns to state A).

#include <boost/mpl/list.hpp>

#include <boost/statechart/state_machine.hpp>
#include <boost/statechart/simple_state.hpp>
#include <boost/statechart/event.hpp>
#include <boost/statechart/transition.hpp>

// states
struct A;
struct B;
struct C;
struct D;

// events
struct S : boost::statechart::event<S> {};
struct T : boost::statechart::event<T> {};
struct U : boost::statechart::event<U> {};

// fsm
struct FSM : boost::statechart::state_machine<FSM, B> {};

// fully defined states/transitions
struct A : boost::statechart::simple_state<A, FSM> {
    typedef boost::statechart::transition<S, C> reactions;

    A() { std::cout << "entered A" << std::endl; }
};

struct B : boost::statechart::simple_state<B, FSM> {
    typedef boost::statechart::transition<S, C> reactions;

    B() { std::cout << "entered B" << std::endl; }
};

struct C : boost::statechart::simple_state<C, FSM> {
    typedef boost::mpl::list<
                boost::statechart::transition<T, A>,
                boost::statechart::transition<T, B>,
                boost::statechart::transition<U, D> > reactions;

    C() { std::cout << "entered C" << std::endl; }
};

struct D : boost::statechart::simple_state<D, FSM> {
    D() { std::cout << "entered D" << std::endl; } 
};

int main() {
    FSM fsm;

    fsm.initiate();

    fsm.process_event(S());
    fsm.process_event(T());
    fsm.process_event(S());
    fsm.process_event(U());

    return 0;
}

The above code returns:

entered B
entered C
entered A
entered C
entered D

and I would like to instead see:

entered B
entered C
entered B
entered C
entered D

Is there any clean way to do this using Boost::Statechart?


Solution

  • I've found an ~okay~ way of doing this by creating an enum mapping for states, storing the previous state in the outermost context (top-level fsm), and then using a custom reaction for the T event:

    #include <boost/mpl/list.hpp>
    
    #include <boost/statechart/state_machine.hpp>
    #include <boost/statechart/simple_state.hpp>
    #include <boost/statechart/event.hpp>
    #include <boost/statechart/transition.hpp>
    #include <boost/statechart/custom_reaction.hpp>
    
    // states
    struct A;
    struct B;
    struct C;
    struct D;
    
    // state enum mapping
    enum class state_mapping {
        A = 0,
        B,
        C,
        D
    };
    
    // events
    struct S : boost::statechart::event<S> {};
    struct T : boost::statechart::event<T> {};
    struct U : boost::statechart::event<U> {};
    
    // fsm
    struct FSM : boost::statechart::state_machine<FSM, B> {
        state_mapping previous_state = state_mapping::B;
    };
    
    // fully defined states/transitions
    struct A : boost::statechart::simple_state<A, FSM> {
        typedef boost::statechart::transition<S, C> reactions;
        
        A() { std::cout << "entered A" << std::endl; }
        virtual ~A() { outermost_context().previous_state = state_mapping::A; }
    };
    
    struct B : boost::statechart::simple_state<B, FSM> {
        typedef boost::statechart::transition<S, C> reactions;
        
        B() { std::cout << "entered B" << std::endl; }
        virtual ~B() { outermost_context().previous_state = state_mapping::B; }
    };
    
    struct C : boost::statechart::simple_state<C, FSM> {
        typedef boost::mpl::list<
                    boost::statechart::custom_reaction<T>,
                    boost::statechart::transition<U, D> > reactions;
                    
        C() { std::cout << "entered C" << std::endl; }
        
        boost::statechart::result react(const T&) {
            
            switch(outermost_context().previous_state) {
                
            case state_mapping::A:
                return transit<A>();
            case state_mapping::B:
                return  transit<B>();
            default:
                return discard_event();
                
            }
            
        }
    };
    
    struct D : boost::statechart::simple_state<D, FSM> {
        D() { std::cout << "entered D" << std::endl; }
    };
    
    int main() {
        FSM fsm;
        
        fsm.initiate();
        
        fsm.process_event(S());
        fsm.process_event(T());
        fsm.process_event(S());
        fsm.process_event(U());
    
        return 0;
    }