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}