Practical example of function pointer

Thread Starter

Kittu20

Joined Oct 12, 2022
431
Hello Everyone,

I have a basic understanding of function pointers in the C language and what they do. However, most of the examples I've come across in textbooks seem quite simple, leaving me curious about their practical use, especially in embedded systems.

Do you use function pointer in your project. Could someone provide an example of how function pointers are genuinely useful in the context of embedded systems?
 

BobTPH

Joined Jun 5, 2013
8,665
Suppose you write a general purpose routine that can draw a graph of a function.

You could pass a function pointer to it to give the function to plot.
 

joeyd999

Joined Jun 6, 2011
5,171
Could someone provide an example of how function pointers are genuinely useful in the context of embedded systems?
In the embedded realm, I've discovered -- through the help of my friends here -- that function pointers provide for the fastest method of decoding large state machines.

C:
//*****************************************************
//** LCD_poll -- Poll the LCD Display                **
//*****************************************************
//** Call each loop of main program                  **
//**   Handles initialization and message display    **
//**   Updates display when lcd_update_f flag is set **
//*****************************************************

void LCD_poll(void)
{
    static void (*state_function[])(void)=
        {LCDstate0, LCDstate1, LCDstate2, LCDstate3,
         LCDstate4, LCDstate5, LCDstate6, LCDstate7,
         LCDstate8, LCDstate9, LCDstate10,LCDstate11,
         LCDstate12,LCDstate13,LCDstate14,LCDstate15,
         LCDstate16,LCDstate17};

    if ((lcd_bfvalid_f && !LCD_isBusy()) || !lcd_bfvalid_f)
        state_function[lcd_state]();
}
 

Thread Starter

Kittu20

Joined Oct 12, 2022
431
I have a code where implemented state machine. How can a function pointer be useful in this scenario?

C:
#include <xc.h>
#include <stdio.h>

#define RED_PIN_1    LATB0
#define GREEN_PIN_1  LATB1
#define YELLOW_PIN_1 LATB2

#define RED_PIN_2    LATB3
#define GREEN_PIN_2  LATB4
#define YELLOW_PIN_2 LATB5

#define PEDESTRIAN_CROSS_BLUE LATB6
#define PEDESTRIAN_DONT_CROSS_RED LATB7

enum LightState {
    RED_STATE,
    YELLOW_STATE,
    GREEN_STATE
};


enum LightState currentLightState = RED_STATE;


volatile unsigned int elapsedTime = 0;

void initializeHardware() {
    TRISB0 = 0;
    TRISB1 = 0;
    TRISB2 = 0;

    TRISB3 = 0;
    TRISB4 = 0;
    TRISB5 = 0;
    TRISB6 = 0;
    TRISB7 = 0;

    LATB = 0x00;

    RED_PIN_1 = 1;
    YELLOW_PIN_1 = 0;
    GREEN_PIN_1 = 0;

    RED_PIN_2 = 1;
    YELLOW_PIN_2 = 0;
    GREEN_PIN_2 = 0;
 
    PEDESTRIAN_CROSS_BLUE = 1;
    PEDESTRIAN_DONT_CROSS_RED = 0;

    ANCON0 = 0x00;
    ANCON1 = 0x00;

    INTCON = 0;

    INTCONbits.GIE = 1;
    INTCONbits.PEIE = 1;
}

void initializeTimer2(void) {
    T2CONbits.T2OUTPS = 0b0000;
    T2CONbits.TMR2ON = 1;
    T2CONbits.T2CKPS = 0b10;
    PR2 = 124;

    PIE1bits.TMR2IE = 1;
}

void main(void) {
    initializeHardware();
    initializeTimer2();

    while (1) {
        unsigned int tempElapsedTime = elapsedTime;

        // Temporarily disable global interrupts
        INTCONbits.GIE = 0;

        // Re-enable global interrupts
        INTCONbits.GIE = 1;

        // Update traffic light states and pedestrian signals
        switch (currentLightState) {
            case RED_STATE:
                if (tempElapsedTime >= 20000) {
                    currentLightState = GREEN_STATE;
                    INTCONbits.GIE = 0;
                    elapsedTime = 0;
                    INTCONbits.GIE = 1;
                    RED_PIN_1 = 0;
                    GREEN_PIN_1 = 1;
                    YELLOW_PIN_1 = 0;
                  

                    RED_PIN_2 = 0;
                    GREEN_PIN_2 = 1;
                    YELLOW_PIN_2 = 0;

                    PEDESTRIAN_CROSS_BLUE = 0;
                    PEDESTRIAN_DONT_CROSS_RED = 1;
                
                }
                break;
            case GREEN_STATE:
                if (tempElapsedTime >= 20000) {
                    currentLightState = YELLOW_STATE;
                    INTCONbits.GIE = 0;
                    elapsedTime = 0;
                    INTCONbits.GIE = 1;
                    RED_PIN_1 = 0;
                    YELLOW_PIN_1 = 1;
                    GREEN_PIN_1 = 0;

                    RED_PIN_2 = 0;
                    YELLOW_PIN_2 = 1;
                    GREEN_PIN_2 = 0;

                    PEDESTRIAN_CROSS_BLUE = 0;
                    PEDESTRIAN_DONT_CROSS_RED = 1;
                  
                }
                break;
            case YELLOW_STATE:
                if (tempElapsedTime >= 3000) {
                    currentLightState = RED_STATE;
                    INTCONbits.GIE = 0;
                    elapsedTime = 0;
                    INTCONbits.GIE = 1;
                    RED_PIN_1 = 1;
                    YELLOW_PIN_1 = 0;
                    GREEN_PIN_1 = 0;

                    RED_PIN_2 = 1;
                    YELLOW_PIN_2 = 0;
                    GREEN_PIN_2 = 0;

                    PEDESTRIAN_CROSS_BLUE = 1;
                    PEDESTRIAN_DONT_CROSS_RED = 0;
                }
                break;
        }
    }
}


void __interrupt() ISR(void) {
    PIR1bits.TMR2IF = 0;
    elapsedTime++;
}
 

nsaspook

Joined Aug 27, 2009
12,810
I have a code where implemented state machine. How can a function pointer be useful in this scenario?

C:
#include <xc.h>
#include <stdio.h>

#define RED_PIN_1    LATB0
#define GREEN_PIN_1  LATB1
#define YELLOW_PIN_1 LATB2

#define RED_PIN_2    LATB3
#define GREEN_PIN_2  LATB4
#define YELLOW_PIN_2 LATB5

#define PEDESTRIAN_CROSS_BLUE LATB6
#define PEDESTRIAN_DONT_CROSS_RED LATB7

enum LightState {
    RED_STATE,
    YELLOW_STATE,
    GREEN_STATE
};


enum LightState currentLightState = RED_STATE;


volatile unsigned int elapsedTime = 0;

void initializeHardware() {
    TRISB0 = 0;
    TRISB1 = 0;
    TRISB2 = 0;

    TRISB3 = 0;
    TRISB4 = 0;
    TRISB5 = 0;
    TRISB6 = 0;
    TRISB7 = 0;

    LATB = 0x00;

    RED_PIN_1 = 1;
    YELLOW_PIN_1 = 0;
    GREEN_PIN_1 = 0;

    RED_PIN_2 = 1;
    YELLOW_PIN_2 = 0;
    GREEN_PIN_2 = 0;

    PEDESTRIAN_CROSS_BLUE = 1;
    PEDESTRIAN_DONT_CROSS_RED = 0;

    ANCON0 = 0x00;
    ANCON1 = 0x00;

    INTCON = 0;

    INTCONbits.GIE = 1;
    INTCONbits.PEIE = 1;
}

void initializeTimer2(void) {
    T2CONbits.T2OUTPS = 0b0000;
    T2CONbits.TMR2ON = 1;
    T2CONbits.T2CKPS = 0b10;
    PR2 = 124;

    PIE1bits.TMR2IE = 1;
}

void main(void) {
    initializeHardware();
    initializeTimer2();

    while (1) {
        unsigned int tempElapsedTime = elapsedTime;

        // Temporarily disable global interrupts
        INTCONbits.GIE = 0;

        // Re-enable global interrupts
        INTCONbits.GIE = 1;

        // Update traffic light states and pedestrian signals
        switch (currentLightState) {
            case RED_STATE:
                if (tempElapsedTime >= 20000) {
                    currentLightState = GREEN_STATE;
                    INTCONbits.GIE = 0;
                    elapsedTime = 0;
                    INTCONbits.GIE = 1;
                    RED_PIN_1 = 0;
                    GREEN_PIN_1 = 1;
                    YELLOW_PIN_1 = 0;
                 

                    RED_PIN_2 = 0;
                    GREEN_PIN_2 = 1;
                    YELLOW_PIN_2 = 0;

                    PEDESTRIAN_CROSS_BLUE = 0;
                    PEDESTRIAN_DONT_CROSS_RED = 1;
               
                }
                break;
            case GREEN_STATE:
                if (tempElapsedTime >= 20000) {
                    currentLightState = YELLOW_STATE;
                    INTCONbits.GIE = 0;
                    elapsedTime = 0;
                    INTCONbits.GIE = 1;
                    RED_PIN_1 = 0;
                    YELLOW_PIN_1 = 1;
                    GREEN_PIN_1 = 0;

                    RED_PIN_2 = 0;
                    YELLOW_PIN_2 = 1;
                    GREEN_PIN_2 = 0;

                    PEDESTRIAN_CROSS_BLUE = 0;
                    PEDESTRIAN_DONT_CROSS_RED = 1;
                 
                }
                break;
            case YELLOW_STATE:
                if (tempElapsedTime >= 3000) {
                    currentLightState = RED_STATE;
                    INTCONbits.GIE = 0;
                    elapsedTime = 0;
                    INTCONbits.GIE = 1;
                    RED_PIN_1 = 1;
                    YELLOW_PIN_1 = 0;
                    GREEN_PIN_1 = 0;

                    RED_PIN_2 = 1;
                    YELLOW_PIN_2 = 0;
                    GREEN_PIN_2 = 0;

                    PEDESTRIAN_CROSS_BLUE = 1;
                    PEDESTRIAN_DONT_CROSS_RED = 0;
                }
                break;
        }
    }
}


void __interrupt() ISR(void) {
    PIR1bits.TMR2IF = 0;
    elapsedTime++;
}
As usually, you're asking the wrong side of the question. Rewriting something is not would you do unless the code is total crap, you design (knowing as much as possible about the end result) it first with the knowledge of various programming abstractions and methods. Randomly adding function pointers or any type of indirection is a recipe for bad code.
 

Thread Starter

Kittu20

Joined Oct 12, 2022
431
-- that function pointers provide for the fastest method of decoding large state machine
What's your perspective on the scale of state machines? How much would you consider 'large' in terms of states - is it 3, 10, 50, 100, 500 states , or more in your opinion? Additionally could you please explain the meaning of a 'decoding state machine' in this context?
 

joeyd999

Joined Jun 6, 2011
5,171
What's your perspective on the scale of state machines? How much would you consider 'large' in terms of states - is it 3, 10, 50, 100, 500 states , or more in your opinion? Additionally could you please explain the meaning of a 'decoding state machine' in this context?
Wrong question.

A switch/case statement is far easier to use in most cases. Function pointers add complexity.

I only use the function pointers where either a) the execution speed must be as fast as possible and the switch/case would take longer, or b) I require the execution time for each state to be predictable.

For actual execution times, feel free to write some code and simulate it.
 

nsaspook

Joined Aug 27, 2009
12,810
Could you please point out what's incorrect in my question? @joeyd999 mention state machine example in his response, which is why I shared my own state machine code.
I didn't say your question was incorrect. I tried to point out that it misses the point of WHY function pointers are used by a request to modify some random code example obviously not coded with polymorphism as a design criteria.
https://www.codeproject.com/Articles/739687/Achieving-polymorphism-in-C
 

geekoftheweek

Joined Oct 6, 2013
1,183
I have a program I wrote for PC that I can use to communicate with microcontrollers. I have different libraries that can be loaded at run time to communicate with microcontrollers through different means. One library is for a Raspberry PI gpio, another library uses the video port I2C of my PC, another connects to an ESP8266, and one other connects to a socket running on another PC. Each library has identical functions defined and after loading and opening the library I use dlsym() to get the function pointer in the library. Not every library has every function of the others, but the functions are all defined exactly the same in each library that implements them.

Not exactly a microcontroller example, but an easy way I found in this case to manage a lot of similar code that doesn't need to be loaded every time the program is run.
 

MrChips

Joined Oct 2, 2009
30,497
Suppose you have a function that is implemented by hardware made by different manufacturers. Each device requires a different device handler. Using pointers means that the code can call which ever device driver is required without having to recompile the program.
 

ApacheKid

Joined Jan 12, 2015
1,455
Hello Everyone,

I have a basic understanding of function pointers in the C language and what they do. However, most of the examples I've come across in textbooks seem quite simple, leaving me curious about their practical use, especially in embedded systems.

Do you use function pointer in your project. Could someone provide an example of how function pointers are genuinely useful in the context of embedded systems?
One thing springs to mind here is that of a finite state machine. These can be neatly implemented as 2D arrays of function pointers, the two subscripts being a "STATE" variable and the other being an "EVENT" variable.

The FSM is then just a loop, in that loop we wait for an "event" and when we get that we invoke the function indexed by that pair of values, the return value of these functions is the new STATE value.

This is a design I've used many times, most of the logic is then simply defined by the setup of the 2D array, extremely intricate behavior can be coded this way but the code has a simple very granular structure, the event processing loop is more or less trivial and each "event handler" is simple because it's job is simply to do whatever is needed when event X occurs in state Y and return a new state Z. There are no complicated if/then/else trees, no switch statements and so on.

Debugging systems that use this approach is often less frustrating too because the system is just jumping from one state to another over and over and so the bugs are usually in how the table is setup and rarely due to complex code.

You can think of - say - a calculator, each key can be an "event" and the states defined to represent the various states the system goes through as keys get pressed.
 
Last edited:

ApacheKid

Joined Jan 12, 2015
1,455
Wrong question.

A switch/case statement is far easier to use in most cases. Function pointers add complexity.

I only use the function pointers where either a) the execution speed must be as fast as possible and the switch/case would take longer, or b) I require the execution time for each state to be predictable.

For actual execution times, feel free to write some code and simulate it.
I'd beg to differ here. Even a simple FSM when coded using hard logic can soon get unwieldy, almost undebuggable. The reason being that once we use if/then/else/switch to branch on states the depths of these becomes related to problem complexity.

I've seen such code where modifications soon make the code very scary, with all kind of flags and temporary variables, we get stuff like

IF curr_state = 2 and prev_state != 5 THEN

or

Code:
SWITCH (X)

CASE (10)

  SWITCH (Y)

  CASE (5)
and so on. An FSM based on tables of functions can make it easy to avoid this, every function can depend purely upon current state and the event itself and there's no explicit coding that cares about history or previous states or previous actions and so on.
 

WBahn

Joined Mar 31, 2012
29,867
The first place where I used function pointers (other than as a contrivance for a homework problem) was when I was teaching C and I had the students each come up with a function to take a turn in a game (Mancala, in this case). The functions each took a pointer to a copy of the game board and returned the number of the pit they had chosen as their move.

I then had, in place of the final exam, a round robin tournament in which each function was pitted against every other function. Included in this were three functions I wrote, one that made a random legal move, on that used a simply heuristic, and one that looked ahead several moves. When a student's function was player 1, that student had to get up and talk about their strategy and implementation while the game was being played behind them. The rate of play was artificially slowed to give the student four minutes to talk, at which point it went to normal speed and several rounds were played before the next student had to get up.

So how did this work?

Each person had to write their function using a specific naming convention and submit a .c and a .h file accordingly. I then had a small C program that scanned the folder for all of the files that met the naming criteria. The only information it needed was the name of the files. It then used this information to generate a .c and .h file that did nothing more than create an array of function pointers and also a make file that it generated the first part of it and then appended what was in another make file to get the complete thing.

This way, as student submitted code, all I had to do was dump their files into the game directory, run my little prep program, and run the make file. The game engine source code never had to be touched.
 
Top