What is the best way to write a state machine in C?
I usually write a big switch-case statement in a for(;;), with callbacks to re-enter the state machine when an external operation is finished.
Do you know a more efficient way?
views:
518answers:
7That's pretty much the standard approach. If you're interested in studying a well considered library and comparing specifics, take a look at Ragel:
Ragel compiles executable finite state machines from regular languages. Ragel targets C, C++, Objective-C, D, Java and Ruby. Ragel state machines can not only recognize byte sequences as regular expression machines do, but can also execute code at arbitrary points in the recognition of a regular language. Code embedding is done using inline operators that do not disrupt the regular language syntax.
I like the Quantum Leaps approach.
The current state is a pointer to a function that takes an event object as argument. When an event happens, just call the state function with that event; The function can then do its work and transition to another state by just setting the state to another function.
E.g.:
// State type and variable, notice that it's a function pointer.
typedef void (*State)(int);
State state;
// A couple of state functions.
void state_xyz(int event) { /*...*/ }
void state_init(int event) {
if (event == E_GO_TO_xyz) {
// State transition done simply by changing the state to another function.
state = state_xyz;
}
}
// main contains the event loop here:
int main() {
int e;
// Initial state.
state = state_init;
// Receive event, dispatch it, repeat... No 'switch'!
while ((e = wait_for_event()) != E_END) {
state(e);
}
return 0;
}
The QL frameworks provides helpers for extra things like entry/exit/init actions, hierarchical state machines, etc. I highly recommend the book for a deeper explanation and good implementation of this.
The best way is largely subjective, but a common way is to use a "table-based" approach where you map state codes (enums or some other integral type) to function pointers. The function returns your next state and other associated data and you loop through this until the terminal state is reached. This might in fact be what you are describing as your approach above.
An alternative approach is a 2D array that describes for each state/event combination the actions to execute and the next state to go to. This can get trickier to manage when you need to transition to different states depending on 'circumstances', but it can be made to work well. You have an event recognizer function which returns the next event; you have the table where each entry in the table identifies the function to call on receiving the event and the next state to go to - unless the called function overrides that state.
Actually generating such code is fiddlier - it depends on how the FSM is described in the first place. Spotting duplicate actions is often important. Often, you can rely on 'sparse matrix' techniques that do not record error handling explicitly: if the entry logically exists in the sparse matrix, you act on that event/state information, but if the entry does not exist you fall back onto appropriate error reporting and resynchronization code.
A 2D array of pointers to structures can be passed into a generic FSM function; the fact that you write a triple-pointer is enough to make you cautious about what is going on. (I wrote one of those back in March 1986 - I don't have the source for that on disk any more, though I do still have a printout of the document that described it.)
Switch statements are a good way to get started, but they tend to get unwieldy when the FSM gets larger.
A couple related (or duplicate) SO questions with great information and ideas:
I used this pattern. http://stackoverflow.com/questions/133214/is-there-a-typical-state-machine-implementation-pattern (check best answer).
But i also add some features
1. Information about previous state.
2. Parameter passing
3. Adding external events like global timeout and "resseting SM"
I found state machines little less cryptic and maintainable.
Anyway, I still think state machines are most difficult and annoying programming task.(I got so far)