c++状态机的使用

news2024/11/18 13:47:05

什么是状态机

状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型。英文名字叫State Machine ,不是指一台实际机器,一般就是指一张状态转换图。全称是有限状态自动机,自动两个字包含重要含义。给定一个状态机,同时给定它的当前状态以及输入,那么输出状态时可以明确的运算出来的,当输入条件时,能输出下一个状态。

现实事物是有不同状态,例如一个LED等,就有 亮 和 灭两种状态。我们通常所说的状态机是有限状态机,也就是被描述的事物的状态的数量是有限个,例如LED灯的状态就是两个亮和 灭。

在这里插入图片描述

为什么用状态机

状态机解决的问题就是当某种模型的状态变更比较比较复杂,且状态比较多,那么我们有必要将这些状态变化的逻辑抽象出来,做成一个可以统一调用的算法,这样封装出来的代码就比较好维护,同时可读性也很强。

状态机在实际工作开发中很有用,应用也非常广泛。一个健壮的状态机可以让你的程序,不论发生何种突发事件都不会突然进入一个不可预知的程序分支,可以很清晰的表达整个状态的流转。

在GUI应用程序、Web应用程序等事件驱动型的应用程序,采用状态机的思路来完成程序设计,可以简化设计流程,使程序的可读性、可维护性都得到增加。

 使用状态机有哪些好处?
1. 当一个程序有多个状态时,规范了状态机的状态转换,避免了一些引入一些复杂的判断逻辑。
2. 规范了程序在不同状态下所能提供的能力。
3. 在能力上可以进行横向扩展,提供新的状态来完善现有逻辑。

简单状态机的实现

使用switch跳转即可实现一种简单的状态机。如果逻辑不是很复杂,使用switch语句也能达到实现目的。举例如下:

enum state
  {
    nullState_,
    firstState_,
    secondState_,
    thirdState_,
    quitState_,
  };

struct param_t
  {
    int param1;
    int param2;
// ......
  };

int nullStateProc(param_t& param){
    return firstState_;
}

int firstStateProc(param_t& param){
    return secondState_;
}

int secondStateProc(param_t& param){
    return secondState_;
}

int thirdStateProc(param_t& param){
    return secondState_;
}

int quitEvent(){
    return nullState_;
}

int stateMachine(state& state_, param_t& param){
    switch (state_)
  {
    case nullState_:
      state_ = static_cast<state>(nullStateProc(param));
      break;
    case firstState_:
      state_ = static_cast<state>(firstStateProc(param));
      break;
    case secondState_:
      state_ = static_cast<state>(secondStateProc(param));
      break;
    case thirdState_:
      state_ = static_cast<state>(thirdStateProc(param));
      break;
    case quitState_:
      quitEvent();
      return 0;
  }

  return 1;

}

void start()
{
  state state_ = nullState_;
  param_t param{};

  while (true)
  {
    auto stateResult = stateMachine(state_, param);
    if (!stateResult)
    {
      return;
    }
  }
}

另一种简单实现

如果需要管理的状态和事件比较多,需要逻辑清晰和便于维护,使用简单的switch可能无法满足需求。这里介绍一种简单的实现,消除庞大的条件分支语句,从配置表容易看出各个状态的转换图。

/*  statemachine.h*/

#ifndef _STATEMACHINE_H_
#define _STATEMACHINE_H_

#include <iostream>

enum EventActionResult {
    EventFailed, EventProcessedOK
};

template<class T, class P>
class State {
public:
    std::string inputEvent;
    State<T, P> *nextState;

    EventActionResult (T::*action)(const std::string &event, P *param);

    State<T, P> *errorState;
};


template<class T, class P>
class StateMachine {
private:
    State<T, P> *init;
    State<T, P> *current;
    T *target;

public:

    StateMachine() {}

    void Init(T *_target, State<T, P> *initialState) {
        init = current = initialState;
        target = _target;
    }

    void Reset() {
        current = init;
    }

    void ProcessEvent(const std::string &event, P *param) {
        for (State<T, P> *p = this->current; p->nextState != NULL; p++) {
            if (p->inputEvent == event) {
                if (p->action != NULL) {
                    if (EventFailed == (this->target->*(p->action))(event, param)) {
                        if (p->errorState != NULL) {
                            //Only if there's an errorstate defined. Otherwise, just do nothing
                            this->current = p->errorState;
                        }
                        return;
                    }
                }
                this->current = p->nextState;
                return;
            }
        }

        //Event not found. Do nothing
        return;
    }
};

以下是使用举例:

class MyStateMachine {
public:

    struct param_t {
        int param;
        int param1;
        int param2;
    };

    StateMachine<MyStateMachine, param_t> stMachine;

    EventActionResult HandleEvent1(const std::string &e, param_t *param);

    EventActionResult HandleEvent2(const std::string &e, param_t *param);

    EventActionResult HandleEventA(const std::string &e, param_t *param);

    EventActionResult HandleEventB(const std::string &e, param_t *param);

    EventActionResult HandleThree(const std::string &e, param_t *param);


    void HandleEvent(const std::string &e, param_t *param);

    void Init();

    void Start();
};
#include "statemachine.h"
#include <cstdlib>
#include <iostream>
#include <stdio.h>

typedef State<MyStateMachine, MyStateMachine::param_t> STATE;

extern STATE Idle[];
extern STATE One[];
extern STATE Two[];


STATE Idle[] =
        {
                //EVENT,NEXT,  ACTION,   ERRORSTATE (where to land if there's an error)
                {"event1", One, &MyStateMachine::HandleEvent1, Idle},
                {"event2", Two, &MyStateMachine::HandleEvent2, Idle},
                {"", NULL, NULL, NULL}, //End of table
        };

STATE One[] =
        {
                {"eventA", Idle, &MyStateMachine::HandleEventA, Idle},
                {"eventB", Idle, &MyStateMachine::HandleEventB, Idle},
                {"", NULL, NULL, NULL},
        };

STATE Two[] =
        {
                {"eventC", Idle, NULL, NULL},
                {"", NULL,  NULL, NULL},
        };

EventActionResult MyStateMachine::HandleEvent1(const std::string &e, param_t *param) {
    std::cout << "HandleEvent1,param:" << param->param << std::endl;
    return EventProcessedOK;
}

EventActionResult MyStateMachine::HandleEvent2(const std::string &e, param_t *param) {
    std::cout << "HandleEvent2,param:" << param->param << std::endl;
    return EventProcessedOK;
}

EventActionResult MyStateMachine::HandleEventA(const std::string &e, param_t *param) {
    std::cout << "HandleEventA,param:" << param->param << std::endl;
    return EventProcessedOK;
}

EventActionResult MyStateMachine::HandleEventB(const std::string &e, param_t *param) {
    std::cout << "HandleEventB,param:" << param->param << std::endl;
    return EventProcessedOK;
}

EventActionResult MyStateMachine::HandleThree(const std::string &e, param_t *param) {
    std::cout << "HandleThree" << std::endl;
    return EventProcessedOK;
}


void MyStateMachine::HandleEvent(const std::string &e, param_t *param) {
    stMachine.ProcessEvent(e, param);
}

void MyStateMachine::Init() {
    stMachine.Init(this, Idle);
}

void MyStateMachine::Start() {
    while (1) {
        char c[255];
        // 模拟输入event
        std::cin.getline(c,255);
        std::string event{c};
        MyStateMachine::param_t param;
        param.param = 1;
        this->HandleEvent(event, &param);
    }
}

引用

什么是状态机?_pingxiaozhao的博客-CSDN博客_状态机的概念

为Linux应用构造有限状态机_wowocpp的博客-CSDN博客_linux 状态机

有限状态机详解(转载)_白小狮的博客-CSDN博客_有限状态机和无限状态机

Linux进程是如何创建出来的?

为Linux操作系统应用构造有限状态机方法-红联Linux系统门户

一文详解 Android状态机StateMachine 使用方式及实现原理_bjxiaxueliang的博客-CSDN博客_statemachine

c++写状态机_zhi_cary的博客-CSDN博客_c++ 状态机

C++有限状态机的实现_Valreaper的博客-CSDN博客_c++ 状态机

github经典C++状态机(fsm)源代码剖析_star-keke的博客-CSDN博客

用C++来实现有限状态机(附代码)_李肖遥的博客-CSDN博客

TinyFSM 介绍_百思可乐的博客-CSDN博客

C++状态机框架实现 - 灰信网(软件开发博客聚合)

状态模式(state)C++实现_shu_chang1993的博客-CSDN博客_c++state

c++写状态机_zhi_cary的博客-CSDN博客_c++ 状态机

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/70192.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Pandas中使用Merge、Join 、Concat合并数据效率对比

在 Pandas 中有很多种方法可以进行dataframe(数据框)的合并。 本文将研究这些不同的方法&#xff0c;以及如何将它们执行速度的对比。 合并DF Pandas 使用 .merge() 方法来执行合并。 import pandas as pd # a dictionary to convert to a dataframe data1 {identificati…

配件厂商Hyper推出支持苹果Find My背包,背包防丢越来越智能

配件厂商 Hyper 今天推出了一款支持 Find My 的双肩包 HyperPack Pro&#xff0c;这样发生遗失或者失窃事件之后能够通过苹果的这项追踪技术快速锁定背包的位置&#xff0c;帮你快速找回。 背包有一个太阳镜口袋和易于拿取的前袋&#xff0c;此外背包上还有一个隐藏式口袋&am…

Github Student Developer Pack申请流程【不在学校的认证方法】

一、GitHub Student Developer Pack申请流程 首先你需要注册一个Github账号 进入GitHub Student Developer Pack申请页面&#xff0c;点击sign up for student developer pack 接着会出现如下图所示的界面&#xff0c;因为是学生&#xff0c;所以点击左下角的Get student ben…

Python打包神器,打包速度快生成文件小防反编译

分享一款打包速度快、生成文件小、更加安全的Python打包神器&#xff0c;本内容来源于网络。 一. pyinstaller和Nuitka使用感受 1.1 使用需求 这次也是由于项目需要&#xff0c;要将python的代码转成exe的程序&#xff0c;在找了许久后&#xff0c;发现了2个都能对python项目…

软件测试之系统测试总结报告

引言 编写目的 编写该测试总结报告主要有以下几个目的 通过对测试结果的分析&#xff0c;得到对软件质量的评价分析测试的过程&#xff0c;产品&#xff0c;资源&#xff0c;信息&#xff0c;为以后制定测试计划提供参考评估测试测试执行和测试计划是否符合分析系统存在的缺陷…

(附源码)Springboot中北创新创业官网 毕业设计 271443

Springboot中北创新创业官网 摘 要 进入21世纪以来&#xff0c;计算机有了迅速的发展。计算机应用、信息技术全面渗透到了人类社会的各个方面&#xff0c;信息化已成为世界经济和社会发展的大趋势。本文主要通过对中北创新创业官网的功能性需求分析&#xff0c;对系统的安全性和…

HTTP状态码206报错

HTTP状态码&#xff08;英语&#xff1a;HTTP Status Code&#xff09;是用以表示网页服务器超文本传输协议响应状态的3位数字代码。它由 RFC 2616 规范定义的&#xff0c;并得到 RFC 2518、RFC 2817、RFC 2295、RFC 2774 与 RFC 4918 等规范扩展。所有状态码的第一个数字代表了…

基于jsp+mysql+ssm富锦市业余足球联赛管理系统-计算机毕业设计

项目介绍 足球联赛管理系统主要目的是对足球联赛中心所有的足球联赛信息进行管理&#xff0c;并且合理管理好管理员发布球队、球员和比赛信息&#xff0c;会员浏览查看球队和比赛信息的流程。提高足球联赛管理的工作效率&#xff0c;降低管理的成本。本系统选用Windows作为服务…

Go-Excelize API源码阅读(四十一)——GetCellRichText

Go-Excelize API源码阅读&#xff08;四十一&#xff09;——GetCellRichText 开源摘星计划&#xff08;WeOpen Star&#xff09; 是由腾源会 2022 年推出的全新项目&#xff0c;旨在为开源人提供成长激励&#xff0c;为开源项目提供成长支持&#xff0c;助力开发者更好地了解…

加固技术护城河,比亚迪商用车加速领跑全球电动化进程

在10月召开的德国汉诺威国际交通运输博览会上&#xff0c;比亚迪首次发布全新刀片电池客车底盘技术平台。 随后的11月&#xff0c;比亚迪首款基于全新刀片电池客车底盘技术平台的新车型B12A03重磅亮相第四届LTA-UITP新加坡国际交通大会暨展览会。 据悉&#xff0c;B12A03是一…

Java 基础(继承)

继承基础1. 继承的特点2. super关键字3. 继承中变量访问特点&#xff08;就近原则&#xff09;4. 继承中成员方法访问特点5. 继承中构造访问特点为什么子类中所有构造方法默认都会访问父类无参构造方法。如果父类中没有无参构造6. 重写概述应用注意事项方法重写和重载有什么区别…

ssm的校园单车自行车租赁系统|租车系统计算机专业毕业论文java毕业设计开题报告

&#x1f496;&#x1f496;更多项目资源&#xff0c;最下方联系我们✨✨✨✨✨✨ 目录 Java项目介绍 资料获取 Java项目介绍 《ssm的校园单车租赁系统》该项目采用技术&#xff1a;jsp springmvcspringmybatis cssjs等相关技术&#xff0c;前端页面使用了EasyUI框架&#…

【Redis】分布式锁基础

1. 集群下的线程并发安全问题 当项目形成集群时&#xff0c;每个 JVM 有一个锁监视器&#xff0c;这些锁监视器彼此间不互通&#xff0c;因此会出现集群下的线程并发安全问题&#xff0c;如下图所示&#xff1a; 2. 分布式锁 分布式锁是解决集群下的线程安全问题的方案&#…

Google Earth Engine(GEE)——image.select(...).rgbToHsv is not a function

本错误涵盖了好多错误,主要的原因就是函数不明确,不知道如何使用,以及函数的参数的设定,所以这里大家要先去看函数,另外,最主要的问题就是影像集合和影像要区分开,否则就会出现xxx is nor a function。这里我们用要注意!!! 提示: Line 9: image.select(...).rgbTo…

YApi跨域插件cross-request无法使用的解决方案

cross-request遇到的问题 最近在使用cross-request插件会收到一些报错信息&#xff1a; Manifest version 2 is deprecated, and support will be removed in 2023. See https://developer.chrome.com/blog/mv2-transition/ for more details. 以上信息是说Google推出了Man…

ADI的DSP开发软件:CCES软件的使用说明(很早期写的一点心得)

CCES 是 ADI 最新的 DSP 开发环境&#xff0c;全称是 Cross Core Embedded Studio&#xff0c;我从 2012 年的1.0.1 版开始使用&#xff0c;也曾经写过一个非常详细的软件安装文档&#xff0c;现在来详细的说一说 CCES 软件的使用操作。下面是我列出来的标题&#xff0c;一个一…

点成分享 | 为什么动物细胞培养大都需要二氧化碳培养箱

在进行动物细胞体外培养的过程中&#xff0c;培养条件苛刻&#xff0c;仅在环境因素方面除了要有严格的无菌环境、适合的温度、渗透压、相对湿度等&#xff0c;维持细胞周围的气体环境和pH稳定也十分重要。 动物细胞的体外培养需要理想的气体环境和适宜的pH环境。众所周知&…

Python中的type和object详解

这篇博客主要描述Python的新风格对象(new-style objects)&#xff0c;如下&#xff1a; <type type>和<type object>分别是什么&#xff1f;用户自定义的类和实例之间是怎么联系的&#xff1f;它们和内置类型又是怎么联系的&#xff1f;什么是元类(metaclasses)&a…

npm安装慢 ,卡在sill idealTree buildDeps不动

先说结论&#xff1a;node的默认镜像是国外的&#xff0c;修改镜像为国内地址。 报错截图&#xff1a; Failed to check for updates&#xff0c;开始以为是版本问题&#xff0c;并没有理会&#xff0c;直接创建项目&#xff0c;结果卡在了sill idealTree buildDeps&#xff08…

有关于torch.autograd.grad

torch.autograd模块给用户提供了一个自定义求导的接口。torch.autograd.grad可以手动地将loss对某部分参数进行梯度求导&#xff0c;特别适用于debug和观测。 笔者经常使用这个接口用于观测模型优化出现问题时&#xff0c;梯度值是否出现异常&#xff1b;以及用于代替tensorbo…