转载声明:如果转载本博客内容,请联系869119842@qq.com,获得作者书面授权。

前言

上一篇我的博客中探讨了一种非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定中增减状态,宏就会帮你完成后面一切,而且你的状态函数可以放在任意位置,唯一需要注意的是函数名必须遵循定义个格式,且状态引用名也必须遵循定义个格式,不过既然是模板就有相应格式,遵循即可。

总结

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

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

  1. C语言状态机

    转载声明 如果转载本博客内容,请联系869119842@qq.com,获得作者书面授权 前言 状态机的好处不用多说,自己百度去,但传统的编程模式,无论是C语言,或是硬件FPGA的Verilog都是采用 ...

  2. go语言的模板,text/template包

    go语言的模板,text/template包 定义 模板就是将一组文本嵌入另一组文本里 传入string--最简单的替换 package main import ( "os" &q ...

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

    Xamarin XAML语言教程模板视图TemplatedView(二) (2)打开MainPage.xaml文件,编写代码,将构建的控件模板应用于中TemplatedView.代码如下: <? ...

  4. Xamarin XAML语言教程模板视图TemplatedView(一)

    Xamarin XAML语言教程模板视图TemplatedView(一) 模板视图TemplatedView 与模板页面相对的是TemplatedView,它被称为模板视图,它的功能和模板页面类似,也 ...

  5. Xamarin XAML语言教程模板页面TemplatedPage

    Xamarin XAML语言教程模板页面TemplatedPage 模板页面TemplatedPage 在上文中我们提到了TemplatedPage,它被称为模板页面,用来显示控件模版.Templat ...

  6. django基础 -- 4. 模板语言 过滤器 模板继承 FBV 和CBV 装饰器 组件

    一.语法 两种特殊符号(语法): {{ }}和 {% %} 变量相关的用{{}},逻辑相关的用{%%}. 二.变量 1. 可直接用  {{ 变量名 }} (可调用字符串, 数字 ,列表,字典,对象等) ...

  7. GO语言html模板

    模板 一个模板是一个字符串或一个文件,里面包含了一个或多个由双花括号包含的{{action}}对象.大部分的字符串只是按面值打印,但是对于actions部分将触发其它的行为.每个actions都包含了 ...

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

    1. 模板介绍 1.1 模板的功能 产生html,控制页面上展示的内容.模板文件不仅仅是一个html文件. 模板文件包含两部分内容: 静态内容:css.js.html. 动态内容:用于动态去产生一些页 ...

  9. C 语言通用模板队列

    前言 嵌入式开发过程中,各个模块之间,各个设备之间进行交互时,都会存在数据的输入输出,由于处理的方式不同,数据不会立即同步处理,因此通常在设计时都会设计缓冲区进行数据的处理,方式数据丢失等问题:一个项 ...

随机推荐

  1. HTTP 错误 500.23 - Internal Server Error 检测到在集成的托管管道模式下不适用的 ASP.NET 设置。

    检测到在集成的托管管道模式下不适用的ASP.NET设置的解决方法(非简单设置为[经典]模式). - CatcherX 2014-03-11 11:03 27628人阅读 评论(2) 收藏 举报  分类 ...

  2. node.js + webstorm :配置开发环境

    一.配置开发环境: 1.先安装node (1).访问http://nodejs.org打开安装包,正常安装,点击next即可. 为了测试是否安装成功,打开命令提示符,输入node,则进入node.js ...

  3. tensorflow版的bvlc模型

    研究相关的图片分类,偶然看到bvlc模型,但是没有tensorflow版本的,所以将caffe版本的改成了tensorflow的: 关于模型这个图: 下面贴出通用模板: from __future__ ...

  4. kali安装后的网络设置

    Kali linux 安装完成后,需要对其网络进行配置.使用DHCP服务是配置网卡最简单的方法之一,但渗透测试时通常不会这样做,因为系统会被记录在DHCP服务器的数据库中. 1  动态DHCP方式 配 ...

  5. android studio异常关机后出现的问题

      使用android studio 时突然卡死. 重启后所有项目都打不开,提示workspace.xml  Error:content is not allowed in prolog 打开work ...

  6. I:trainage Ditches

    总时间限制: 1000ms 内存限制: 65536kB描述Every time it rains on Farmer John's fields, a pond forms over Bessie's ...

  7. 关于链接到QQ客服的问题,原来只需要在a标签上加一串话而已呐~~~~

    <a class="btn" target=blank href=tencent://message/?uin=&Site=www.woyouli.com&M ...

  8. WEBSTORM 2016.3 activation code激活

    选择activation code 激活方式,复制粘贴下面的激活码43B4A73YYJ-eyJsaWNlbnNlSWQiOiI0M0I0QTczWVlKIiwibGljZW5zZWVOYW1lIjoi ...

  9. jquery性能

    1. 使用最新版本的jQuery jQuery的版本更新很快,你应该总是使用最新的版本.因为新版本会改进性能,还有很多新功能. 下面就来看看,不同版本的jQuery性能差异有多大.这里是三条最常见的j ...

  10. java 创建线程的三种方法Callable,Runnable,Thread比较及用法

    转自:http://www.chinaitlab.com/Java/line/942440.html 编写多线程程序是为了实现多任务的并发执行,从而能够更好地与用户交互.一般有三种方法,Thread, ...