我只是想知道是否有人知道在互联网上开发状态机的一些很好的教程.还是电子书?
我开始在状态机上工作,只需要一些通用的东西让我开始.
如果使用函数指针,状态机在C中非常简单.
基本上你需要2个数组 - 一个用于状态函数指针,一个用于状态转换规则.每个状态函数返回代码,您按状态查找状态转换表并返回代码以查找下一个状态,然后执行它.
int entry_state(void); int foo_state(void); int bar_state(void); int exit_state(void); /* array and enum below must be in sync! */ int (* state[])(void) = { entry_state, foo_state, bar_state, exit_state}; enum state_codes { entry, foo, bar, end}; enum ret_codes { ok, fail, repeat}; struct transition { enum state_codes src_state; enum ret_codes ret_code; enum state_codes dst_state; }; /* transitions from end state aren't needed */ struct transition state_transitions[] = { {entry, ok, foo}, {entry, fail, end}, {foo, ok, bar}, {foo, fail, end}, {foo, repeat, foo}, {bar, ok, end}, {bar, fail, end}, {bar, repeat, foo}}; #define EXIT_STATE end #define ENTRY_STATE entry int main(int argc, char *argv[]) { enum state_codes cur_state = ENTRY_STATE; enum ret_codes rc; int (* state_fun)(void); for (;;) { state_fun = state[cur_state]; rc = state_fun(); if (EXIT_STATE == cur_state) break; cur_state = lookup_transitions(cur_state, rc); } return EXIT_SUCCESS; }
我没有放lookup_transitions()
功能,因为它是微不足道的.
这就是我多年来一直使用机器的方式.
我更喜欢在巨大的switch
语句上使用函数指针,但与qrdl的答案相反,我通常不使用显式返回码或转换表.
此外,在大多数情况下,您需要一种机制来传递其他数据.这是一个示例状态机:
#includestruct state; typedef void state_fn(struct state *); struct state { state_fn * next; int i; // data }; state_fn foo, bar; void foo(struct state * state) { printf("%s %i\n", __func__, ++state->i); state->next = bar; } void bar(struct state * state) { printf("%s %i\n", __func__, ++state->i); state->next = state->i < 10 ? foo : 0; } int main(void) { struct state state = { foo, 0 }; while(state.next) state.next(&state); }
状态机本身不需要解释甚至使用教程.我建议您查看数据以及如何解析数据.
例如,我不得不解析近太空气球飞行计算机的数据协议,它以特定格式(二进制)将数据存储在SD卡上,需要将其解析为逗号分隔文件.使用状态机是最有意义的,因为根据下一部分信息,我们需要更改我们正在解析的内容.
代码使用C++编写,可用作ParseFCU.如您所见,它首先检测我们正在解析的版本,并从那里进入两个不同的状态机.
它以一个已知良好的状态进入状态机,此时我们开始解析,并根据我们遇到的字符,我们要么继续前进到下一个状态,要么返回到先前的状态.这基本上允许代码自我适应数据的存储方式以及是否存在某些数据.
在我的示例中,GPS字符串不是飞行计算机记录的要求,因此如果找到该单个日志写入的结束字节,则可以跳过GPS字符串的处理.
状态机很容易编写,一般来说我遵循它应该流动的规则.通过系统的输入应该从一个状态流到一个状态.
不幸的是,状态机上的大多数文章都是为C++或其他直接支持多态的语言编写的,因为很好地将FSM实现中的状态建模为派生自抽象状态类的类.
但是,使用switch语句将事件分配到状态(对于简单的FSM,它们几乎是代码)或使用表将事件映射到状态转换,在C中实现状态机非常容易.
关于C中状态机的基本框架,有一些简单但不错的文章:
http://www.gedan.net/2008/09/08/finite-state-machine-matrix-style-c-implementation/
http://www.gedan.net/2009/03/18/finite-state-machine-matrix-style-c-implementation-function-pointers-addon/
编辑:网站"正在维护",网站存档链接:
http://web.archive.org/web/20160517005245/http://www.gedan.net/2008/09/08/finite-state-machine-matrix-style-c-implementation
http://web.archive.org/web/20160808120758/http://www.gedan.net/2009/03/18/finite-state-machine-matrix-style-c-implementation-function-pointers-addon/
switch
基于语句的状态机通常使用一组宏来"隐藏" switch
语句的机制(或者使用一组if
/ then
/ else
语句而不是a switch
),并使用"FSM语言"来描述C语言中的状态机资源.我个人更喜欢基于表格的方法,但这些方法肯定具有优点,被广泛使用,并且对于更简单的FSM尤其有效.
Steve Rabin在"Game Programming Gems"第3.0章(设计通用的强大AI引擎)中概述了一个这样的框架.
这里讨论一组类似的宏:
https://contrarymotion.net/2008/02/12/some-code-snippets-for-a-simple-c-state-machine/
如果您对C++状态机实现也感兴趣,那么可以找到更多内容.如果你有兴趣,我会发布指针.