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");
}
This code doesn’t make any sense and certainly does not compile:
03 void (*fp)();
04
05 fp functionPointer = function_a;
06
07 (*fp)(); //prints “a”
First fp is a declaration here not a typedef and second functionptr is initialized not fp, and third you didn’t mention that &function_a is optional. I use it because its mandatory in C++ so &C::function offers consistency. Third C++ offers (void) and () as alternatives where C does not so () means anything bprintut clearly intended was (void) here. I don’t use (void) that often in C++ decls or definitions although that doesn’t jibe w/ the consistency argument I made over using & w/ C++ member function ptrs and & also w/ C function ptrs. It would seem that using & just makes sense to take the address of a function. Some C++ implementations support mfp p = C::func but that isn’t permitted in the standard. I suppose typing & isn’t a lot of effort for the conceptual cleanness but typing void in any argument list is more effort.
Clearly it would seem that you’re using C++ but just to make sure I put all the code below in C. Another follow on could include C++ member function ptrs which offer variations on even using virtual and pure virtual member functions in a table while using a derived class object to involve those non-virtual, virtual and pure virtual functions. The elegant aspect here is that the FSM won’t know if they are non-virtual, virtual or pure virtual member functions which offers a great deal of flexibility in creating finite state machines with variations and/or open protocols.
The following will compile and produce the intended result
void function_a(void) { printf(“a”) }
typedef void (*fp)(void);
fp functionPointer = &function_a; /* & optional but supported – no compiler should issue a warning */
(*functionPointer)(); /* C++ supports functionPointer() also – not sure about C */
An Active Object using either a table of member function ptrs would be useful – one could create a hybrid table or a table for an abstract class that is to be initialized by derived objects.
Hello Andrew,
your guess is wrong the above code should have been c-code. If it would have been c++ there should be something like a class reference e.g. myClass::*myFunctionPointer
Your right with your statement though the code should have been
03 void (*fp)(void);
04 fp = &function_a; /* the address operator can be ommited though */
05 (*fp)();
It would have been better (clearer and safer) to have written
03 typedef void (*fp)(void);
04 fp functionPointer = &function_a;
05 (*functionPointer)(); /* funcionPointer() should also work (on most compilers) */
and you’re right the last one is much better understandable. I’ll improve the example code above.
Regarding your other comments:
void function(void) and void function()
are not the same in C, you are right. It is just a hint for the compiler though, to check wether there are any arguments in a function call and issue an error if there are. You could define your function to be void function() and if you never call the function with arguments there is no problem at all with your code. If you’d call your function with any argument though, there is absolutely un-defined behaviour (e.g. stack overflow) and the compiler want tell you about that. It is more type-safier to write void function(void) though. So, YES, it is a good idea to use void function(void) against void function() in C. In C++ though these thow are equivalents.
address operator
you are right, that it is more understandable, but not necessary. If you know that fpr is a function pointer: what should fpr = function mean else as fpr = &function. It would not make any other sense to pass an adress of a function to a pointer of a function and that’s what the compiler does anyways. It’s like using an pointer to an array: array_ptr = &array[0] would be conceptually more clearer, but array_ptr = array does the same and I don’t find one or the other more readable. If type-safety is a programmers concern there are much better languages then C.
The formal definition in the C standard reads:
>>A function designator is an expression that has function type. Except when it is the operand of the sizeof operator or the unary & operator, a function designator with type “function returning type” is converted to an expression that has type “pointer to function returning type”.<<
So it's in the design of the language that fpr = &function is the same as fpr = function and I find it quite obvious. (Sure with int *ptr_to_int; int a = 5; ptr_to_int = &a; ptr_to_int = a; it's obvious that &a and a are quite a different thing, but with functions? What else should a = function mean than the address to that function (read: that function itself)? A function has no value or anything like that ;-) )
active object
yeah, I thought about that too during my implementation. An active object though would need some additional memory footprint and I don't see the need to change the table dynamically at runtime. I put these tables in an additional .h-file and defined them once for my program. If they needed to be changed I just changed the .h-file and recompiled the hole thing. I find it more convenient and safe, as the state logic is something that should not change during run-time.
However, I would find it interesting to see an implementation with a hybrid table or on abstract class.