11
17

nunnifsmgen-有限狀態機程式碼自動產生器

這個是從同事那邊知道的好東西, Nunni FSM Generator,它能自動根據你輸入的設定檔產生狀態機的程式碼,詳細的資料請查閱軟體的使用說明,但我要講的重點在於它”自動產生的程式碼部份”,因為自動產生的code封裝得十分漂亮,所以我想對code做點介紹

以前寫狀態機的東西,不脫離if,else,case,switch…等這幾種語法搭配,可是寫出來的code”不大容易讀懂”,比如說我今天有個狀態機如下圖,假設有一張桌子,上面只能擺一個瓶子,我如果拿走了這個瓶子,那我就不能從桌子上再拿走瓶子了,如果桌子上已經擺了一個瓶子,那桌子上就再也不能多加瓶子上去

如果用if-else語法寫寫看,可能會像下面這樣

  1. #include <stdio.h> 
  2.  
  3. int bottles=1;
  4.  
  5. void GetBottleAPI() 
  6. { 
  7. if (bottles==1) 
  8. { 
  9. printf("You got bottle\n");
  10. bottles--;
  11. } 
  12. else printf("No bootle to get\n");
  13. } 
  14.  
  15. void PutBottleAPI() 
  16. { 
  17. if (bottles==0) 
  18. { 
  19. printf("You put a bottle\n");
  20. bottles++;
  21. } 
  22. else printf("Too much bootles\n");
  23. } 
  24.  
  25. int main() 
  26. { 
  27.  
  28. GetBottleAPI();
  29. GetBottleAPI();
  30. PutBottleAPI();
  31. PutBottleAPI();
  32. GetBottleAPI();
  33. }

用int bottles當global變數判斷get or put的動作是否合法,這種寫法如果用在小型的狀態機還OK,如果狀態機又多又大又複雜,可以想見程式碼會為這些狀態多寫好幾個判斷變數

試試看下面的程式碼,我只要定義好每個狀態與外來動作的觸發機制,就可以寫出蠻漂亮的code

  1. #include <stdio.h> 
  2.  
  3. struct TableState ;
  4.  
  5. struct TableFSM 
  6. { 
  7. int (*GetBottleStatus)( struct TableFSM *fsm, void * o );
  8. int (*PutBottleStatus)( struct TableFSM *fsm, void * o );
  9. void (*changeState)( struct TableFSM *fsm, struct TableState *nextState );
  10. struct TableState *m_state;
  11. };
  12.  
  13. struct TableState 
  14. { 
  15. int (*GetBottleStatus)( struct TableFSM *fsm, void * o );
  16. int (*PutBottleStatus)( struct TableFSM *fsm, void * o );
  17. };
  18.  
  19. static struct TableState TableFull;
  20. static struct TableState TableEmpty;
  21.  
  22. void GetBottle() 
  23. { 
  24. printf("You get a bottle\n");
  25. } 
  26.  
  27. void GiveBottle() 
  28. { 
  29. printf("You put bottle on the table\n");
  30. } 
  31.  
  32. void NoBottle() 
  33. { 
  34. printf("There is no bottle on the table\n");
  35. } 
  36.  
  37. void BottleFull() 
  38. { 
  39. printf("There is too much bottle on the table\n");
  40. } 
  41.  
  42. int FSMGetBottleStat( struct TableFSM *fsm, void * o ) 
  43. { 
  44. return fsm->m_state->GetBottleStatus( fsm, o );
  45. } 
  46.  
  47. int FSMPutBottleStat( struct TableFSM *fsm, void * o ) 
  48. { 
  49. return fsm->m_state->PutBottleStatus( fsm, o );
  50. } 
  51.  
  52. void FSMChangeState( struct TableFSM *fsm, struct BottleState *newState ) 
  53. { 
  54. fsm->m_state = newState;
  55. } 
  56.  
  57. int GetBottleAPI( struct TableFSM *fsm, void * o ) 
  58. { 
  59. GetBottle();
  60. fsm->changeState( fsm, &TableEmpty);
  61. } 
  62.  
  63.  
  64. int GiveBottleAPI(struct TableFSM *fsm, void * o ) 
  65. { 
  66. GiveBottle();
  67. fsm->changeState( fsm, &TableFull );
  68. } 
  69.  
  70. int TooFewBottleAPI(struct TableFSM *fsm, void * o ) 
  71. { 
  72. NoBottle();
  73. fsm->changeState( fsm, &TableEmpty );
  74.  
  75. } 
  76.  
  77. int TooMuchBottleAPI(struct TableFSM *fsm, void * o ) 
  78. { 
  79. BottleFull();
  80. fsm->changeState( fsm, &TableFull );
  81.  
  82. } 
  83.  
  84.  
  85. int main() 
  86. { 
  87. static struct TableFSM bf;
  88.  
  89. TableFull.GetBottleStatus = GetBottleAPI;
  90. TableFull.PutBottleStatus = TooMuchBottleAPI;
  91. TableEmpty.GetBottleStatus = TooFewBottleAPI;
  92. TableEmpty.PutBottleStatus = GiveBottleAPI;
  93. bf.GetBottleStatus=FSMGetBottleStat;
  94. bf.PutBottleStatus=FSMPutBottleStat;
  95. bf.changeState=FSMChangeState;
  96. bf.m_state=&TableFull;
  97.  
  98. FSMGetBottleStat(&bf,0);
  99. FSMGetBottleStat(&bf,0);
  100. FSMPutBottleStat(&bf,0);
  101. FSMPutBottleStat(&bf,0);
  102. }

上面的程式碼相當簡潔,當外部動作觸發內部狀態改變,會把FSM指定的函式指定到轉入後的state callback,所以programmer可以完全專注在GetBottleAPI之類的函式實作,而不用考慮state轉換的程式複雜度

標籤: software
評論: 2 | 引用: 0 | 閱讀: 18042
  • 1 
ahdeng [ 2008-12-03 18:06 | 回覆 | 編輯 刪除 ]
請問"狀態機"在寫程式時那裡會用到?
或是說"狀態機"應用在那裡?
Joey [ 回復於2008-12-12 10:56 郵箱 | 編輯 刪除 ]
STUN(udp traversal through NAT)這樣的程式就會需要定義比較完整的狀態機
  • 1 
發表評論
暱 稱: 密 碼:
網 址: E - mail:
驗證碼: 驗證碼圖片 選 項:
頭 像:
內 容: