標籤:
轉載聲明:如果轉載本部落格內容,請聯絡[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_PROCEDUREtypedef 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] 唐童鞋^_^
C語言狀態機器模板