在上一文中,我们介绍了该状态机模型的使用方法。通过例子,我们发现可以使用该模型快速构建满足基本业务需求的状态机。本文我们将解析该模型的基础代码,以便大家可以根据自己状态机特点进行修改。(转载请指明出于breaksoftware的csdn博客)

该模板库的基础方法实现在之后给出的工程的AutoStateChart.h中,该文件一共215行,其中有16行是辅助调试代码。以上一文中状态机类为例:

class CMachine_Download_Run_App :public AutoStateChart::CAutoStateChartMachine<CMachine_Download_Run_App, CStoreofMachine>

CMachine_Download_Run_App类继承于模板类CAutoStateChartMachine,该模板类有两个参数:继承类自身和CStoreofMachine。CStoreofMachine类顾名思义,其是状态机中用于存储数据的类。为什么要设计这样的类?因为在我们的状态机模型中,每个基础状态都是割裂的。这样设计的一个好处便是我们可以让每个基础状态的逻辑代码独立,和其他模块没有任何耦合。当我们要删除某个状态时,我们只要将它从状态机的跳转声明中摘除即可。当我们要新增某个状态时,我们也只要在状态机跳转中做相应声明即可。但是往往优点也伴随着缺点:它使得每个基础状态类的数据交互产生了障碍。特别是没有上下文关系的基础状态,跳跃性的传递信息将变得非常困难。于是我们就需要一个存活于整个状态机声明周期的“数据库”,它可以被每个基础状态类访问和修改。于是CStoreofMachine就应运而生。因为该类比较独立,所以我们先从该类开始解析。首先我们看下该类的声明:

#pragma once
#include "AutoStateChart.h"#define PROPERTY(type,name)                                                    \
public:                                                                     \void Set##name(const type& n) {                                            \m_##name = n;                                                     \}                                                                      \type Get##name() {                                                     \return m_##name;                                                   \}                                                                      \__declspec(property(get = Get##name, put = Set##name)) type Prop##name;  \
private:                                                                    \type m_##name;                                                         \class CStoreofMachine{PROPERTY(std::string, ValueString);PROPERTY(std::wstring, ValueWString);PROPERTY(int, ValueInt);
};

该类的写法可能只适合于windows的vs平台,其他平台没论证过。其实它的内容是非常简单的,就是暴露成员变量的set和get方法。只是我觉得这种写法比较有意思,才在这儿罗列下。
        我们再看下该类在模板中的使用,我们先从最基础的类开始解析

 class CEmpytLocalStore{};template<class Store = CEmpytLocalStore>class CLocalStoreAccess{public:typedef boost::function< Store& () > func;Store& GetStore(){return m_pFunc();};void SetStore(func& pCallback){m_pFunc = pCallback;};public:func m_pFunc;};

我们先定义了一个空类——CEmptyLocalStore,它相当于一个默认的“数据库”。当模板的使用者不需要“数据库”时,就可以在模板中不声明“数据库”类,此时我们的CEmptyLocalStore就生效了。比如我们上例的状态机可以改成:

class CMachine_Download_Run_App :public AutoStateChart::CAutoStateChartMachine<CMachine_Download_Run_App>

CLocalStoreAccess类主要提供如下作用:

  1. 设置访问“数据库”类对象的方法——SetStore
  2. 获取“数据库”类对象——GetStore

成员变量m_pFunc是一个函数指针,用于获取“数据库”类对象。该变量将由CLoaclStoreAccess继承类设置,相当于CLocalStoreAccess暴露了设置访问“数据库”类对象的能力。而它并不保存“数据库”类对象——它只提供“访问”能力,而不提供“存储”能力。

 template<class Store = CEmpytLocalStore>class CLocalStoreBase:public boost::enable_shared_from_this<CLocalStoreBase<Store>>,public CLocalStoreAccess<Store> {public:void Init(){ func pfunc = boost::bind(&CLocalStoreBase<Store>::_GetStore, shared_from_this()); SetStore(pfunc);};private:Store& _GetStore(){return m_Store;};private:Store m_Store;};

CLoaclStoreBase类的私有成员变量m_Store就是“数据库”类对象,即该类提供了“存储”功能。它继承于CLoaclStoreAccess类,使得该类具备了访问数据库的能力——虽然它的私有方法可以访问“数据库”类对象,但是我还是希望将这些能力分开。因为之后介绍的基础状态类要有“访问”的能力,而不应该具备“存储”的能力。如果不将这些能力进行拆分,将会导致层次结构混乱。
        CLoaclStoreBase类的init方法,打通了和ClocalStoreAccess的关系——设置函数指针。
        介绍完用于存储上下文的模板类后,我们现在可以关注下状态机相关的类了。我们先看上一文中一个基础状态类的例子

class CSimpleState_Download_From_A :public AutoStateChart::CAutoStateChartBase<CSimpleState_Download_From_A, CMachine_Download_Run_App, CStoreofMachine>

CSimpleState_Download_From_A类继承于CAutoStateChartBase模板类。第一个模板参数是继承类自身,第二个是它所属的状态机,第三个是“数据库”类。我们在看下CAutoStateChartBase类的声明

 template<class T, class MachineOrCompositeStates, class Store = CEmpytLocalStore>class CAutoStateChartBase:public boost::enable_shared_from_this<CAutoStateChartBase<T,MachineOrCompositeStates,Store>>,public CLocalStoreAccess<Store>{BOOST_TYPEOF_REGISTER_TYPE(T)public:std::string GetCurrentState(){ return typeid(T).name();};bool IsCompositeStates(){return false;};void SetInitState( const std::string& strState ){};public:virtual void Entry(){}; virtual std::string Exit(){return "";};};

该模板类使用第一个模板参数类的类名作为其继承类的状态,并使用GetCurrentState方法提供获取功能。比如上例中的状态名为class CSimpleState_Download_From_A。这个模板类继承于CLocalStoreAccess模板类,使得继承类具有可以“访问”第三个模板参数类——“数据库”类的能力——不具备“存储”能力。同时该类还暴露了两个方法——Entry和Exit,他们分别用于在进出该状态时,让状态机调用。
        状态和存储类都介绍完了,我们就剩下调度状态变化的状态机类和复合状态类。其实从某种程度上说,复合状态是一种简单的状态机,它们在很多地方存在共性。我们从状态机类入口,进行讲解。首先看下上一文中的例子

class CMachine_Download_Run_App :public AutoStateChart::CAutoStateChartMachine<CMachine_Download_Run_App, CStoreofMachine>

状态机类需要继承于CAutoStateChartMachine模板类,该类声明如下:

 template<class T, class LocalStore = CEmpytLocalStore>class CAutoStateChartMachine:public boost::enable_shared_from_this<CAutoStateChartMachine<T,LocalStore>>,public CLocalStoreAccess<LocalStore>{public:typedef LocalStore SelfStore;typedef T Self;public:CAutoStateChartMachine(){m_spStore.reset();};virtual ~CAutoStateChartMachine(){};private:virtual bool Transition(){return false;};public:void StartMachine() {if ( !m_spStore ) {m_spStore = boost::make_shared<CLocalStoreBase<LocalStore>>();m_spStore->Init();SetStore( m_spStore->m_pFunc );}while( Transition()){};};private:void Init(){};public:bool IsCompositeStates(){return false;};protected:std::string m_strCurrentState;std::string m_strCondition;MapString m_MapCompositeStatesSubState;boost::shared_ptr<CLocalStoreBase<LocalStore>> m_spStore;};

我们先看下这个类的成员变量。m_strCurrentState保存了状态机在跳转中的当前状态,m_strCondition保存了状态机中当前状态之前的状态的输出,它用于决定状态跳转方向。m_MapCompositeStatesSubState用于保存状态机中离开复合状态时的最后状态,即它记录复合状态机的浅历史。m_spStore指向“数据库”类对象。
        该模板类最重要的函数就是StartMachine,它在第一次被调用时创建了“数据库”类对象。然后死循环调用Transition方法。Tansition方法是个虚方法,它是整个模型的核心。状态机类需要实现自己的Transition方法,以使得状态机可以运转起来。

我们再看下复合状态类的基础模板

 template<class T, class MachineOrCompositeStates, class LocalStore = CEmpytLocalStore>class CCompositeStates:public CAutoStateChartBase<T,MachineOrCompositeStates,LocalStore>{BOOST_TYPEOF_REGISTER_TYPE(T)public:CCompositeStates(){};~CCompositeStates(){};private:virtual bool Transition(){return false;};public:virtual void Entry(){while(Transition());};virtual std::string Exit(){return m_strCondition;};public:std::string GetCurrentState(){return m_strCurrentState;};bool IsCompositeStates(){return true;};void SetInitState( const std::string& strState ){ m_strCurrentState = strState; };protected:std::string m_strCurrentState;std::string m_strCondition;MapString m_MapCompositeStatesSubState;};

因为复合状态也是一种状态,所以它也要有Entry和Exit两种方法。而其Entry方法就是调用Transition方法。该模板类的Transition方法也是虚方法,这意味着继承于该模板类的方法也要去实现Transition。
        于是所有的重心都集中于Transition方法的实现。
        为了让代码美观,我参考了MFC中使用宏简洁代码的思路,设计了如下的宏:

#define STARTSTATE(state)                                                        \do {                                                                       \boost::shared_ptr<state> sp = boost::make_shared<state>();                \sp->SetStore( m_pFunc );                                                \if ( sp->IsCompositeStates() ) {                                        \std::string strState = typeid(state).name();                      \BOOST_AUTO(it, m_MapCompositeStatesSubState.find(strState));       \if ( m_MapCompositeStatesSubState.end() != it ) {                 \sp->SetInitState(it->second);                                    \if ( DEBUGFRAMEFLAG ) {                                            \std::string strInitState = it->second;                     \std::cout<<"CompositeStates SetInitState:"<<strInitState<<std::endl;\}                                                             \}                                                                  \}                                                                      \sp->Entry();                                                            \m_strCondition = sp->Exit();                                           \if ( sp->IsCompositeStates() ) {                                        \std::string strState = typeid(state).name();                      \std::string strInnerState = sp->GetCurrentState();                 \m_MapCompositeStatesSubState[strState] = strInnerState;               \if ( DEBUGFRAMEFLAG ) {                                                \std::cout<<"CompositeStates SaveState:"<<strState<< " " << strInnerState<< std::endl;    \}                                                                  \}                                                                      \} while (0);                                                               \#define    REGISTERSTATECONVERTBEGIN(startstate)                                   \bool Transition() {                                                            \do {                                                                   \if ( m_strCurrentState.empty() ) {                                 \m_strCurrentState = typeid(startstate).name();                    \STARTSTATE(startstate);                                            \return true;                                                   \}                                                                  \}while(0);                                                             \#define REGISTERSTATECONVERT(fromstate,condition,tostate)                      \do {                                                                   \std::string strFromState = typeid(fromstate).name();              \std::string strToState = typeid(tostate).name();                  \if ( DEBUGFRAMEFLAG    ) {                                             \std::cout<<"strFromState:"<<strFromState<<std::endl;               \std::cout<<"condition:"<<condition<<std::endl;                     \std::cout<<"strToState:"<<strToState<<std::endl;                   \std::cout<<"m_strCurrentState:"<<m_strCurrentState<<std::endl;     \std::cout<<"m_strCondition:"<<m_strCondition<<std::endl<<std::endl;  \}                                                                      \if ( IsCompositeStates() ) {                                           \if ( strFromState != m_strCurrentState                                \|| ( !m_strCondition.empty() && condition != m_strCondition ) ) { \break;                                                     \}                                                                  \}                                                                      \else {                                                                 \if ( strFromState != m_strCurrentState                                \|| condition != m_strCondition ) {                                \break;                                                         \}                                                                  \}                                                                      \m_strCurrentState = strToState;                                       \STARTSTATE(tostate);                                               \return true;                                                       \}while(0);                                                             \#define REGISTERSTATECONVERTEND()                                              \return false;                                                          \}; 

然后复合状态类和状态机类只要使用这些宏去组织状态跳转,就可以清晰的描述过程了。

最后附上测试工程的源码。链接:http://pan.baidu.com/s/1pJ3djKZ 密码:genu

使用C++实现一套简单的状态机模型——原理解析相关推荐

  1. 使用C++实现一套简单的状态机模型——实例

    一般来说,"状态机"是一种表达状态转换变换逻辑的方法.曾经有人和我讨论过为什么不直接用ifelse,而要使用"状态机"去实现一些逻辑,认为使用"状态机 ...

  2. python 支付宝个人账单_金融支付财务融合业务-实践分享1:订单、账单、交易流水、账套知识解构、原理解析...

    本文作者从实际工作实践出发,结合案例等分享了电商金融支付财务融合中的基本概念和相关原理解析,包括:订单.账单.交易流水和账知识解构,供大家一同参考和学习. 从事电商.进销存.金融.支付.财务的产品同学 ...

  3. 一套简单通用的Java后台管理系统,拿来即用,非常方便(附项目地址)

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:huanzi-qch cnblogs.com/huanzi- ...

  4. sql 拼接int类型的字段_一套简单实用的SQL脚本(下篇)

    点击上方SQL数据库开发,关注获取SQL视频教程 SQL专栏 SQL数据库基础知识汇总 SQL数据库高级知识汇总 之前已经分享了一部分内容<一套简单实用的SQL脚本(上篇)>接上一章我们继 ...

  5. ajaxfileupload 访问不到后台_一套简单通用的Java后台管理系统,拿来即用,非常方便(附项目地址)...

    前言 这套Base Admin是一套简单通用的后台管理系统,主要功能有:权限管理.菜单管理.用户管理,系统设置.实时日志,实时监控,API加密,以及登录用户修改密码.配置个性菜单等 技术栈 前端:la ...

  6. 搭建一套简单的CDN网络

    目前对于CDN网络搭建技术有很多成熟的商业方案,对于资金受限的网站可能无法自己搭建或购买.这里提供了一套简单的实现CDN网络的技术架构,所使用的软件全部是开源高效且免费的. 根据CDN网络技术原理,必 ...

  7. 设计一套简单的计算机系统及其指令系统,【精品】计算机组成综合设计指导书...

    [精品]计算机组成综合设计指导书 (45页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 19.9 积分 <计算机组成综合设计>指导董志学刘志强 ...

  8. 一套简单通用的Java后台管理系统,拿来即用,非常方便(附项目源码)

    前言 这套Base Admin是一套简单通用的后台管理系统,主要功能有:权限管理.菜单管理.用户管理,系统设置.实时日志,实时监控,API加密,以及登录用户修改密码.配置个性菜单等 技术栈 前端:la ...

  9. C语言实现简单的状态机

    先来看一下状态机的定义: 状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动作.完成特定操作的控制中心.有限状态机简写为FSM(Finite Sta ...

最新文章

  1. HDU1892(二维树状数组)
  2. 【解题报告】【HDOJ1233】【最小生成树】还是畅通工程
  3. MySql: 常见错误
  4. [大數據、Big Data、巨量資料、海量資料]之分析模式工具
  5. 前端学习(1403):多人管理23错误unexpected identifier
  6. CentOS6.9下ssh密钥登录配置步骤(免密码登录)和ssh-keygen 命令常用参数
  7. php 获取agent,PHP代码 解析HTTP_USER_AGENT 获取客户端操作系统
  8. Code Block 使用笔记(win7、updating)
  9. Git pull 强制覆盖本地文件
  10. 中小型研发团队架构落地实践18篇,含案例、代码
  11. 罗盘时钟制作代码_抖音超火的姓氏罗盘壁纸,安卓和苹果都能做
  12. UiPath安装pdf
  13. OFDM学习笔记(四)(信道估计简介)
  14. [视频编码] BD-rate和BD-PSNR
  15. happen-before八条原则
  16. CheckBox和ListView的结合使用
  17. 光学红外雨量IFR202型传感器智慧检测雨量场景等行业
  18. 查询当前数据库名、用户名、数据库服务器IP、端口、数据库版本信息。
  19. 西山小菜鸟之Scrapy学习笔记---爬取企查查网站公司基本信息
  20. 5-(4-氨基苯基)-10,15.20-三苯基卟啉(TPP-NH2)/多金属氧酸盐-四苯基卟啉化合物TPP-PW齐岳供应

热门文章

  1. PaddleClas
  2. 使用Python,OpenCV从图像中删除轮廓
  3. 什么是self-attention、Multi-Head Attention、Transformer
  4. runtime 日志权限不够_MySQL权限处理的一个小bug
  5. python argv 详解_Python3 sys.argv[ ]用法详解
  6. g-gdb工具使用图谱(持续更新)
  7. 高并发系统搭建:web负载均衡
  8. Cachegrind--缓存命中检查工具及其可视化
  9. [kuangbin带你飞]专题五 并查集 E - 食物链 (带权并查集)
  10. Django-C002-深入模型,到底有多深