boost状态机说明

1. 状态

对象在其生命周期内,所处于的运动和发展阶段。例如:人可以处于工作中这个状态,也可以处于娱乐中状态。

状态应该有上下文环境。例如:人处于工作中状态,要属于职业生涯状态而不能是退休状态,也必须是活着的,而不能是死亡的状态。状态可以有子状态。例如:工作中状态,可以有开会中,会客中,被训斥中等子状态。

 

2. 状态机

由许多相关状态构成的集合。把一个对象,或者一个系统看做是一个状态机。

 

struct M : state_machine< M, A > {}; //这里定义了一个状态机类M。初始状态在A

 

struct A : simple_state<A, M>{}; //这里定义一个了状态A,属于M状态机。

 

struct A : simple_state<A, M, A1>{}; //这里定义一个了状态A,属于M状态机。A状态有初始子状态A1

 

其实可以状态机看做状态,把状态看做状态机,区别不是很大。

如果不考虑“历史状态”,当进入状态M后,会自动进入初始状态A,然后自动进入A1。如果A1有子状态A11,就会进入A11。

 

3. 状态本地存储

一般的,状态对象里不存放任何变量。我们把数据存放到状态机M里。这样做不方便。例如:我们工作相关的数据和娱乐相关的数据都放到“生存”这个状态机类里,首先就失去了数据封装隐藏的好处了。其次,每个状态或子状态只要能访问到状态机,就可读写任意其他状态需要的重要信息,容易导致类似“全局变量”带来的坏处。

利用context<>, state_cast<>等机制,我们可以容易的访问当前状态的“容器状态”和“激活的子状态”的引用,然后调用Set,Get方法去改变他们的数据值。

 

4.事件

状态机对外部或内部事件感兴趣,引起状态转移。例如:当人处于混混欲睡状态时,突然收到一个好消息事件,人就改变为兴奋状态了。

struct Evt: sc::event< Evt > {}; //事件类。当然可以加入自己想传达的任何信息。

struct Evt: sc::event< Evt > { int raised_salary; }; //可以加一些成员变量进去。

 

5. 状态转移

当状态感觉到一个事件发生时,可以对这个事件做出反应。状态转移就是一条“规则”,指明如何反应。例如:

当有人骂你这个事件发生时,你可以根据“恶意度”,骂人者与你的“亲密程度”,选择忽略这个事件,或者转移到“暴怒状态”,或者做出一定回击。

struct A1 : simple_state<A1, A> {

typedef transition< Evt, B  > reactions; }; //状态转移规则用typedef说明,注意reactions不能拼错。

 

typedef transition< Evt, B  > reactions; //当遇到事件Evt是,就转移到状态B。

状态转移能从一个状态跳转到任意的一个状态,即使状态是嵌套的深层子状态。

 D->C->B->A

           \

            InnerMostCommonOuter->Machime

           /  

    Z->Y->X 

假设D是最深层的嵌套子状态,可以从D跳转到Y。首先要跳出D(执行D的Exit Action),依次跳出C,B,A。

不会离开InnerMostCommonOuter状态,进入X(执行X的Entry Action),进入Y。

 

6. 监护条件

当一个事件发生时,根据一个条件表达式的bool值,决定此转换的目标。最好的监护条件实现是if语句或者swich语句。boost提供custom_reaction<>自定义转换机制,方便实现监护条件。

 

struct A : sc::simple_state<A, M> {

 

    typedef sc::custom_reaction< Evt > reactions;

 

    sc::result react( const Evt &evt )

    {

        //实现Transition Action

        cout<<"Transition Action Called"<<endl;

 

        //实现Guard Condition

        if (evt.m_raised_salary > 0){

            return transit< C >(); //跳转到状态C    

        }

        if (evt.m_raised_salary < 0){

            return transit< B >(); //跳转到状态B    

        }

        else{

            return discard_event(); //什么也不做

        }

    }

};

7. 转移动作Transition Action

同一个转移关联的动作。可以用custom_reaction<>机制实现。也可以用下述方法实现。

struct A : simple_state<A, M>{

typedef transition< Evt, B,  S, &S::transtion_action  > reactions;

其中,S要求至少是状态A,B的InnerMostCommonOuter,其Outer环境也可以,当然状态机M更可以。

struct S : sc::state_machine< S, 。。。 > {

    void transtion_action(const Evt &){ //transtion_action必须是这样的函数签名。

        std::cout<<"S::transtion_action() called"<<std::endl;

    }

};

 

8. 投递事件

最简单是process_event函数

S s;

s.initiate();

s.process_event(Evt()); 

事件首先被投递给最深层的当前子状态,让后向外层投递。

假设C->B->A,其中C是当前最深子状态,并且ABC三者都定义了对事件Evt的转移规则。

则C先获得事件Evt的处理。一般来说B,A将不能感知到事件。

C可以调用forward_event()将事件抛给B处理。这是当C不能处理一个事件X(未定义状态转移)时,默认行为(抛给直接上级状态处理)。

 

9. 进入/退出动作

struct A : simple_state<A, ...> {

    A(){

        std::cout<<"A构造函数被调用, Entry Action"<<std::endl;

    }

    ~A(){

        std::cout<<"~A析构函数被调用,Exit Action"<<std::endl;

    }

使用构造函数和析构函数实现了状态的进入,退出Action

 

10. 历史状态

struct A : simple_state<A, M, A1, sc::has_shallow_history > {

状态A有一个特殊的伪子状态---浅历史状态。其初始值可以认为是A1(等于初始子状态)。

当从A转移出后,能记住最后一个直接子状态例如:A9。

当从别处重新回到A状态时,能直接进入到A9。(而不是进入初始子状态。)

“浅”是指:只记住A的直接子状态;“深”是指:能记住A的最深的嵌套子状态。

 

struct X : simple_state<X, ...

typedef sc::transition<Evt, sc::shallow_history< A1 > > reactions;

回到A,进入历史状态

 

11. 多事件

 typedef boost::mpl::list<

        sc::transition< Evt1, B >

        sc::transition< Evt2, C >

    > reactions;

一个状态对多种事件的反应规则

 

12. 头文件

#include <boost/statechart/event.hpp>

#include <boost/statechart/state_machine.hpp>

#include <boost/statechart/simple_state.hpp>

#include <boost/statechart/transition.hpp>

#include <boost/statechart/custom_reaction.hpp>

#include <boost/statechart/deep_history.hpp>

#include <boost/statechart/shallow_history.hpp>

#include <boost/statechart/deferral.hpp>

 

13. 延迟事件

struct B : sc::simple_state<B, M> {

     typedef sc::deferral< EvtDefer > reactions;

}

处于B状态时,暂时不能处理EvtDefer,但是希望离开B状态时,才处理事件EvtDefer。

 延迟事件同post_event()不同之处在于,post_event能把事件放入状态机主事件队列,在action执行完成后,

就会立刻执行被post的事件(没有等待B结束,这个重要条件)

 

14. 状态机terminate()

terminate()会造成状态机离开当前活动状态,到达伪终态。

typedef boost::mpl::list<

     sc::transition< Evt2A,A >,

     sc::deferral< EvtDefer >,

     sc::termination< EvtKill >

  > reactions;

或者

sc::result A::react( const EvtKill & )

{

    return terminate();

}

 

15. sc::result

sc::result A::react( const Evt& )

{

    return discard_event(); //忽略事件

}

sc::result A::react( const Evt& )

{

    return forward_event(); //请求向上级状态处理此事件

}

sc::result A::react( const Evt& )

{

    return transit<B>(); //转移到B状态

}

sc::result A::react( const Evt& )

{

    return terminate(); //状态机自杀

}

 

16. 查询状态机是否在运行

bool lb = s.terminated(); //其中s是一个状态机对象

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值