最近一个项目有一个需求,考量了一下决定使用状态机,实现完需求以后,不得不感慨,状态机在处理逻辑上面实现起来很有优势,也便于管理。
在这里分享一下我所修改的状态机实现。改动的地方不多,参考了《C语言最优状态机规范 - 投机者 》
和原作者的实现一样,状态机的跳转通过函数指针实现,将有关的状态函数实现入口放进一个数组,为了方便维护,数组的下标采取枚举型变量对应。
只要枚举变量中的下标顺序和函数指针数组对应,状态函数中返回下一次的状态,在主体函数中读取到上一次的状态,就可以完成状态机了。
为了方便控制和外部调用,我引入了一个专门记录状态的数据域(st),存放了当前的状态和下一个状态,还引出了一个能够重置状态机状态的接口;
以下是我的实现,使用的时候最简单的方法便是修改头文件中的step_*函数,以及Steps数组与states。高级一点可以将后2者引出,通过外部传参的方式实现。
这里是其中的一个可用的简单版本
/* # File : fsm.h # Author : toujizhe, schips # Git : https://gitee.com/schips/ # Ref : https://www.cnblogs.com/toujizhe/articles/5473489.html # Date : 2016-05-09 12:20, 2019-05-20 13:14:52 # Describe: 一个可以复位到状态机,根据ref的源码进行改善 理论上可以设置状态机为任何状态, 但是现在只提供重置为初始化状态的接口。 */ #ifndef __FSM_H_ #define __FSM_H_ /* ----------------------- Defines ------------------------------------------*/ // 数据类型定义区 typedef unsigned char State; typedef State(*Procedure)(void *); typedef struct _SM_STATE { State cs; // 当前状态 State ns; // 下个状态 } SM_STATE; typedef struct _SM_VALUE { // 自定义数据结构 int cnt; } SM_VALUE; // 状态机参数 typedef struct _SM_VAR { // 现在的状态以及下个状态 SM_STATE st; // 状态机数据区域 SM_VALUE var; } SM_ARG; /* ----------------------- Static functions ---------------------------------*/ static inline void SetNextState(void*arg, State st); static inline void EmyptData(void *arg); static inline State step_init(void * arg); static inline State step_count(void * arg);//计数 static inline State step_done(void * arg);//计数完成 static inline State step_default(void * arg);//错误过程 //状态定义(这里的顺序要求与 Steps[] 一致) enum states{ s_init, s_count, s_done, s_default}; // 函数指针 (相当于函数入口) static Procedure Steps[] = { step_init, step_count, step_done, step_default }; /* ----------------------- Start implementation -----------------------------*/ static inline void SetNextState(void*arg, State st) { SM_ARG *p = (SM_ARG *)arg; p->st.ns = st; } static inline void EmyptData(void *arg) { SM_ARG *p = (SM_ARG *)arg; p->var.cnt = 0; } //初始化 static inline State step_init(void * arg) { SM_ARG *p = (SM_ARG *)arg; // 状态处理 SetNextState(arg, s_count); // 数据处理 EmyptData(arg); return p->st.ns; } // 正常工作状态 static inline State step_count(void * arg)//计数 { SM_ARG *p = (SM_ARG *)arg; if (p->var.cnt < 3) { p->var.cnt ++; SetNextState(arg, s_count); } else { SetNextState(arg, s_done); } return p->st.ns; } // 任务完成 static inline State step_done(void * arg)//计数完成 { SM_ARG *p = (SM_ARG *)arg; // 驻留 SetNextState(arg, s_done); // 可以根据需要让其跳转到 s_init return p->st.ns; } static inline State step_default(void * arg)//错误过程 { SM_ARG *p = (SM_ARG *)arg; SetNextState(arg, s_done); return p->st.ns; } // 复位到初始情况 static inline void ResetStateMachine(void *arg) { SM_ARG *p = (SM_ARG *)arg; p->st.ns = s_init; } static inline void InitStateMachine(void *arg) { ResetStateMachine(arg); EmyptData(arg); } static inline int BestStateMachine(void *arg) { SM_ARG *p = (SM_ARG *)arg; // 记录当前状态 p->st.cs = p->st.ns; // 根据现在情况得到下一个阶段到状态 p->st.ns = Steps[p->st.cs](arg); // 返回当前状态(可根据需要修改) return p->st.cs; } #endif /* __FSM_H_ */
使用范例。
/* # File : fsm_demo.c # Author : SCHIPS # Mail : schips@dingtalk.com # Git : https://gitee.com/schips/ # Date : Mon, May 50, 2019 13:14:52 PM ♡ */ #include "fsm.h" #include <stdio.h> int main(void) { int ret = 0; SM_ARG var; InitStateMachine(&var); while(1) { printf("cs = %d, ns = %d\n",var.st.cs,var.st.ns); BestStateMachine(&var); // 停止条件 if(var.st.cs == s_done) { ResetStateMachine(&var); //break; } ret++; if(ret >10) { break; } } return 0; }
来源:https://www.cnblogs.com/schips/p/10926795.html