C++学习 Boost状态机说明statechart

2024-01-05 10:58

本文主要是介绍C++学习 Boost状态机说明statechart,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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是一个状态机对象

这篇关于C++学习 Boost状态机说明statechart的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/572637

相关文章

深入理解C++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规

Redis分布式锁使用及说明

《Redis分布式锁使用及说明》本文总结了Redis和Zookeeper在高可用性和高一致性场景下的应用,并详细介绍了Redis的分布式锁实现方式,包括使用Lua脚本和续期机制,最后,提到了RedLo... 目录Redis分布式锁加锁方式怎么会解错锁?举个小案例吧解锁方式续期总结Redis分布式锁如果追求

结构体和联合体的区别及说明

《结构体和联合体的区别及说明》文章主要介绍了C语言中的结构体和联合体,结构体是一种自定义的复合数据类型,可以包含多个成员,每个成员可以是不同的数据类型,联合体是一种特殊的数据结构,可以在内存中共享同一... 目录结构体和联合体的区别1. 结构体(Struct)2. 联合体(Union)3. 联合体与结构体的

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo

关于SpringBoot的spring.factories文件详细说明

《关于SpringBoot的spring.factories文件详细说明》spring.factories文件是SpringBoot自动配置机制的核心部分之一,它位于每个SpringBoot自动配置模... 目录前言一、基本结构二、常见的键EnableAutoConfigurationAutoConfigu

C++11的函数包装器std::function使用示例

《C++11的函数包装器std::function使用示例》C++11引入的std::function是最常用的函数包装器,它可以存储任何可调用对象并提供统一的调用接口,以下是关于函数包装器的详细讲解... 目录一、std::function 的基本用法1. 基本语法二、如何使用 std::function

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

Zookeeper安装和配置说明

一、Zookeeper的搭建方式 Zookeeper安装方式有三种,单机模式和集群模式以及伪集群模式。 ■ 单机模式:Zookeeper只运行在一台服务器上,适合测试环境; ■ 伪集群模式:就是在一台物理机上运行多个Zookeeper 实例; ■ 集群模式:Zookeeper运行于一个集群上,适合生产环境,这个计算机集群被称为一个“集合体”(ensemble) Zookeeper通过复制来实现

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06