दिलचस्प पोस्ट
विशिष्ट वस्तुओं के लिए LINQ के साथ काम नहीं कर रहा है SQLite और साझा वरीयताओं के पेशेवरों और विपक्ष क्या यह सी # 4 में एक सहकारिता बग है? ES6 / 2015 में रिक्त-सुरक्षित संपत्ति पहुंच (और सशर्त असाइनमेंट) निर्धारित हैडर के लिए समायोजित करने के लिए एक HTML एंकर को ऑफसेट करना jQuery अजाक्स फ़ाइल अपलोड करें X86 विधानसभा में शून्य को रजिस्टर करने का सबसे अच्छा तरीका क्या है: xor, mov या और? जावा ईई 6 में जावा वेब अनुप्रयोग बनाने के लिए क्या सीखें? कैसे जांचें कि iframe लोड किया गया है या इसकी सामग्री है? क्या मैं स्प्रिंग-डेटा-रेस्ट / स्प्रिंग-हैटेओज क्लाइंट के स्वरूपण को कस्टम नियंत्रक बना सकता हूँ? मैं एक एनएसएआरआर एनएसडीक्स के साथ कैसे सॉर्ट कर सकता हूँ? सबमिट बटन की तरह देखने के लिए एक एंकर टैग स्टाइल करना क्या ऑफसेट हैइट, क्लाइंट हाईइट, स्क्रॉल हेइट? कमांड लाइन अनुप्रयोग में कीबोर्ड से इनपुट मानचित्रण को प्रत्यय के लिए JSF उपसर्ग बदलने से मुझे सीएसएस पृष्ठभूमि छवियों पर मैपिंग पुन: लागू करने की शक्ति मिलती है

सी राज्य मशीन डिजाइन

मैं मिश्रित सी और सी ++ में एक छोटी सी परियोजना को तैयार कर रहा हूँ मैं अपने कार्यकर्ता धागा में से एक के दिल में एक छोटे-ईश राज्य मशीन का निर्माण कर रहा हूं।

मैं सोच रहा था कि क्या आपके गुरु पर एसई आपकी राज्य-मशीन डिजाइन तकनीकों का हिस्सा होगा।

नोट: मैं मुख्य रूप से परीक्षण और कार्यान्वयन तकनीकों के बाद हूं।

नवीनीकृत: एसओ पर एकत्र हुए सभी महान इनपुट के आधार पर, मैंने इस वास्तुकला पर बसे हैं:

वैकल्पिक शब्द

वेब के समाधान से एकत्रित समाधान "सी राज्य मशीन डिजाइन"

राज्य मशीनें जो मैंने पहले डिज़ाइन की हैं (सी, नहीं सी ++) सभी एक struct एरे और एक लूप के नीचे आते हैं। ढांचे में मूल रूप से एक राज्य और घटना (देखने के लिए) और एक ऐसा कार्य होता है जो नए राज्य को वापस देता है, जैसे कुछ:

 typedef struct { int st; int ev; int (*fn)(void); } tTransition; 

फिर आप अपने राज्यों और घटनाओं को सरल परिभाषित करते हैं ( ANY विशिष्ट मार्कर हैं, नीचे देखें):

 #define ST_ANY -1 #define ST_INIT 0 #define ST_ERROR 1 #define ST_TERM 2 : : #define EV_ANY -1 #define EV_KEYPRESS 5000 #define EV_MOUSEMOVE 5001 

तब आप सभी कार्यों को परिभाषित करते हैं जिन्हें संक्रमण द्वारा कहा जाता है:

 static int GotKey (void) { ... }; static int FsmError (void) { ... }; 

इन सभी कार्यों को कोई चर नहीं लेने और राज्य मशीन के लिए नए राज्य को वापस करने के लिए लिखा जाता है। इस उदाहरण में ग्लोबल वैरिएबल का उपयोग किसी भी जानकारी को राज्य कार्यों में पारित करने के लिए किया जाता है जहां आवश्यक हो।

ग्लोबल्स का इस्तेमाल करना उतना बुरा नहीं है जितना लगता है क्योंकि एफएसएम आमतौर पर एक संकलन इकाई के अंदर बंद हो जाता है और सभी चर उस यूनिट के स्थैतिक होते हैं (यही वजह है कि मैंने ऊपर "ग्लोबल" के आसपास कोटेशन का इस्तेमाल किया – वे और अधिक में साझा किए गए हैं वास्तव में ग्लोबल की तुलना में एफएसएम) सभी विश्व के रूप में, देखभाल की आवश्यकता है

संक्रमण सरणी तब सभी संभव बदलावों को परिभाषित करता है और उन कार्यों को परिभाषित करता है जो उन बदलावों के लिए बुलाते हैं (पकड़-सभी पिछले एक सहित):

 tTransition trans[] = { { ST_INIT, EV_KEYPRESS, &GotKey}, : : { ST_ANY, EV_ANY, &FsmError} }; #define TRANS_COUNT (sizeof(trans)/sizeof(*trans)) 

इसका क्या मतलब है: यदि आप ST_INIT स्थिति में हैं और आपको EV_KEYPRESS ईवेंट प्राप्त होता है, तो GotKey को कॉल करें।

एफएसएम की कार्यप्रणाली अपेक्षाकृत सरल लूप बन जाती है:

 state = ST_INIT; while (state != ST_TERM) { event = GetNextEvent(); for (i = 0; i < TRANS_COUNT; i++) { if ((state == trans[i].st) || (ST_ANY == trans[i].st)) { if ((event == trans[i].ev) || (EV_ANY == trans[i].ev)) { state = (trans[i].fn)(); break; } } } } 

जैसा कि ऊपर बताया गया है, ST_ANY के उपयोग को वाइल्ड-कार्ड के रूप में ST_ANY हैं, जिससे किसी ईवेंट को फ़ंक्शन कॉल करने की इजाजत दे सकती है, इससे कोई फर्क नहीं पड़ता कि वर्तमान स्थिति EV_ANY भी इसी तरह कार्य करता है, किसी विशेष स्थिति में कोई भी समारोह किसी फ़ंक्शन को कॉल करने की अनुमति देता है।

यह गारंटी भी दे सकता है कि, यदि आप संक्रमण सरणी के अंत तक पहुंचते हैं, तो आपको यह पता ST_ANY/EV_ANY कोई त्रुटि मिलती है कि आपका एफएसएम सही तरीके से नहीं बनाया गया है ( ST_ANY/EV_ANY संयोजन का उपयोग करके

मैंने बहुत सारे संचार परियोजनाओं पर इस तरह के कोड का उपयोग किया है, जैसे कि एम्बेडेड सिस्टम के लिए संचार ढेर के प्रारंभिक कार्यान्वयन और प्रोटोकॉल। बदलाव का सरणी बदलने में सादगी और सापेक्ष आसानी से बड़ा लाभ था।

मुझे इसमें कोई संदेह नहीं है कि आजकल उच्च स्तर के अस्थिरताएं हो सकती हैं जो आजकल अधिक उपयुक्त हो सकती हैं लेकिन मुझे संदेह है कि वे सभी इस प्रकार की संरचना को उबालेंगे।


और, जैसा कि एक टिप्पणी में ldog कहते हैं, आप सभी फ़ंक्शंस के लिए एक संरचना संकेतक पास करके और पूरी तरह से ldog से बच सकते हैं (और ईवेंट लूप में उस का उपयोग करके)। इससे कई राज्य मशीनों को हस्तक्षेप के बिना साइड-बाय-साइड चलाने की अनुमति मिलेगी।

सिर्फ एक संरचना प्रकार बनाओ जिसमें मशीन-विशिष्ट डेटा (नंगे न्यूनतम पर राज्य) रखता है और इसका उपयोग विश्व स्तर के बजाय।

इसके कारण मैंने शायद ही ऐसा किया है, क्योंकि यह केवल इसलिए है क्योंकि मैंने जो राज्य मशीनें लिखी हैं, उनमें सिंगलटन प्रकार हैं (उदाहरण के लिए एक-ऑफ, प्रोसेस-शुरू, विन्यास फाइल पढ़ने), एक से अधिक उदाहरणों को चलाने की आवश्यकता नहीं है । लेकिन अगर आपको एक से ज्यादा चलना पड़ता है, तो इसका मूल्य होता है

अन्य जवाब अच्छे हैं, लेकिन एक बहुत ही "हल्के" कार्यान्वयन मैंने उपयोग किया है जब राज्य मशीन बहुत सरल दिखती है:

 enum state { ST_NEW, ST_OPEN, ST_SHIFT, ST_END }; enum state current_state = ST_NEW; while (current_state != ST_END) { input = get_input(); switch (current_state) { case ST_NEW: /* Do something with input and set current_state */ break; case ST_OPEN: /* Do something different and set current_state */ break; /* ... etc ... */ } } 

मैं इसका उपयोग तब करता हूं जब राज्य मशीन काफी सरल है कि फ़ंक्शन पॉइंटर और राज्य संक्रमण तालिका दृष्टिकोण अतिप्राप्ति है। यह अक्सर वर्ण-दर-वर्ण या शब्द-दर-शब्द पार्सिंग के लिए उपयोगी होता है।

कंप्यूटर विज्ञान में हर नियम को तोड़ने के लिए मुझे माफ़ करें, लेकिन एक राज्य मशीन कुछ में से एक है (मैं केवल दो हाथों में ही गिन सकता हूं) जहां goto स्टेटमेंट केवल अधिक कुशल नहीं है, बल्कि आपके कोड क्लीनर और पढ़ने में आसान भी है। चूंकि goto स्टेटमेंट लॉबल्स पर आधारित हैं, आप संख्याओं की गड़बड़ी का ट्रैक रखने या एक एनएएम का उपयोग करने के बजाय अपनी राज्यों को नाम दे सकते हैं। यह बहुत क्लीनर कोड बनाता है क्योंकि आपको फंक्शन पॉइंटर या विशाल स्विच स्टेटमेंट की अतिरिक्त अतिरिक्त कसौटी की जरूरत नहीं है और लूप के दौरान। क्या मैंने उल्लेख किया है कि यह अधिक कुशल भी है?

यहाँ एक राज्य मशीन की तरह लग सकता है:

 void state_machine() { first_state: // Do some stuff here switch(some_var) { case 0: goto first_state; case 1: goto second_state; default: return; } second_state: // Do some stuff here switch(some_var) { case 0: goto first_state; case 1: goto second_state; default: return; } } 

आप सामान्य विचार प्राप्त करते हैं मुद्दा यह है कि आप राज्य मशीन को एक प्रभावी ढंग से लागू कर सकते हैं और जो पाठक पर पढ़ना और चिल्लाना अपेक्षाकृत आसान है, वे राज्य मशीन को देख रहे हैं। ध्यान दें कि यदि आप goto स्टेटमेंट्स का प्रयोग कर रहे हैं, तो आपको अभी भी सावधान रहना चाहिए क्योंकि ऐसा करने के दौरान पैर में खुद को शूट करना बहुत आसान है।

आप राज्य मशीन कंपाइलर http://smc.sourceforge.net/ पर विचार कर सकते हैं

यह शानदार खुला स्रोत उपयोगिता एक साधारण भाषा में एक राज्य मशीन का विवरण स्वीकार करती है और इसे सी और सी ++ सहित – किसी दर्जन या किसी भी एक भाषा में संकलित करता है उपयोगिता स्वयं ही जावा में लिखा है, और इसे बिल्ड के भाग के रूप में शामिल किया जा सकता है।

गोफ राज्य पैटर्न या किसी अन्य दृष्टिकोण का उपयोग करते हुए हाथ कोडिंग की बजाय यह करने का कारण यह है कि एक बार जब आपका राज्य मशीन कोड के रूप में व्यक्त किया जाता है, तो अंतर्निहित संरचना बॉयलरप्लेट के वजन के नीचे गायब हो जाती है जिसे इसे समर्थन देने के लिए तैयार करने की आवश्यकता होती है। इस दृष्टिकोण का उपयोग आपको चिंता का एक उत्कृष्ट अलगाव देता है, और आप अपने राज्य मशीन 'दृश्यमान' की संरचना को बनाए रखते हैं। ऑटो-जनरेटेड कोड मॉड्यूल में जाता है जिसे आपको स्पर्श करने की आवश्यकता नहीं होती है, ताकि आप वापस लिखे गए समर्थन कोड को प्रभावित किए बिना राज्य मशीन की संरचना के साथ वापस जा सकते हैं और बेला कर सकते हैं।

क्षमा करें, मैं अति उत्साही हूं, और निश्चित रूप से हर किसी को बंद करना लेकिन यह एक शीर्ष पायदान उपयोगिता है, और अच्छी तरह से प्रलेखित भी है।

मिरो सैकिक (ब्लॉग स्टेट स्पेस , वेबसाइट राज्य मशीन और उपकरण ) के काम की जांच सुनिश्चित करें, जिनके लेख सी / सी ++ उपयोगकर्ता जर्नल में बहुत अच्छे थे।

वेबसाइट में ओपन सोर्स और एक राज्य मशीन फ्रेमवर्क (क्यूपी फ्रेमवर्क) , एक इवेंट हैंडलर (क्यूईपी) , एक बुनियादी मॉडलिंग टूल (क्यूएम) और एक ट्रेसिंग टूल (क्यूएसपी) दोनों के एक पूर्ण (सी / सी ++) कार्यान्वयन शामिल है। राज्य मशीनों को आकर्षित करने, कोड बनाने और उन्हें डिबग करने की अनुमति दें

इस पुस्तक में कार्यान्वयन और उसका उपयोग कैसे किया गया है और किस प्रकार का उपयोग करना है और हिआरिक और परिमित राज्य मशीनों के आधारभूत तत्वों को समझने के लिए भी महान सामग्री पर एक विस्तृत व्याख्या शामिल है।

वेबसाइट में एम्बेडेड प्लेटफ़ॉर्म के साथ सॉफ़्टवेयर के उपयोग के लिए कई बोर्ड सहायता पैकेज के लिंक भी शामिल हैं।

मैंने कुछ ऐसा किया है जो पक्सडीएब्लो का वर्णन करता है, केवल राज्य / घटना बदलावों की एक सरणी के बजाय, मैंने समारोह बिंदुओं की एक 2-आयामी सरणी की स्थापना की है, जिसमें एक अक्ष का सूचक और वर्तमान राज्य मान के रूप में ईवेंट मान है अन्य। तब मैं सिर्फ state = state_table[event][state](params) कॉल करता हूं और सही काम होता है। अमान्य राज्य / घटना संयोजनों का प्रतिनिधित्व करने वाले कक्ष एक फ़ंक्शन को एक फ़ंक्शन कहते हैं जो ऐसा कहता है, ज़ाहिर है।

जाहिर है, यह केवल तभी काम करता है जब राज्य और ईवेंट मान दोनों निकटतम सीमाएं हैं और 0 से शुरू करते हैं या पर्याप्त रूप से बंद होते हैं

एक बहुत अच्छा टेम्पलेट-आधारित सी ++ राज्य मशीन "फ्रेमवर्क" अपने लेख में स्टीफन Heinzmann द्वारा दिया जाता है

चूंकि लेख में एक पूर्ण कोड डाउनलोड करने के लिए कोई लिंक नहीं है, इसलिए मैंने एक परियोजना में कोड को चिपकाने के लिए स्वतंत्रता ली है और इसे जांचें। नीचे दिए गए सामान का परीक्षण किया गया है और इसमें कुछ छोटी लेकिन काफी स्पष्ट गायब टुकड़े शामिल हैं।

यहां प्रमुख नवाचार यह है कि संकलक बहुत कुशल कोड पैदा कर रहा है। रिक्त प्रविष्टि / निकास कार्यों की कोई लागत नहीं है गैर रिक्त प्रविष्टि / निकास क्रियाएं इनलाइन हैं कंपाइलर राज्यचर्च की पूर्णता की पुष्टि भी कर रहा है। अनुपलब्ध कार्रवाइयां लिंकिंग त्रुटियां उत्पन्न करती हैं केवल एक चीज जो पकड़ा नहीं है वह लापता Top::init

यह मिरो साबिक के कार्यान्वयन के लिए एक बहुत ही अच्छा विकल्प है, यदि आप जो खो गए हैं बिना जी सकते हैं – यह एक पूर्ण यूएमएल स्टेटचार्ट कार्यान्वयन से काफी दूर है, हालांकि यह सही ढंग से यूएमएल सिमेंटिक्स का कार्यान्वयन करता है, जबकि डिजाइन के द्वारा Samek का कोड बाहर निकलने / संक्रमण को संभाल नहीं करता है / सही कार्रवाई में प्रविष्टियां

यदि यह कोड आपके लिए क्या काम करता है, और आपके पास आपके सिस्टम के लिए एक सभ्य C ++ कंपाइलर है, तो यह शायद मिरो के सी / सी ++ कार्यान्वयन से बेहतर प्रदर्शन करेगा कंपाइलर आपके लिए एक चपटा, हे (1) संक्रमण राज्य मशीन कार्यान्वयन उत्पन्न करता है अगर विधानसभा निर्गम के ऑडिट की पुष्टि करता है कि अनुकूलन वांछित के रूप में काम करती है, तो आप सैद्धांतिक प्रदर्शन के करीब आ जाते हैं। सर्वश्रेष्ठ भाग: यह अपेक्षाकृत छोटा है, कोड को समझना आसान है।

 #ifndef HSM_HPP #define HSM_HPP // This code is from: // Yet Another Hierarchical State Machine // by Stefan Heinzmann // Overload issue 64 december 2004 // http://accu.org/index.php/journals/252 /* This is a basic implementation of UML Statecharts. * The key observation is that the machine can only * be in a leaf state at any given time. The composite * states are only traversed, never final. * Only the leaf states are ever instantiated. The composite * states are only mechanisms used to generate code. They are * never instantiated. */ // Helpers // A gadget from Herb Sutter's GotW #71 -- depends on SFINAE template<class D, class B> class IsDerivedFrom { class Yes { char a[1]; }; class No { char a[10]; }; static Yes Test(B*); // undefined static No Test(...); // undefined public: enum { Res = sizeof(Test(static_cast<D*>(0))) == sizeof(Yes) ? 1 : 0 }; }; template<bool> class Bool {}; // Top State, Composite State and Leaf State template <typename H> struct TopState { typedef H Host; typedef void Base; virtual void handler(Host&) const = 0; virtual unsigned getId() const = 0; }; template <typename H, unsigned id, typename B> struct CompState; template <typename H, unsigned id, typename B = CompState<H, 0, TopState<H> > > struct CompState : B { typedef B Base; typedef CompState<H, id, Base> This; template <typename X> void handle(H& h, const X& x) const { Base::handle(h, x); } static void init(H&); // no implementation static void entry(H&) {} static void exit(H&) {} }; template <typename H> struct CompState<H, 0, TopState<H> > : TopState<H> { typedef TopState<H> Base; typedef CompState<H, 0, Base> This; template <typename X> void handle(H&, const X&) const {} static void init(H&); // no implementation static void entry(H&) {} static void exit(H&) {} }; template <typename H, unsigned id, typename B = CompState<H, 0, TopState<H> > > struct LeafState : B { typedef H Host; typedef B Base; typedef LeafState<H, id, Base> This; template <typename X> void handle(H& h, const X& x) const { Base::handle(h, x); } virtual void handler(H& h) const { handle(h, *this); } virtual unsigned getId() const { return id; } static void init(H& h) { h.next(obj); } // don't specialize this static void entry(H&) {} static void exit(H&) {} static const LeafState obj; // only the leaf states have instances }; template <typename H, unsigned id, typename B> const LeafState<H, id, B> LeafState<H, id, B>::obj; // Transition Object template <typename C, typename S, typename T> // Current, Source, Target struct Tran { typedef typename C::Host Host; typedef typename C::Base CurrentBase; typedef typename S::Base SourceBase; typedef typename T::Base TargetBase; enum { // work out when to terminate template recursion eTB_CB = IsDerivedFrom<TargetBase, CurrentBase>::Res, eS_CB = IsDerivedFrom<S, CurrentBase>::Res, eS_C = IsDerivedFrom<S, C>::Res, eC_S = IsDerivedFrom<C, S>::Res, exitStop = eTB_CB && eS_C, entryStop = eS_C || eS_CB && !eC_S }; // We use overloading to stop recursion. // The more natural template specialization // method would require to specialize the inner // template without specializing the outer one, // which is forbidden. static void exitActions(Host&, Bool<true>) {} static void exitActions(Host&h, Bool<false>) { C::exit(h); Tran<CurrentBase, S, T>::exitActions(h, Bool<exitStop>()); } static void entryActions(Host&, Bool<true>) {} static void entryActions(Host& h, Bool<false>) { Tran<CurrentBase, S, T>::entryActions(h, Bool<entryStop>()); C::entry(h); } Tran(Host & h) : host_(h) { exitActions(host_, Bool<false>()); } ~Tran() { Tran<T, S, T>::entryActions(host_, Bool<false>()); T::init(host_); } Host& host_; }; // Initializer for Compound States template <typename T> struct Init { typedef typename T::Host Host; Init(Host& h) : host_(h) {} ~Init() { T::entry(host_); T::init(host_); } Host& host_; }; #endif // HSM_HPP 

टेस्ट कोड निम्नानुसार है।

 #include <cstdio> #include "hsm.hpp" #include "hsmtest.hpp" /* Implements the following state machine from Miro Samek's * Practical Statecharts in C/C++ * * |-init-----------------------------------------------------| * | s0 | * |----------------------------------------------------------| * | | * | |-init-----------| |-------------------------| | * | | s1 |---c--->| s2 | | * | |----------------|<--c----|-------------------------| | * | | | | | | * |<-d-| |-init-------| | | |-init----------------| | | * | | | s11 |<----f----| | s21 | | | * | /--| |------------| | | |---------------------| | | * | a | | | | | | | | | * | \->| | |------g--------->|-init------| | | | * | | |____________| | | |-b->| s211 |---g--->| * | |----b---^ |------f------->| | | | | * | |________________| | |<-d-|___________|<--e----| * | | |_____________________| | | * | |_________________________| | * |__________________________________________________________| */ class TestHSM; typedef CompState<TestHSM,0> Top; typedef CompState<TestHSM,1,Top> S0; typedef CompState<TestHSM,2,S0> S1; typedef LeafState<TestHSM,3,S1> S11; typedef CompState<TestHSM,4,S0> S2; typedef CompState<TestHSM,5,S2> S21; typedef LeafState<TestHSM,6,S21> S211; enum Signal { A_SIG, B_SIG, C_SIG, D_SIG, E_SIG, F_SIG, G_SIG, H_SIG }; class TestHSM { public: TestHSM() { Top::init(*this); } ~TestHSM() {} void next(const TopState<TestHSM>& state) { state_ = &state; } Signal getSig() const { return sig_; } void dispatch(Signal sig) { sig_ = sig; state_->handler(*this); } void foo(int i) { foo_ = i; } int foo() const { return foo_; } private: const TopState<TestHSM>* state_; Signal sig_; int foo_; }; bool testDispatch(char c) { static TestHSM test; if (c<'a' || 'h'<c) { return false; } printf("Signal<-%c", c); test.dispatch((Signal)(c-'a')); printf("\n"); return true; } int main(int, char**) { testDispatch('a'); testDispatch('e'); testDispatch('e'); testDispatch('a'); testDispatch('h'); testDispatch('h'); return 0; } #define HSMHANDLER(State) \ template<> template<typename X> inline void State::handle(TestHSM& h, const X& x) const HSMHANDLER(S0) { switch (h.getSig()) { case E_SIG: { Tran<X, This, S211> t(h); printf("s0-E;"); return; } default: break; } return Base::handle(h, x); } HSMHANDLER(S1) { switch (h.getSig()) { case A_SIG: { Tran<X, This, S1> t(h); printf("s1-A;"); return; } case B_SIG: { Tran<X, This, S11> t(h); printf("s1-B;"); return; } case C_SIG: { Tran<X, This, S2> t(h); printf("s1-C;"); return; } case D_SIG: { Tran<X, This, S0> t(h); printf("s1-D;"); return; } case F_SIG: { Tran<X, This, S211> t(h); printf("s1-F;"); return; } default: break; } return Base::handle(h, x); } HSMHANDLER(S11) { switch (h.getSig()) { case G_SIG: { Tran<X, This, S211> t(h); printf("s11-G;"); return; } case H_SIG: if (h.foo()) { printf("s11-H"); h.foo(0); return; } break; default: break; } return Base::handle(h, x); } HSMHANDLER(S2) { switch (h.getSig()) { case C_SIG: { Tran<X, This, S1> t(h); printf("s2-C"); return; } case F_SIG: { Tran<X, This, S11> t(h); printf("s2-F"); return; } default: break; } return Base::handle(h, x); } HSMHANDLER(S21) { switch (h.getSig()) { case B_SIG: { Tran<X, This, S211> t(h); printf("s21-B;"); return; } case H_SIG: if (!h.foo()) { Tran<X, This, S21> t(h); printf("s21-H;"); h.foo(1); return; } break; default: break; } return Base::handle(h, x); } HSMHANDLER(S211) { switch (h.getSig()) { case D_SIG: { Tran<X, This, S21> t(h); printf("s211-D;"); return; } case G_SIG: { Tran<X, This, S0> t(h); printf("s211-G;"); return; } } return Base::handle(h, x); } #define HSMENTRY(State) \ template<> inline void State::entry(TestHSM&) { \ printf(#State "-ENTRY;"); \ } HSMENTRY(S0) HSMENTRY(S1) HSMENTRY(S11) HSMENTRY(S2) HSMENTRY(S21) HSMENTRY(S211) #define HSMEXIT(State) \ template<> inline void State::exit(TestHSM&) { \ printf(#State "-EXIT;"); \ } HSMEXIT(S0) HSMEXIT(S1) HSMEXIT(S11) HSMEXIT(S2) HSMEXIT(S21) HSMEXIT(S211) #define HSMINIT(State, InitState) \ template<> inline void State::init(TestHSM& h) { \ Init<InitState> i(h); \ printf(#State "-INIT;"); \ } HSMINIT(Top, S0) HSMINIT(S0, S1) HSMINIT(S1, S11) HSMINIT(S2, S21) HSMINIT(S21, S211) 

तकनीक मैं राज्य मशीनों (प्रोग्राम नियंत्रण के लिए कम से कम) के लिए पसंद करती हूं फ़ंक्शन पॉइंटर्स का उपयोग करना है। प्रत्येक राज्य का एक अलग फ़ंक्शन द्वारा प्रतिनिधित्व किया जाता है। फ़ंक्शन इनपुट प्रतीक लेता है और अगले स्थिति के लिए फ़ंक्शन पॉइंटर को देता है। केंद्रीय प्रेषण पाश मॉनिटर अगले इनपुट लेता है, इसे वर्तमान स्थिति में फ़ीड करता है, और परिणाम को संसाधित करता है।

इसके पर टाइपिंग थोड़ा अजीब हो जाता है, क्योंकि सी में फ़ंक्शन पॉइंटर्स के प्रकार वापस लेने के तरीके को इंगित करने का कोई तरीका नहीं है, इसलिए राज्य void* । लेकिन आप ऐसा कुछ कर सकते हैं:

 typedef void* (*state_handler)(input_symbol_t); void dispatch_fsm() { state_handler current = initial_handler; /* Let's assume returning null indicates end-of-machine */ while (current) { current = current(get_input); } } 

तब आपके व्यक्तिगत राज्य फ़ंक्शंस उनके इनपुट पर स्विच कर सकते हैं और उचित मूल्य को वापस कर सकते हैं।

सरल मामला

 enum event_type { ET_THIS, ET_THAT }; union event_parm { uint8_t this; uint16_t that; } static void handle_event(enum event_type event, union event_parm parm) { static enum { THIS, THAT } state; switch (state) { case THIS: switch (event) { case ET_THIS: // Handle event. break; default: // Unhandled events in this state. break; } break; case THAT: // Handle state. break; } } 

अंक: राज्य निजीकरण नहीं है, न केवल संकलन इकाई के लिए बल्कि ईवेंट_हैंडलर के लिए भी है। मुख्य स्विच से अलग-अलग मामलों को अलग-अलग संभाला जा सकता है जो आवश्यक समझा जा सकता है।

अधिक जटिल मामला

जब स्विच पूरी तरह से कुछ स्क्रीन से बड़ा हो जाता है, तो इसे फ़ंक्शन में विभाजित करता है जो प्रत्येक राज्य को संभालता है, फ़ंक्शन को सीधे देखने के लिए एक राज्य तालिका का उपयोग कर। राज्य अभी भी ईवेंट हैंडलर के लिए निजी है राज्य हैंडलर कार्य अगले राज्य वापस आते हैं। यदि आवश्यक हो तो कुछ ईवेंट मुख्य ईवेंट हैंडलर में विशेष उपचार प्राप्त कर सकते हैं। मैं राज्य प्रविष्टि और बाहर निकलने के लिए छद्म-घटनाओं में फेंकना चाहता हूं और संभवत: राज्य मशीन शुरू होती है:

 enum state_type { THIS, THAT, FOO, NA }; enum event_type { ET_START, ET_ENTER, ET_EXIT, ET_THIS, ET_THAT, ET_WHATEVER, ET_TIMEOUT }; union event_parm { uint8_t this; uint16_t that; }; static void handle_event(enum event_type event, union event_parm parm) { static enum state_type state; static void (* const state_handler[])(enum event_type event, union event_parm parm) = { handle_this, handle_that }; enum state_type next_state = state_handler[state](event, parm); if (NA != next_state && state != next_state) { (void)state_handler[state](ET_EXIT, 0); state = next_state; (void)state_handler[state](ET_ENTER, 0); } } 

मुझे यकीन नहीं है कि मैं सिंटैक्स को खारिज कर रहा हूं, खासकर फ़ंक्शन पॉइंटर्स की सरणी के संबंध में। मैंने किसी भी एक कंपाइलर के माध्यम से इसे नहीं चलाया है समीक्षा के बाद, मैंने देखा कि मैं state_handler ()) को कॉल करने से पहले छद्म घटनाओं ((शून्य) कोष्ठक को संभालने पर स्पष्ट रूप से अगली स्थिति त्यागने में भूल गया था। यह ऐसा कुछ है जो मुझे करना पसंद है, भले ही संकलक चुप्पी से स्वीकार करते हैं। यह कोड के पाठकों को बताता है कि "हां, वास्तव में वापसी मूल्य का उपयोग किए बिना फ़ंक्शन को कॉल करने का मतलब था", और यह इसके बारे में चेतावनी से स्थिर विश्लेषण उपकरणों को रोक सकता है। यह अलग-अलग हो सकता है क्योंकि मुझे याद नहीं है कि ऐसा करने से किसी और को भी देखा जा रहा है।

अंक: जटिलता का एक छोटा सा जोड़ (जांच रहा है कि अगले राज्य वर्तमान से भिन्न है या नहीं), कहीं से डुप्लिकेट कोड से बचा सकता है, क्योंकि राज्य हैंडलर कार्य छद्म घटनाओं का आनंद ले सकते हैं, जब कोई राज्य दर्ज हो जाता है और छोड़ दिया जाता है। याद रखें कि छद्म घटनाओं को संभालने पर राज्य बदल नहीं सकता है, क्योंकि इन घटनाओं के बाद राज्य हैंडलर का नतीजा निकला है। आप निश्चित रूप से व्यवहार को संशोधित करना चुन सकते हैं।

एक राज्य हैंडलर ऐसा दिखेगा:

 static enum state_type handle_this(enum event_type event, union event_parm parm) { enum state_type next_state = NA; switch (event) { case ET_ENTER: // Start a timer to do whatever. // Do other stuff necessary when entering this state. break; case ET_WHATEVER: // Switch state. next_state = THAT; break; case ET_TIMEOUT: // Switch state. next_state = FOO; break; case ET_EXIT: // Stop the timer. // Generally clean up this state. break; } return next_state; } 

अधिक जटिलता

जब संकलन इकाई बहुत बड़ी हो जाती है (जो कुछ भी आपको लगता है, मुझे 1000 लाइनों के बारे में कहना चाहिए), प्रत्येक राज्य हैंडलर को एक अलग फ़ाइल में डाल दिया जाए जब प्रत्येक राज्य हैंडलर कुछ स्क्रीन से अधिक लंबा हो जाता है, प्रत्येक ईवेंट को एक अलग फ़ंक्शन में विभाजित करता है, जिस तरह से राज्य स्विच विभाजित किया गया था। आप यह कई तरह से कर सकते हैं, अलग से राज्य से या एक आम तालिका का उपयोग करके, या विभिन्न योजनाओं के संयोजन कर सकते हैं। उनमें से कुछ अन्य लोगों द्वारा यहां शामिल किए गए हैं। अपनी टेबल सॉर्ट करें और द्विआधारी खोज का उपयोग करें यदि गति एक आवश्यकता है

सामान्य प्रोग्रामिंग

मुझे सॉर्टिंग टेबल जैसे मुद्दों से निपटने के लिए या यहां तक ​​कि विवरणों से राज्य मशीनों को उत्पन्न करने के लिए पहले प्रोग्राम को पसंद करना चाहिए, जिससे आपको "प्रोग्राम के बारे में प्रोग्राम लिखना" हो सके। मेरा मानना ​​है कि बूस्ट लोग सी ++ टेम्पलेट्स का शोषण कर रहे हैं, लेकिन मुझे सिंटैक्स क्रिप्टिक लगता है।

दो-आयामी तालिकाओं

मैंने पूर्व में राज्य / ईवेंट टेबल का प्रयोग किया है लेकिन मुझे यह कहना होगा कि सरलतम मामलों के लिए मुझे उन्हें आवश्यक नहीं मिल रहा है और मैं स्विच स्टेटमेंट की स्पष्टता और पठनीयता पसंद करता हूं, भले ही यह पिछले एक स्क्रीन को पूरा करता है। अधिक जटिल मामलों के लिए टेबल जल्दी से हाथ से निकल जाते हैं क्योंकि दूसरों ने नोट किया है। मैं यहाँ मौजूद मुहावरों को कई घटनाओं को जोड़ने की अनुमति देता है और जब आप ऐसा महसूस करते हैं, स्मृति खपत की मेज को बनाए रखने के बिना (भले ही यह प्रोग्राम मेमोरी हो) हो।

अस्वीकरण

विशेष आवश्यकताएं इन मुहावरों को कम उपयोगी प्रदान कर सकती हैं, लेकिन मैंने उन्हें बहुत स्पष्ट और रख-रखाव पाया है।

अत्यंत अनचाहे, लेकिन कोड को मजेदार, अब मेरे मूल उत्तर की तुलना में अधिक परिष्कृत संस्करण में; अप-टू-डेट संस्करणों को mercurial.intuxication.org पर पाया जा सकता है:

sm.h

 #ifndef SM_ARGS #error "SM_ARGS undefined: " \ "use '#define SM_ARGS (void)' to get an empty argument list" #endif #ifndef SM_STATES #error "SM_STATES undefined: " \ "you must provide a list of comma-separated states" #endif typedef void (*sm_state) SM_ARGS; static const sm_state SM_STATES; #define sm_transit(STATE) ((sm_state (*) SM_ARGS)STATE) #define sm_def(NAME) \ static sm_state NAME ## _fn SM_ARGS; \ static const sm_state NAME = (sm_state)NAME ## _fn; \ static sm_state NAME ## _fn SM_ARGS 

example.c

 #include <stdio.h> #define SM_ARGS (int i) #define SM_STATES EVEN, ODD #include "sm.h" sm_def(EVEN) { printf("even %i\n", i); return ODD; } sm_def(ODD) { printf("odd %i\n", i); return EVEN; } int main(void) { int i = 0; sm_state state = EVEN; for(; i < 10; ++i) state = sm_transit(state)(i); return 0; } 

एक और दिलचस्प ओपन सोर्स टूल राज्यचर्च.ऑर्ग पर यकीन्दू स्टेटचार्ट टूल है । यह हेरल स्टेटचार्ट्स का उपयोग करता है और इस तरह से पदानुक्रमित और समानांतर राज्य प्रदान करता है और सी और सी ++ (साथ ही जावा) कोड उत्पन्न करता है। यह पुस्तकालयों का उपयोग नहीं करता है लेकिन 'सादे कोड' दृष्टिकोण का पालन करता है कोड मूल रूप से स्विच-केस संरचनाओं को लागू करता है। कोड जनरेटर भी अनुकूलित किया जा सकता है। इसके अतिरिक्त उपकरण अन्य कई विशेषताएं प्रदान करता है

इस देर से (सामान्य रूप से) आ रहा है, लेकिन आज के जवाबों को स्कैन करना मैं सोचता हूं कि कुछ महत्वपूर्ण याद आ रही है;

मुझे अपनी अपनी परियोजनाओं में पाया गया है कि हर मान्य राज्य / घटना संयोजन के लिए कोई फ़ंक्शन नहीं होने के लिए बहुत उपयोगी हो सकता है। I do like the idea of effectively having a 2D table of states/events. But I like the table elements to be more than a simple function pointer. Instead I try to organize my design so at it's heart it comprises a bunch of simple atomic elements or actions. That way I can list those simple atomic elements at each intersection of my state/event table. The idea is that you don't have to define a mass of N squared (typically very simple) functions. Why have something so error-prone, time consuming, hard to write, hard to read, you name it ?

I also include an optional new state, and an optional function pointer for each cell in the table. The function pointer is there for those exceptional cases where you don't want to just fire off a list of atomic actions.

You know you are doing it right when you can express a lot of different functionality, just by editing your table, with no new code to write.

Alrght, I think mine's just a little different from everybody else's. A little more separation of code and data than I see in the other answers. I really read up on the theory to write this, which implements a full Regular-language (without regular expressions, sadly). Ullman, Minsky, Chomsky. Can't say I understood it all, but I've drawn from the old masters as directly as possible: through their words.

I use a function pointer to a predicate that determines the transition to a 'yes' state or a 'no' state. This facilitates the creation of a finite state acceptor for a regular language that you program in a more assembly-language-like manner. Please don't be put-off by my silly name choices. 'czek' == 'check'. 'grok' == [go look it up in the Hacker Dictionary].

So for each iteration, czek calls a predicate function with the current character as argument. If the predicate returns true, the character is consumed (the pointer advanced) and we follow the 'y' transition to select the next state. If the predicate returns false, the character is NOT consumed and we follow the 'n' transition. So every instruction is a two-way branch! I must have been reading The Story of Mel at the time.

This code comes straight from my postscript interpreter , and evolved into its current form with much guidance from the fellows on comp.lang.c. Since postscript basically has no syntax (only requiring balanced brackets), a Regular Language Accepter like this functions as the parser as well.

 /* currentstr is set to the start of string by czek and used by setrad (called by israd) to set currentrad which is used by israddig to determine if the character in question is valid for the specified radix -- a little semantic checking in the syntax! */ char *currentstr; int currentrad; void setrad(void) { char *end; currentrad = strtol(currentstr, &end, 10); if (*end != '#' /* just a sanity check, the automaton should already have determined this */ || currentrad > 36 || currentrad < 2) fatal("bad radix"); /* should probably be a simple syntaxerror */ } /* character classes used as tests by automatons under control of czek */ char *alpha = "0123456789" "ABCDE" "FGHIJ" "KLMNO" "PQRST" "UVWXYZ"; #define EQ(a,b) a==b #define WITHIN(a,b) strchr(a,b)!=NULL int israd (int c) { if (EQ('#',c)) { setrad(); return true; } return false; } int israddig(int c) { return strchrnul(alpha,toupper(c))-alpha <= currentrad; } int isdot (int c) {return EQ('.',c);} int ise (int c) {return WITHIN("eE",c);} int issign (int c) {return WITHIN("+-",c);} int isdel (int c) {return WITHIN("()<>[]{}/%",c);} int isreg (int c) {return c!=EOF && !isspace(c) && !isdel(c);} #undef WITHIN #undef EQ /* the automaton type */ typedef struct { int (*pred)(int); int y, n; } test; /* automaton to match a simple decimal number */ /* /^[+-]?[0-9]+$/ */ test fsm_dec[] = { /* 0*/ { issign, 1, 1 }, /* 1*/ { isdigit, 2, -1 }, /* 2*/ { isdigit, 2, -1 }, }; int acc_dec(int i) { return i==2; } /* automaton to match a radix number */ /* /^[0-9]+[#][a-Z0-9]+$/ */ test fsm_rad[] = { /* 0*/ { isdigit, 1, -1 }, /* 1*/ { isdigit, 1, 2 }, /* 2*/ { israd, 3, -1 }, /* 3*/ { israddig, 4, -1 }, /* 4*/ { israddig, 4, -1 }, }; int acc_rad(int i) { return i==4; } /* automaton to match a real number */ /* /^[+-]?(d+(.d*)?)|(d*.d+)([eE][+-]?d+)?$/ */ /* represents the merge of these (simpler) expressions [+-]?[0-9]+\.[0-9]*([eE][+-]?[0-9]+)? [+-]?[0-9]*\.[0-9]+([eE][+-]?[0-9]+)? The complexity comes from ensuring at least one digit in the integer or the fraction with optional sign and optional optionally-signed exponent. So passing isdot in state 3 means at least one integer digit has been found but passing isdot in state 4 means we must find at least one fraction digit via state 5 or the whole thing is a bust. */ test fsm_real[] = { /* 0*/ { issign, 1, 1 }, /* 1*/ { isdigit, 2, 4 }, /* 2*/ { isdigit, 2, 3 }, /* 3*/ { isdot, 6, 7 }, /* 4*/ { isdot, 5, -1 }, /* 5*/ { isdigit, 6, -1 }, /* 6*/ { isdigit, 6, 7 }, /* 7*/ { ise, 8, -1 }, /* 8*/ { issign, 9, 9 }, /* 9*/ { isdigit, 10, -1 }, /*10*/ { isdigit, 10, -1 }, }; int acc_real(int i) { switch(i) { case 2: /* integer */ case 6: /* real */ case 10: /* real with exponent */ return true; } return false; } /* Helper function for grok. Execute automaton against the buffer, applying test to each character: on success, consume character and follow 'y' transition. on failure, do not consume but follow 'n' transition. Call yes function to determine if the ending state is considered an acceptable final state. A transition to -1 represents rejection by the automaton */ int czek (char *s, test *fsm, int (*yes)(int)) { int sta = 0; currentstr = s; while (sta!=-1 && *s) { if (fsm[sta].pred((int)*s)) { sta=fsm[sta].y; s++; } else { sta=fsm[sta].n; } } return yes(sta); } /* Helper function for toke. Interpret the contents of the buffer, trying automatons to match number formats; and falling through to a switch for special characters. Any token consisting of all regular characters that cannot be interpreted as a number is an executable name */ object grok (state *st, char *s, int ns, object *src, int (*next)(state *,object *), void (*back)(state *,int, object *)) { if (czek(s, fsm_dec, acc_dec)) { long num; num = strtol(s,NULL,10); if ((num==LONG_MAX || num==LONG_MIN) && errno==ERANGE) { error(st,limitcheck); /* } else if (num > INT_MAX || num < INT_MIN) { */ /* error(limitcheck, OP_token); */ } else { return consint(num); } } else if (czek(s, fsm_rad, acc_rad)) { long ra,num; ra = (int)strtol(s,NULL,10); if (ra > 36 || ra < 2) { error(st,limitcheck); } num = strtol(strchr(s,'#')+1, NULL, (int)ra); if ((num==LONG_MAX || num==LONG_MIN) && errno==ERANGE) { error(st,limitcheck); /* } else if (num > INT_MAX || num < INT_MAX) { */ /* error(limitcheck, OP_token); */ } else { return consint(num); } } else if (czek(s, fsm_real, acc_real)) { double num; num = strtod(s,NULL); if ((num==HUGE_VAL || num==-HUGE_VAL) && errno==ERANGE) { error(st,limitcheck); } else { return consreal(num); } } else switch(*s) { case '(': { int c, defer=1; char *sp = s; while (defer && (c=next(st,src)) != EOF ) { switch(c) { case '(': defer++; break; case ')': defer--; if (!defer) goto endstring; break; case '\\': c=next(st,src); switch(c) { case '\n': continue; case 'a': c = '\a'; break; case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = '\v'; break; case '\'': case '\"': case '(': case ')': default: break; } } if (sp-s>ns) error(st,limitcheck); else *sp++ = c; } endstring: *sp=0; return cvlit(consstring(st,s,sp-s)); } case '<': { int c; char d, *x = "0123456789abcdef", *sp = s; while (c=next(st,src), c!='>' && c!=EOF) { if (isspace(c)) continue; if (isxdigit(c)) c = strchr(x,tolower(c)) - x; else error(st,syntaxerror); d = (char)c << 4; while (isspace(c=next(st,src))) /*loop*/; if (isxdigit(c)) c = strchr(x,tolower(c)) - x; else error(st,syntaxerror); d |= (char)c; if (sp-s>ns) error(st,limitcheck); *sp++ = d; } *sp = 0; return cvlit(consstring(st,s,sp-s)); } case '{': { object *a; size_t na = 100; size_t i; object proc; object fin; fin = consname(st,"}"); (a = malloc(na * sizeof(object))) || (fatal("failure to malloc"),0); for (i=0 ; objcmp(st,a[i]=toke(st,src,next,back),fin) != 0; i++) { if (i == na-1) (a = realloc(a, (na+=100) * sizeof(object))) || (fatal("failure to malloc"),0); } proc = consarray(st,i); { size_t j; for (j=0; j<i; j++) { a_put(st, proc, j, a[j]); } } free(a); return proc; } case '/': { s[1] = (char)next(st,src); puff(st, s+2, ns-2, src, next, back); if (s[1] == '/') { push(consname(st,s+2)); opexec(st, op_cuts.load); return pop(); } return cvlit(consname(st,s+1)); } default: return consname(st,s); } return null; /* should be unreachable */ } /* Helper function for toke. Read into buffer any regular characters. If we read one too many characters, put it back unless it's whitespace. */ int puff (state *st, char *buf, int nbuf, object *src, int (*next)(state *,object *), void (*back)(state *,int, object *)) { int c; char *s = buf; while (isreg(c=next(st,src))) { if (s-buf >= nbuf-1) return false; *s++ = c; } *s = 0; if (!isspace(c) && c != EOF) back(st,c,src); /* eat interstice */ return true; } /* Helper function for Stoken Ftoken. Read a token from src using next and back. Loop until having read a bona-fide non-whitespace non-comment character. Call puff to read into buffer up to next delimiter or space. Call grok to figure out what it is. */ #define NBUF MAXLINE object toke (state *st, object *src, int (*next)(state *, object *), void (*back)(state *, int, object *)) { char buf[NBUF] = "", *s=buf; int c,sta = 1; object o; do { c=next(st,src); //if (c==EOF) return null; if (c=='%') { if (DUMPCOMMENTS) fputc(c, stdout); do { c=next(st,src); if (DUMPCOMMENTS) fputc(c, stdout); } while (c!='\n' && c!='\f' && c!=EOF); } } while (c!=EOF && isspace(c)); if (c==EOF) return null; *s++ = c; *s = 0; if (!isdel(c)) sta=puff(st, s,NBUF-1,src,next,back); if (sta) { o=grok(st,buf,NBUF-1,src,next,back); return o; } else { return null; } } 

boost.org comes with 2 different state chart implementations:

  • Meta State Machine
  • Statechart

As always, boost will beam you into template hell.

The first library is for more performance-critical state machines. The second library gives you a direct transition path from a UML Statechart to code.

Here's the SO question asking for a comparison between the two where both of the authors respond.

This series of Ars OpenForum posts about a somewhat complicated bit of control logic includes a very easy-to-follow implementation as a state machine in C.

Saw this somewhere

 #define FSM #define STATE(x) s_##x : #define NEXTSTATE(x) goto s_##x FSM { STATE(x) { ... NEXTSTATE(y); } STATE(y) { ... if (x == 0) NEXTSTATE(y); else NEXTSTATE(x); } } 

Given that you imply you can use C++ and hence OO code, I would suggest evaluating the 'GoF'state pattern (GoF = Gang of Four, the guys who wrote the design patterns book which brought design patterns into the limelight).

It is not particularly complex and it is widely used and discussed so it is easy to see examples and explanations on line.

It will also quite likely be recognizable by anyone else maintaining your code at a later date.

If efficiency is the worry, it would be worth actually benchmarking to make sure that a non OO approach is more efficient as lots of factors affect performance and it is not always simply OO bad, functional code good. Similarly, if memory usage is a constraint for you it is again worth doing some tests or calculations to see if this will actually be an issue for your particular application if you use the state pattern.

The following are some links to the 'Gof' state pattern, as Craig suggests:

I really liked paxdiable's answer and decided to implement all the missing features for my application like guard variables and state machine specific data.

I uploaded my implementation to this site to share with the community. It has been tested using IAR Embedded Workbench for ARM.

https://sourceforge.net/projects/compactfsm/

Your question is quite generic,
Here are two reference articles that might be useful,

  1. Embedded State Machine Implementation

    This article describes a simple approach to implementing a state machine for an embedded system. For purposes of this article, a state machine is defined as an algorithm that can be in one of a small number of states. A state is a condition that causes a prescribed relationship of inputs to outputs, and of inputs to next states.
    A savvy reader will quickly note that the state machines described in this article are Mealy machines. A Mealy machine is a state machine where the outputs are a function of both present state and input, as opposed to a Moore machine, in which the outputs are a function only of state.

    • Coding State Machines in C and C++

      My preoccupation in this article is with state-machine fundamentals and some straightforward programming guidelines for coding state machines in C or C++. I hope that these simple techniques can become more common, so that you (and others) can readily see the state-machine structure right from the source code.

I have used State Machine Compiler in Java and Python projects to with success.

This is an old post with lots of answers, but I thought I'd add my own approach to the finite state machine in C. I made a Python script to produce the skeleton C code for any number of states. That script is documented on GituHub at FsmTemplateC

This example is based on other approaches I've read about. It doesn't use goto or switch statements but instead has transition functions in a pointer matrix (look-up table). The code relies on a big multi-line initializer macro and C99 features (designated initializers and compound literals) so if you don't like these things, you might not like this approach.

Here is a Python script of a turnstile example which generates skeleton C-code using FsmTemplateC :

 # dict parameter for generating FSM fsm_param = { # main FSM struct type string 'type': 'FsmTurnstile', # struct type and name for passing data to state machine functions # by pointer (these custom names are optional) 'fopts': { 'type': 'FsmTurnstileFopts', 'name': 'fopts' }, # list of states 'states': ['locked', 'unlocked'], # list of inputs (can be any length > 0) 'inputs': ['coin', 'push'], # map inputs to commands (next desired state) using a transition table # index of array corresponds to 'inputs' array # for this example, index 0 is 'coin', index 1 is 'push' 'transitiontable': { # current state | 'coin' | 'push' | 'locked': ['unlocked', ''], 'unlocked': [ '', 'locked'] } } # folder to contain generated code folder = 'turnstile_example' # function prefix prefix = 'fsm_turnstile' # generate FSM code code = fsm.Fsm(fsm_param).genccode(folder, prefix) 

The generated output header contains the typedefs:

 /* function options (EDIT) */ typedef struct FsmTurnstileFopts { /* define your options struct here */ } FsmTurnstileFopts; /* transition check */ typedef enum eFsmTurnstileCheck { EFSM_TURNSTILE_TR_RETREAT, EFSM_TURNSTILE_TR_ADVANCE, EFSM_TURNSTILE_TR_CONTINUE, EFSM_TURNSTILE_TR_BADINPUT } eFsmTurnstileCheck; /* states (enum) */ typedef enum eFsmTurnstileState { EFSM_TURNSTILE_ST_LOCKED, EFSM_TURNSTILE_ST_UNLOCKED, EFSM_TURNSTILE_NUM_STATES } eFsmTurnstileState; /* inputs (enum) */ typedef enum eFsmTurnstileInput { EFSM_TURNSTILE_IN_COIN, EFSM_TURNSTILE_IN_PUSH, EFSM_TURNSTILE_NUM_INPUTS, EFSM_TURNSTILE_NOINPUT } eFsmTurnstileInput; /* finite state machine struct */ typedef struct FsmTurnstile { eFsmTurnstileInput input; eFsmTurnstileCheck check; eFsmTurnstileState cur; eFsmTurnstileState cmd; eFsmTurnstileState **transition_table; void (***state_transitions)(struct FsmTurnstile *, FsmTurnstileFopts *); void (*run)(struct FsmTurnstile *, FsmTurnstileFopts *, const eFsmTurnstileInput); } FsmTurnstile; /* transition functions */ typedef void (*pFsmTurnstileStateTransitions)(struct FsmTurnstile *, FsmTurnstileFopts *); 
  • enum eFsmTurnstileCheck is used to determine whether a transition was blocked with EFSM_TURNSTILE_TR_RETREAT , allowed to progress with EFSM_TURNSTILE_TR_ADVANCE , or the function call was not preceded by a transition with EFSM_TURNSTILE_TR_CONTINUE .
  • enum eFsmTurnstileState is simply the list of states.
  • enum eFsmTurnstileInput is simply the list of inputs.
  • The FsmTurnstile struct is the heart of the state machine with the transition check, function lookup table, current state, commanded state, and an alias to the primary function that runs the machine.
  • Every function pointer (alias) in FsmTurnstile should only be called from the struct and has to have its first input as a pointer to itself so as to maintain a persistent state, object-oriented style.

Now for the function declarations in the header:

 /* fsm declarations */ void fsm_turnstile_locked_locked (FsmTurnstile *fsm, FsmTurnstileFopts *fopts); void fsm_turnstile_locked_unlocked (FsmTurnstile *fsm, FsmTurnstileFopts *fopts); void fsm_turnstile_unlocked_locked (FsmTurnstile *fsm, FsmTurnstileFopts *fopts); void fsm_turnstile_unlocked_unlocked (FsmTurnstile *fsm, FsmTurnstileFopts *fopts); void fsm_turnstile_run (FsmTurnstile *fsm, FsmTurnstileFopts *fopts, const eFsmTurnstileInput input); 

Function names are in the format {prefix}_{from}_{to} , where {from} is the previous (current) state and {to} is the next state. Note that if the transition table does not allow for certain transitions, a NULL pointer instead of a function pointer will be set. Finally, the magic happens with a macro. Here we build the transition table (matrix of state enums) and the state transition functions look up table (a matrix of function pointers):

 /* creation macro */ #define FSM_TURNSTILE_CREATE() \ { \ .input = EFSM_TURNSTILE_NOINPUT, \ .check = EFSM_TURNSTILE_TR_CONTINUE, \ .cur = EFSM_TURNSTILE_ST_LOCKED, \ .cmd = EFSM_TURNSTILE_ST_LOCKED, \ .transition_table = (eFsmTurnstileState * [EFSM_TURNSTILE_NUM_STATES]) { \ (eFsmTurnstileState [EFSM_TURNSTILE_NUM_INPUTS]) { \ EFSM_TURNSTILE_ST_UNLOCKED, \ EFSM_TURNSTILE_ST_LOCKED \ }, \ (eFsmTurnstileState [EFSM_TURNSTILE_NUM_INPUTS]) { \ EFSM_TURNSTILE_ST_UNLOCKED, \ EFSM_TURNSTILE_ST_LOCKED \ } \ }, \ .state_transitions = (pFsmTurnstileStateTransitions * [EFSM_TURNSTILE_NUM_STATES]) { \ (pFsmTurnstileStateTransitions [EFSM_TURNSTILE_NUM_STATES]) { \ fsm_turnstile_locked_locked, \ fsm_turnstile_locked_unlocked \ }, \ (pFsmTurnstileStateTransitions [EFSM_TURNSTILE_NUM_STATES]) { \ fsm_turnstile_unlocked_locked, \ fsm_turnstile_unlocked_unlocked \ } \ }, \ .run = fsm_turnstile_run \ } 

When creating the FSM, the macro FSM_EXAMPLE_CREATE() has to be used.

Now, in the source code every state transition function declared above should be populated. The FsmTurnstileFopts struct can be used to pass data to/from the state machine. Every transition must set fsm->check to be equal to either EFSM_EXAMPLE_TR_RETREAT to block it from transitioning or EFSM_EXAMPLE_TR_ADVANCE to allow it to transition to the commanded state. A working example can be found at (FsmTemplateC)[ https://github.com/ChisholmKyle/FsmTemplateC%5D .

Here is the very simple actual usage in your code:

 /* create fsm */ FsmTurnstile fsm = FSM_TURNSTILE_CREATE(); /* create fopts */ FsmTurnstileFopts fopts = { .msg = "" }; /* initialize input */ eFsmTurnstileInput input = EFSM_TURNSTILE_NOINPUT; /* main loop */ for (;;) { /* wait for timer signal, inputs, interrupts, whatever */ /* optionally set the input (my_input = EFSM_TURNSTILE_IN_PUSH for example) */ /* run state machine */ my_fsm.run(&my_fsm, &my_fopts, my_input); } 

All that header business and all those functions just to have a simple and fast interface is worth it in my mind.

You could use the open source library OpenFST .

OpenFst is a library for constructing, combining, optimizing, and searching weighted finite-state transducers (FSTs). Weighted finite-state transducers are automata where each transition has an input label, an output label, and a weight. The more familiar finite-state acceptor is represented as a transducer with each transition's input and output label equal. Finite-state acceptors are used to represent sets of strings (specifically, regular or rational sets); finite-state transducers are used to represent binary relations between pairs of strings (specifically, rational transductions). The weights can be used to represent the cost of taking a particular transition.

 void (* StateController)(void); void state1(void); void state2(void); void main() { StateController=&state1; while(1) { (* StateController)(); } } void state1(void) { //do something in state1 StateController=&state2; } void state2(void) { //do something in state2 //Keep changing function direction based on state transition StateController=&state1; } 

I personally use self referencing structs in combination with pointer arrays. I uploaded a tutorial on github a while back, link:

https://github.com/mmelchger/polling_state_machine_c

Note: I do realise that this thread is quite old, but I hope to get input and thoughts on the design of the state-machine as well as being able to provide an example for a possible state-machine design in C.