Boost.MSM: Exit orthogonal regions via a join pseudo state

回眸只為那壹抹淺笑 提交于 2019-12-05 10:51:41

You could use a counter which will increment each time the join state is entered. When this counter equals the number of orthogonal regions, the state following the join state will be activated.

This could be done manually or in a generic way. Below I implemented a generic way where the joining logic is added to the submachine Sub by inheriting from the template JoinSM.

Sub has 3 orthogonal regions (which in this simple example just consist of one state each, namely Orthogonal1, Orthogonal2 and Orthogonal3). All of these orthogonal states are connected to the Join state but no direct connection to the Exit state from the Join state is specified within Sub.

This connection is implemented in JoinSM. Every time the Join state is reached from Sub, the Waiting state is activated and the counter is incremented. If the counter reaches the number of orthogonal regions, the Event AllJoined is fired and the transition to Exit is activated.

Since JoinSM queries the number of orthogonal regions through the size of initial_state, adding or removing regions in Sub will automatically be reflected in the joining logic.

#include <iostream>
#include <cstdlib>
#include <memory>
#include <cxxabi.h>

template <class T>
std::string demangle()
{
    const char* name = typeid(T).name();
    int status = -1; 
    std::unique_ptr<char, void(*)(void*)> res {
        abi::__cxa_demangle(name, NULL, NULL, &status),
        std::free
    };
    return (status==0) ? res.get() : name ;
}


#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
#include <boost/msm/back/metafunctions.hpp>
#include <boost/mpl/assert.hpp>

using namespace boost::msm;
using namespace boost::msm::front;


template <typename State>
struct BaseState : public boost::msm::front::state<>
{
    template <class Event,class FSM> void on_entry(Event const&,FSM& )
    {
        std::cout << "on_entry: " << demangle<State>()  << std::endl;
    }
    template <class Event,class FSM> void on_exit(Event const&,FSM& ) 
    {
        std::cout << "on_exit: " << demangle<State>() << std::endl;
    }
};


// EVENTS
struct EnterOrthogonal {};

struct Orthogonal1Finished{};
struct Orthogonal2Finished{};
struct Orthogonal3Finished{};



struct SubSM_ : state_machine_def<SubSM_>
{
    struct Started : BaseState<Started>{};
    struct Exit : exit_pseudo_state<none> {};

    struct Orthogonal1 : BaseState<Orthogonal1>{};
    struct Orthogonal2 : BaseState<Orthogonal2>{};
    struct Orthogonal3 : BaseState<Orthogonal3>{};

    struct Join : BaseState<Join>{};

    typedef boost::mpl::vector<Orthogonal1, Orthogonal2, Orthogonal3> initial_state;
    struct transition_table : boost::mpl::vector<
     Row<Orthogonal1, Orthogonal1Finished, Join, none, none>,
     Row<Orthogonal2, Orthogonal2Finished, Join, none, none>,
     Row<Orthogonal3, Orthogonal3Finished, Join, none, none>
     > {};
};


template <typename SM, typename JoinState = typename SM::Join, typename ExitState = typename SM::Exit>
struct JoinSM  : SM
{
    struct AllJoined{};

    constexpr static int num_regions = boost::mpl::size<typename SM::initial_state>::value;
    int count;

    template <class Event,class FSM>
    void on_entry(Event const& ,FSM&) 
    {
        // reset count
        count = 0;
    }

    struct Waiting : BaseState<Waiting>
    {
        template <class Event,class FSM>
        void on_entry(const Event& e,FSM& f)
        {
            BaseState<Waiting>::on_entry(e,f);
            f.count++; 
            if (f.count == FSM::num_regions)
            {
                f.process_event(AllJoined()); 
            } 
        }
    };

    typedef boost::mpl::vector<
        Row<JoinState, none, Waiting, none, none>,
        Row<Waiting, AllJoined, ExitState, none, none>
    > additional_transition_table;

    typedef boost::mpl::joint_view<
        typename SM::transition_table,
        additional_transition_table
    > transition_table;
};

// inherit from JoinSM to add the joining logic
using Sub = back::state_machine<JoinSM<SubSM_>>;

struct MainSM_ : state_machine_def<MainSM_>
{
    struct Started : BaseState<Started>{};
    struct AfterJoin : BaseState<AfterJoin>{};
    using initial_state = boost::mpl::vector<Started>;
    struct transition_table : boost::mpl::vector<
        Row<Started, EnterOrthogonal, Sub, none, none>,
        Row<Sub::exit_pt<SubSM_::Exit>, none, AfterJoin, none, none>
    > {};
};

struct MainSM_;
using Main = back::state_machine<MainSM_>;    


int main()
{

    Main main;
    main.start();
    main.process_event(EnterOrthogonal());
    main.process_event(Orthogonal3Finished());
    main.process_event(Orthogonal1Finished());
    main.process_event(Orthogonal2Finished());
}

Output:

on_entry: MainSM_::Started
on_exit: MainSM_::Started
on_entry: SubSM_::Orthogonal1
on_entry: SubSM_::Orthogonal2
on_entry: SubSM_::Orthogonal3
on_exit: SubSM_::Orthogonal3
on_entry: SubSM_::Join
on_exit: SubSM_::Join
on_entry: JoinSM<SubSM_, SubSM_::Join, SubSM_::Exit>::Waiting
on_exit: SubSM_::Orthogonal1
on_entry: SubSM_::Join
on_exit: SubSM_::Join
on_entry: JoinSM<SubSM_, SubSM_::Join, SubSM_::Exit>::Waiting
on_exit: SubSM_::Orthogonal2
on_entry: SubSM_::Join
on_exit: SubSM_::Join
on_entry: JoinSM<SubSM_, SubSM_::Join, SubSM_::Exit>::Waiting
on_exit: JoinSM<SubSM_, SubSM_::Join, SubSM_::Exit>::Waiting
on_exit: JoinSM<SubSM_, SubSM_::Join, SubSM_::Exit>::Waiting
on_exit: JoinSM<SubSM_, SubSM_::Join, SubSM_::Exit>::Waiting
on_entry: MainSM_::AfterJoin

Live example: http://coliru.stacked-crooked.com/a/6c060d032bc53573

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!