Finite State Machine Matrix-Style-C-Implementation (Function Pointers Addon)

Finite State Machine Matrix-Style-C-Implementation (Function Pointers Addon)

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 what are functions pointers basically?

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.

   
    int *a;
    int b=5;

     a = &b; // a is given the adress of b, a = 5
     b = *a + 10; // b = 15
     &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

void main() {

     void (*fp)();

    fp functionPointer = function_a;

    (*fp)();  //prints "a"

    fp functionPointer = function_b;

    (*fp)();  //prints "b"

}

void function_a(void) {
      fprintf("a \n");
}

void function_b(void) {
     fprintf("b\n");
}

What to do with function pointers?

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

// from: http://www.newty.de/ftp/intro.html#why

float Plus    (float a, float b) { return a+b; }
float Minus   (float a, float b) { return a-b; }
float Multiply(float a, float b) { return a*b; }
float Divide  (float a, float b) { return a/b; }

// Solution with a switch-statement -  specifies which operation to execute
void Switch(float a, float b, char opCode)
{
   float result;

   // execute operation
   switch(opCode)
   {
      case '+' : result = Plus     (a, b); break;
      case '-' : result = Minus    (a, b); break;
      case '*' : result = Multiply (a, b); break;
      case '/' : result = Divide   (a, b); break;
   }

   cout << \"Switch: 2+5=\" << result << endl;         // display result
}

// Solution with a function pointer
// a function which takes two floats and returns a float. The function pointer
// "specifies" which operation shall be executed.
void Switch_With_Function_Pointer(float a, float b, float (*pt2Func)(float, float))
{
   float result = pt2Func(a, b);    // call using function pointer
}

// Execute example code
void Replace_A_Switch()
{
   Switch(2, 5, /* '+' specifies function 'Plus' to be executed */ '+');
   Switch_With_Function_Pointer(2, 5, /* pointer to function 'Minus' */ &Minus);
}

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:

/*****************************************************************
 * typedefs
 *****************************************************************/

typedef enum {
  STATE1,
  STATE2,
  STATE3
} state;

typedef enum {
  NILEVENT,
  EVENT1,
  EVENT2
} 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:

typedef void (*action)();

// General functions
void stateEval(event e);
void exit(int status);
void getIOValues(void);

//Actions
void action1_1(void);
void action1_2(void);
void action1_3(void);
void action2_1(void);
void action2_2(void);
void action2_3(void);
void action3_1(void);
void action3_2(void);
void 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:

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

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

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

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

All these snippets go into the Header-File.

#include
#include "main.h"

state  currentState = STATE1;

int main()
{
	//Initializations
	event  eventOccured = NILEVENT;
	action actionToDo   = action1_1;

	while(1) {
		// event input, NIL-event for non-changing input-alphabet of FSM
		// in real implementation this should be triggered by event registers e.g.
		// evaluation of complex binary expressions could be implemented to release the events

                int e = 0;             

               printf("----------------\n");
               printf("Event to occure: ");
               scanf("%u",&e);
               stateEval( (event) e); // typecast to event enumeration type
               printf("-----------------\n");

	};
	return (0);
}

/********************************************************************************
 * stateEval (event)
 * in Dependancy of an triggered event, the action wich is required by this
 * transition will be returned. The proper action is determined by the current state the
 * automat holds. The current state will then be transitioned to the requestet next
 * state
 ********************************************************************************/

void stateEval(event e)
{
	//determine the State-Matrix-Element in dependany of current state and triggered event
        stateElement stateEvaluation = stateMatrix[currentState][e];

	//do the transition to the next state (set requestet next state to current state)...
	currentState = stateEvaluation.nextState;
	//... and fire the proper action
	(*stateEvaluation.actionToDo)();
}

/**********************************************************************
 * action functions
 **********************************************************************/

void action1_1() {
  printf("action1.1 \n");
}

void action1_2() {
  printf("action1.2 \n");
}

void action1_3() {
  printf("action1.3 \n");
}

void action2_1() {
  printf("action2.1 \n");
}

void action2_2() {
  printf("action2.2 \n");
}

void action2_3() {
  printf("action2.3 \n");
}

void action3_1() {
  printf("action3.1 \n");
}

void action3_2() {
  printf("action3.2 \n");
}

void action3_3() {
  printf("action3.3 \n");
}

Related Posts