boost::statechart研究报告

    基于 http://www.boost.org/doc/libs/1_59_0/libs/statechart/doc/tutorial.html#AsynchronousStateMachines 的文档说明html

    git地址和例子在:https://github.com/boostorg/statechartreact

     最近项目老大对游戏服务器的施法状态机进行了重构,借此机会研究了两天,并且中文文档比较少,整理一下本身备忘的同时也许可以给别人一点参考。我仅限于对同步状态机的实现作了一点验证性的试验,异步状态机的实如今example中有,用到的时候再研究,通常若是没有涉及到线程间交互的话同步状态机应该够用了。git

    statechart大概能够包含三块,状态机:state_machine,状态:state或simple_state以及事件 event。state_machine做为载体,state做为内容,event做为事件传输介质, 官方的helloword是一个最简单的初始化程序,能够看一下,下面介绍一些其余使用注意事项和扩展,以及罗列本身在写demo时候的一些问题。github

     关于定义须要注意的问题:数组

     1:如何定义一个state_machine,一个state和simple_state和如何定义一个event,看文档都能明白,注意自定义的状态继承state和simple_state有一点微小区别,继承  state 须要传入一个my_context做为上下文参数,若是你想在一个状态的构造函数中 使用如下函数族的话:服务器

 simple_state<>::post_event()
 simple_state<>::clear_shallow_history<>()
 simple_state<>::clear_deep_history<>()
 simple_state<>::outermost_context()
 simple_state<>::context<>()
 simple_state<>::state_cast<>()
 simple_state<>::state_downcast<>()
 simple_state<>::state_begin()
 simple_state<>::state_end()

 乖乖从state派生不然会报错,源码中有对应说明,以上函数组应该都会调用到下面的接口,所以在模块内部进行了限制,因此我以为是能够默认从state继承用的。异步

 from:simple_state.hpp

outermost_context_type & outermost_context() { // This assert fails when an attempt is made to access the state machine // from a constructor of a state that is *not* a subtype of state<>. // To correct this, derive from state<> instead of simple_state<>. BOOST_ASSERT( get_pointer( pContext_ ) != 0 ); return pContext_->outermost_context(); } const outermost_context_type & outermost_context() const { // This assert fails when an attempt is made to access the state machine // from a constructor of a state that is *not* a subtype of state<>. // To correct this, derive from state<> instead of simple_state<>. BOOST_ASSERT( get_pointer( pContext_ ) != 0 ); return pContext_->outermost_context(); }

  2: 还有须要注意的是状态定义的前后问题,基本上听从从外到里的方式,即 evnet, state_machine, state, sub_state(二层状态)..的方式就没什么问题。函数

  事件,行为及状态切换post

    事件是状态机中可能被触发的消息,当事件被触发的时候,若是不采起特殊措施,当前所处状态会首先接受到事件,若是注册了事件处理函数,则交由事件处理函数进行处理,不然将向外层抛出事件,直到找到对应的消息处理函数。异常传播也是相似的机制,若是本身这层没有捕捉到异常异常就会向外层扩散,只不过是若是连最外层的状态找不到对应的事件处理函数,这个消息就失效了,可是异常会一直向外传播直到本身的客户端程序。this

   行为,表示如何处理事件。有一种简单的方式是收到某个消息直接进行状态切换  相似于 sc::transition<EvStartStop, MyState_0>的方式,收到EvStartStop就跳转到MyState_0状态了,比较暴力直接;还有一种方式是使用react函数进行自定义处理,如sc::custom_reaction<EvBingo>,这时候在cpp中实现一个react,好比下面这样:

  //达成目标切换到初始
 sc::result MyState_2_1::react(const EvBingo& event)
 {
   //post_event(EvStartStop()); //容许从构造函数中发放消息
   std::cout << "恭喜你完成了状态机训练,让咱们从头开始!!" << std::endl;
   std::cout << context<MyStateMachine>().rtStr() << std::endl;
   return discard_event();
 }
 

  若是是自定义了react消息,表示当前状态接受并处理了EvBingo消息,他有权抛弃事件(discard_event),抛出其余消息可是会延迟到本函数执行完毕后抛出(post_event(xxx)),当即抛出消息(process_event(xxx)),继续向上层状态抛出同一事件(forward_event),或者直接跳转 (transit),可是要注意的是,若是使用的是transit或者process_event这种可能致使状态即时切换的函数时,最好在以后的流程中不要对当前state进行操做了,由于它至关于你delete了一个类对象可是还在继续使用对象的数据,这个操做是至关危险的。因此 transit和process_event通常是放在 react函数流程的最后执行的。

   上面介绍的几个函数包含了行为切换的一部分,既能够在当前状态内自由的抛弃,传播(新)事件,甚至直接作状态切换等,这里顺便提一句,还能够吧事件定义为defferal,如 sc::deferral< EvBingo >,那这个事件也会被延迟抛出,只不过它延迟的时间点更靠后,上面说到  post_event(xxx)和forward_event可能会等到react函数执行完毕以后再进行事件触发,可是deferral保证只有当前状态exit以后才分发这个事件。也就是说若是没有出当前状态这个事件将被永久积压!

    以上几种行为都是在某种状态中进行的,实际上不管处在react函数中,仍是状态的析构函数中,都还没脱离当前状态,那么如何在状态切换的中间作一点事情,即当前状态机不处于任何状态,这就用到了 transaction function,他在本状态切换出去以后而且还没有进入下一状态以前被调用

sc::transition<EvRtToLast, sc::deep_history<MyState_2_0>, MyStateMachine, &MyStateMachine::onEvStartState1>, //中间状态

 在EvRtToLast事件被分发后,状态机脱离了本状态准备切换到下一状态,此时会在中间调用 MyStateMachine::onEvStartState1函数。

 历史状态回溯 History

    假如咱们有这样一个模型,状态1是单一状态记为S1,状态2是多层状态记为S2,里面包含n个子状态,S20,S21...S2n, 那么假设在第一次从S1切换到S2的时候,在S2内部进行了一顿鼓捣又回到了S1,此时S1但愿再次切换到S2时可以自动切换到S2内部的最近一次的历史状态。按照官方的例子来讲就是你用照相机的时候,半松开按键的时候并不但愿照相机老是回到空闲状态,而是但愿回到进入拍照状态以前界面。这时候须要用到历史回溯功能,个人例子由于是随便敲的比较粗糙,将就配合解释一下:

      

 struct MyState_1 : public sc::state< MyState_1, MyStateMachine>
 {
   public:
     MyState_1(my_context ctx);
     virtual ~MyState_1();
 
     //状态列表
     typedef boost::mpl::list<
       ....
       sc::transition< EvStartState2, sc::deep_history<MyState_2_1> >
         > reactions;
     ....
 };



struct MyState_2 : public sc::state< MyState_2, MyStateMachine, MyState_2_0, sc::has_deep_history >
{
  public:
    MyState_2(my_context ctx);
    virtual ~MyState_2();
    ....
};

 struct MyState_2_0 : public sc::state<MyState_2_0, MyState_2> {...}
 struct MyState_2_1 : public sc::state<MyState_2_1, MyState_2> {...}

  int main()
{
 sm.process_event(EvStartState2()); // 第一次进入state2,进入history默认的状态 MyState_2_1
 sm.process_event(EvStartState2_0()); //回到 MyState_2_0
 sm.process_event(EvStartState1());    //回到 MyState_1
 sm.process_event(EvStartState2());    //第二次进入state2,将进入MyState_2_0,由于历史列表上MyState_2_0在最顶上
return 0;
}

   正交状态机

   正交状态机的意思是可能有多个初始状态,可是这些状态链是互不联系的,既不能从一个正交状态切换到另外一个正交状态,官方的例子已经很详尽了, 见 http://www.boost.org/doc/libs/1_59_0/libs/statechart/doc/tutorial.html#AsynchronousStateMachines 中 Orthogonal states 部分。

  异步状态机  没有细研究,暂不作记录

相关文章
相关标签/搜索