#define flip 1 #define flop 2 int flipflop_state; switch (flipflop_state) { case flip: do_flip_action; if (condition_to_toggle == true) flipflop_state = flop; break; case flop: do_flop_action; if (condition_to_toggle == true) flipflop_state = flip; break; default: do_action_that_never_should_happen; break; }As can be seen in this simple example of a flip-flop, it is very easy to make a dangerous error. No compiler on earth forbids (or even warns about it) the programmer to assign an illegal value (e.g. 1234) to the state machine variable. Moreover the out-of-bounds-check (the default:-case) is mandatory here. Wouldn't it be nice when the computer could do some work to make errors of this kind impossible? With the further advantage that the out-of-bounds-check could be eliminated? Or eliminating the costly checks at all?
The last two points can easily be solved by a skilled programmer with the use of a pointer to a function, that is pointing to the actual function to be executed in the present state of the machine. Changing the state is done by pointing the pointer to another function. This concept executes much, much faster than the example above. But the drawback is this: Either you verify before each call via the pointer that it really points to a legal address or you risk crashing the application (when an illegal value was assigned to the pointer). And question one is still open. This is where the state-machine-preprocessor comes in.
#include < stdio.h> /* delete the space after the < ! Is in for the browser only */ #SEQUENCE void flipflop() /* define the state machine and give it a name */ #PRESENT flop /* define the first state, like a normal function */ { int c; printf("Current state is FLOP - Hit < f > to switch state!\n"); if (getchar() == 'f') #NEW flip /* check condition to transit to other state */ } #PRESENT flip /* define the other state */ { int c; printf("Current state is FLIP - Hit < f > to switch state!\n"); if (getchar() == 'f') #NEW flop } void init_flipflop (void) { #NEW flop } /* define a funtion to set the machine to a known state! */ #ENDSEQUENCE /* end of the state machine definition */ void main() /* let us try it out */ { init_flipflop(); /* set machine to a known state BEFORE the 1.st time use */ while (1==1) /* in this example we loop forever */ { getchar(); /* to "eat" the input terminator, for aesthetic reasons only */ flipflop(); /* call the state machine */ } }
What do the new keywords do? Let us start by grouping them into two categories: #SEQUENCE and #ENDSEQUENCE belong to the first category and #PRESENT and #NEW go into the second. The rules for the keywords of the first category are:
#include < stdio.h> /* delete the space after the < ! Is in for the browser only */ #include "FF.SMH" /* define the state machine and give it a name */ void _flipflop_flop_() /* define the first state, like a normal function */ { int c; printf("Current state is FLOP - Hit < f > to switch state!\n"); if (getchar() == 'f') _flipflop_ = _flipflop_flip_ ; /* check condition to transit to other state */ } void _flipflop_flip_() /* define the other state */ { int c; printf("Current state is FLIP - Hit < f > to switch state!\n"); if (getchar() == 'f') _flipflop_ = _flipflop_flop_ ; } void init_flipflop (void) { _flipflop_ = _flipflop_flop_ ; } /* define a funtion to set the machine to a known state! */ /* End of state machine flipflop */ /* end of the state machine definition */ void main() /* let us try it out */ { init_flipflop(); /* set machine to a known state BEFORE the 1.st time use */ while (1==1) /* in this example we loop forever */ { getchar(); /* to "eat" the input terminator, for aesthetic reasons only */ flipflop(); /* call the state machine */ } }As you can see, it is mostly a copy of the input file. But some changes are noteworthy. Your declaration of your state machine is replaced by an #include of the new header file. All the #PRESENTs are gone. But the state names are changed. They have the name of the state machine prepended. And they start and end with an underscore. This is a common method to avoid name clashing. Finally the #ENDSEQUENCE left no trace at all.
void (*_flipflop_)(); #define flipflop() (*_flipflop_)() void _flipflop_flop_() ; void _flipflop_flip_() ;You see the declaration of the state machine and all the states here. And a simple definition to make the state machine like you intended to call it. That is all. The reason, why this header file is needed is simple: It is easier for the preprocessor to first insert the #include into the C file and then later, when all states are defined, to write the header file.