转载声明:如果转载本博客内容,请联系[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] 唐童鞋^_^