C语言状态机模板

转载声明:如果转载本博客内容,请联系[email protected],获得作者书面授权。

前言

上一篇我的博客中探讨了一种非swtich-case结构的状态机写法,但是个人感觉写起来比较麻烦,如果增加一个状态,需要手动地在函数指针数组中添加相应的功能函数,而且状态函数的也必须写在函数指针数组前面导致代码结构较差,如果写在后面,又要在前面声明,就更麻烦了,总之,不易维护,想到Adam Dunkels在LwIP中逆天的宏定义用法,使得代码有自动更新的功能,于是也尝试着套用下其写法,现在这段代码更易维护,可做模板使用。

实现

还是上一篇博客的例子,只是多了些宏定义,这次先定义个状态列表:

#define STATE_LIST(_) _(init)_(count)_(done)_(dft)

可能你觉得奇怪,那么先别急,看下一段定义:

#define DEFINE_STATE(state) State##_##state,
enum States {
    STATE_LIST(DEFINE_STATE)
    State_Nums
};
#undef DEFINE_STATE

对于初学者可能已经晕了,其实你只需记住宏就是替换,就能理解,下面我先展开States中第一层(注意逗号不可少):

enum States {
    DEFINE_STATE(init),
    DEFINE_STATE(count),
    DEFINE_STATE(done),
    DEFINE_STATE(dft),
    State_Nums
};

接下来根据DEFINE_STATE展开宏(##起连接Token的作用),则有:

enum States {
    State_init,
    State_count,
    State_stop,
    State_dft,
    State_Nums
};

这样我们得到了一个完整的状态枚举,运用此方法可声明状态函数,并将声明的状态函数存入函数指针数组中,你的状态函数也不会有位置限制,完整代码如下:

#include<stdio.h>
typedef unsigned char State;
typedef State(*Procedure)(void *);
//状态列表,状态增减只在此处按格式写入
#define STATE_LIST(_) _(init)_(count)_(done)_(dft)

#define Step_NULL ((void *)0)

//状态执行函数声明,你的函数必须遵循如Step_init的格式
//并必须返回下一状态如State_init
#define STATEMENT_STEP(state) State Step##_##state(void * arg);
STATE_LIST(STATEMENT_STEP)
#undef STATEMENT_STEP

//状态枚举
#define DEFINE_STATE(state) State##_##state,
enum States {
    STATE_LIST(DEFINE_STATE)
    State_Nums
};
#undef DEFINE_STATE

//状态执行函数查找表
#define STATE_PROCEDURE(State) Step##_##State,
Procedure Steps[] = {
    STATE_LIST(STATE_PROCEDURE)
    Step_NULL
};
#undef STATE_PROCEDURE

typedef struct _SM_VAR  //对状态机参数封装
{
    int cnt;
}SM_VAR;

void BestStateMachine(void * invar)
{
    static State NS = State_init; //定义下一状态
    NS = Steps[NS](invar);
}
int main(void)
{
    SM_VAR var;
    int i;
    for (i = 0; i <= 8; i++){
        BestStateMachine(&var);
    }
    return 0;
}
State Step_init(void * arg)//初始化
{
    SM_VAR *p = (SM_VAR *)arg;
    p->cnt = 0;
    printf("CS:init ;cnt=%d;NS:count\n", p->cnt);
    return State_count;
}
State Step_count(void * arg)//计数
{
    SM_VAR *p = (SM_VAR *)arg;
    if (p->cnt < 3){
        p->cnt += 1;
        printf("CS:count;cnt=%d;NS:count\n", p->cnt);
        return State_count;
    }
    else{
        printf("CS:count;cnt=%d;NS:done\n", p->cnt);
        return State_done;
    }
}
State Step_done(void * arg)//计数完成
{
    SM_VAR *p = (SM_VAR *)arg;
    printf("CS:done ;cnt=%d;NS:init\n", p->cnt);
    return State_init;
}
State Step_dft(void * arg)//错误过程
{
    SM_VAR *p = (SM_VAR *)arg;
    printf("Wrong State\n");
    return State_init;
}

在VS2013调试,输出结果和上一篇博客一样,你现在一定会发现代码修改变得异常简单,你只需在STATE_LIST定中增减状态,宏就会帮你完成后面一切,而且你的状态函数可以放在任意位置,唯一需要注意的是函数名必须遵循定义个格式,且状态引用名也必须遵循定义个格式,不过既然是模板就有相应格式,遵循即可。

总结

不多说了,由此模板,状态机编写变得异常简单,你可以将注意完全转移到状态机的运行,而不必考虑其他的事。邮箱:[email protected] 唐童鞋^_^

时间: 2024-11-08 23:36:08

C语言状态机模板的相关文章

Xamarin XAML语言教程模板视图TemplatedView(二)

Xamarin XAML语言教程模板视图TemplatedView(二) (2)打开MainPage.xaml文件,编写代码,将构建的控件模板应用于中TemplatedView.代码如下: <?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://s

C语言 HelloWorld 模板

C语言 HelloWorld 模板 在写第一个C语言的程序前你先需要了解一些关于C语言的基础知识. 基础知识 一.注释 1.行注释 // 2.块注释 /* */ 二.头文件 1.导入库操作 // 导入库操作 include// 导入一个文件 stdio.h(std是一个标准库.i是input.o是output..h头文件.标准输入输出库) // <>表示导入系统文件. “”表示导入自定义文件 #include <stdio.h> 三.函数 1.函数 // int 数据类型.整型.如

Django中模板总结[模板语言,模板继承,CSRF攻击,反向解析]

模板 概述 作为Web框架,Django提供了模板,用于编写html代码,还可以嵌入模板代码更快更方便的完成页面开发,再通过在视图中渲染模板,将生成最终的html字符串返回给客户端浏览器.模版致力于表达外观,而不是程序逻辑.模板的设计实现了业务逻辑view与显示内容template的分离,一个视图可以使用任意一个模板,一个模板可以供多个视图使用. 创建模板文件夹 在项目的文件里面定义一个templates文件夹 在创建的文件夹里面再创建一个文件夹为每个应用的html文件夹 起名为和应用名同名的文

GO语言html模板

模板 一个模板是一个字符串或一个文件,里面包含了一个或多个由双花括号包含的{{action}}对象.大部分的字符串只是按面值打印,但是对于actions部分将触发其它的行为.每个actions都包含了一个用模板语言书写的表达式,一个action虽然简短但是可以输出复杂的打印值,模板语言包含通过选择结构体的成员.调用函数或方法.表达式控制流if-else 语句range循环语句,还有其它实例化模板等诸多特性.Action内部不能有换行,但注释可以有换行. 示例 模板执行时会遍历结构并将指针表示为'

Django框架(十一):模板介绍、模板语言、模板继承、HTML转义

1. 模板介绍 1.1 模板的功能 产生html,控制页面上展示的内容.模板文件不仅仅是一个html文件. 模板文件包含两部分内容: 静态内容:css.js.html. 动态内容:用于动态去产生一些页面内容.通过模板语言来产生. 1.2 模板文件的使用 通常是在视图函数中使用模板产生html内容返回给客户端. 加载模板文件loader.get_template,获取模板文件的内容,产生一个模板对象. 定义模板上下文RequeseContext,给模板文件传递数据. 模板渲染产生html页面内容r

【Java模板语言】TinyTemplate 实战 &lt;一&gt;:Serlvet集成

Writer      :BYSocket(泥沙砖瓦浆木匠) 一.什么是TinyTemplate 什么是模板语言? 模板语言是为了使用户界面与业务数据(内容)分离而产生的,并能生成特定格式的文档. 什么是TinyTemplate?(开源项目地址入口) Tiny模板引擎是一个基于Java技术构建的模板引擎,它具有体量小.性能高和扩展易的特点. 适合于所有通过文本模板生成文本类型内容的场景,如:XML.源文件.HTML等等,可以说,它的出现就是为了替换Velocity模板引擎而来,因此在指令集上在尽

《开源框架那点事儿23》:Tiny模板语言(VelocityPlus)初步入门

2 Tiny模板语言概述 3 Tiny模板语言能为您做什么? 4 Tiny模板语言简介 5 Hello Tiny! 6 注释 7 引用(References) 7.1 变量(variables) 7.2 属性(properties) 7.3 方法(methods) 8 表达式(Expression) 8.1 取值表达式 8.2 Map常量 8.3 数组常量 8.4 其他表达式 9 索引表示法 10 渲染 11 与Java无缝对接 12 模板布局 13 指令集 13.1 #set指令 13.2 条

高性能跨语言模板引擎Crox

http://thx.github.io/crox/ Crox是一个由JavaScript语言实现的高性能跨语言模板引擎.Crox模板可以直接在JavaScript环境中使用,也可以翻译成PHP,JSP等其他编程语言的可执行方法或翻译成Velocity,Smarty等其他模板引擎的源模板,Crox将保证翻译后的结果具备最佳执行效率. 在生产系统中,我们使用的各种前后端模板引擎多有其独到的一面,而Crox的特点即它是面向"跨语言并高性能"这个目标设计的,Crox的设计过程是在模板提供的功

Tiny模板语言(VelocityPlus)初步入门

1 关于用户手册 本文主要介绍如何在模板中使用Tiny模板语言,通过查阅本手册,可以对Tiny模板语言 TTL(Tiny Template Language)的用法有一个较全面的认识,并学会如何有效地使用Tiny模板语言.同时,本文提供了较多的例子帮您来学习并掌握它. 2 Tiny模板语言概述 Tiny 模板语言是一个参考Velocity语法的模板语言,它对Velocity模板语言中一些功能不太完全及使用过程中比较不方便的地方进行全面的扩展和升级,同时为了更好的适应Web界面层的开发,还提供了强