C-Frameworks: Basic State Machine Implementation. Pt. 2

How to Use Function Pointers for Implementing Finite State Machines

I posted my solution for Finite State Machines in C using Matrix. Jean-Marc (f1hdi.org) commented to that entry:

I would love to have the state event matrix not only returning a ‘next state’ and action to do BUT directly calling ‘actions’ functions. A long time ago , when I was at school, I wrote such ptr function call but can’t reproduce it now. Basically , in your routine below, I would love to replace the ‘return’ by a call to a function which you would have its pointer in the matrix. Any idea ?

The simple answer to Jean-Marc’s questions is “Yes” and I’m really sorry that I hadn’t got the time to answer sooner. The basic idea of using function pointers is the right choice to find a solution for this task.

So let’s see how we can solve this one.

What are functions pointers generally?

First of all a pointer is a data type whose value is “pointing” to a specific address in memory. “Dereferencing” a pointer returns the value stored at that specific address. Defining a pointer is done with an asterisk before the identifier of the variable. A pointer is assigned via the “adress of”-operator & and dereferencing a pointer is done with an asterisk before the identifiers name again.

1int *a;
2int b=5;
3 
4a  = &b;       // a is given the adress of b, a = 5
5b  = *a + 10;  // b = 15
6&b = a;        // b is given the adress of a, b = 5

Function pointers point to the adress of a c-function. Dereferencing a function pointer means to envoke the apropriate function. The syntax for letting a variable point to a function differs from the above example. Pointing to a function is done via

 1void main() {
 2
 3    void (*fp)();
 4    
 5    fp functionPointer = function_a;
 6    (*fp)();  //prints "a"
 7    
 8    fp functionPointer = function_b;
 9    (*fp)();  //prints "b"
10}
11
12void function_a(void) {
13      fprintf("a \n");
14}
15
16void function_b(void) {
17     fprintf("b\n");
18}

What to do with function pointers?

So these are function pointers, but why do whe need them?

 1// from: http://www.newty.de/ftp/intro.html#why
 2float Plus    (float a, float b) { return a+b; }
 3float Minus   (float a, float b) { return a-b; }
 4float Multiply(float a, float b) { return a*b; }
 5float Divide  (float a, float b) { return a/b; }
 6
 7// Solution with a switch-statement -  specifies which operation to execute
 8void Switch(float a, float b, char opCode)
 9{
10
11   float result;
12
13   // execute operation
14   switch(opCode)
15   {
16      case '+' : result = Plus     (a, b); break;
17      case '-' : result = Minus    (a, b); break;
18      case '*' : result = Multiply (a, b); break;
19      case '/' : result = Divide   (a, b); break;
20   }
21
22   cout << "Switch: 2+5=" <<  result << endl;         // display result
23
24}
25
26// Solution with a function pointer
27// a function which takes two floats and returns a float. The function pointer
28// "specifies" which operation shall be executed.
29
30void Switch_With_Function_Pointer(float a, float b, float (*pt2Func)(float, float))
31{
32
33   float result = pt2Func(a, b);    // call using function pointer
34
35}
36
37// Execute example code
38void Replace_A_Switch()
39{
40
41   Switch(2, 5, /* '+' specifies function 'Plus' to be executed */ '+');
42   Switch_With_Function_Pointer(2, 5, /* pointer to function 'Minus' */ &Minus);
43
44}

Changing the Type Definitions from the Matrix-Style-Implementation

You should have a look at my old post on how to implement a Matrix-Style-Implementation of Finite State Machines in C, to understand how the general mechanisms on this implementation work. I won’t recapitulate theme here.

First off all, you’ll have to define the generic typedefs for the state-event matrix as mentioned in the older implementation:

 1/*****************************************************************
 2 * typedefs
 3 *****************************************************************/
 4
 5typedef enum {
 6  STATE1,
 7  STATE2,
 8  STATE3
 9} state;
10
11typedef enum {
12  NILEVENT,
13  EVENT1,
14  EVENT2
15} event;

These are exactly the same data types as the approach without function pointers. We’ll now define a typedef for a function pointer to an action that shall be released in each state, the action-functions and the generic state-machine function:

 1typedef void (*action)();
 2
 3// General functions
 4void stateEval(event e);
 5void exit(int status);
 6void getIOValues(void);
 7
 8//Actions
 9void action1_1(void);
10void action1_2(void);
11void action1_3(void);
12
13void action2_1(void);
14void action2_2(void);
15void action2_3(void);
16
17void action3_1(void);
18void action3_2(void);
19void action3_3(void);

The action function-pointer and state enumerators are combined in a structure typedef used for the function-pointer based state-event matrix:

1typedef struct {
2    state nextState;       // Enumerator for the next state
3    action actionToDo;     // function-pointer to the action that shall be released in current state
4}  stateElement;               // structure for the elements in the state-event matrix

And finally the state-event matrix will be build-up:

1stateElement stateMatrix[3][3] = {
2   { {STATE1, action1_1}, {STATE2, action1_2}, {STATE3, action1_3} },
3   { {STATE2, action2_1}, {STATE2, action2_2}, {STATE3, action2_3} },
4   { {STATE3, action3_1}, {STATE3, action3_2}, {STATE3, action3_3} }
5};

And this will be the state-machine invocation with function-pointers involved

All these snippets go into the Header-File.

 1#include
 2#include "main.h"
 3
 4state  currentState = STATE1;
 5
 6int main()
 7{
 8    //Initializations
 9    event  eventOccured = NILEVENT;
10    action actionToDo   = action1_1;
11
12    while(1) {
13        // event input, NIL-event for non-changing input-alphabet of FSM
14        // in real implementation this should be triggered by event registers e.g.
15        // evaluation of complex binary expressions could be implemented to release the events
16
17                int e = 0;            
18
19               printf("----------------\n");
20               printf("Event to occure: ");
21               scanf("%u",&e);
22               stateEval( (event) e); // typecast to event enumeration type
23               printf("-----------------\n");
24
25    };
26
27    return (0);
28
29}
30
31/********************************************************************************
32 * stateEval (event)
33 * in Dependancy of an triggered event, the action wich is required by this
34 * transition will be returned. The proper action is determined by the current state the
35 * automat holds. The current state will then be transitioned to the requestet next
36 * state
37 ********************************************************************************/
38
39void stateEval(event e)
40{
41    //determine the State-Matrix-Element in dependany of current state and triggered event
42        stateElement stateEvaluation = stateMatrix[currentState][e];
43
44
45    //do the transition to the next state (set requestet next state to current state)...
46    currentState = stateEvaluation.nextState;
47
48    //... and fire the proper action
49    (*stateEvaluation.actionToDo)();
50
51}
52
53/**********************************************************************
54 * action functions
55 **********************************************************************/
56
57void action1_1() {
58  printf("action1.1 \n");
59}
60
61void action1_2() {
62  printf("action1.2 \n");
63}
64
65void action1_3() {
66  printf("action1.3 \n");
67}
68
69void action2_1() {
70  printf("action2.1 \n");
71}
72
73void action2_2() {
74  printf("action2.2 \n");
75}
76
77void action2_3() {
78  printf("action2.3 \n");
79}
80
81void action3_1() {
82  printf("action3.1 \n");
83}
84
85void action3_2() {
86  printf("action3.2 \n");
87}
88
89void action3_3() {
90  printf("action3.3 \n");
91}

comments powered by Disqus