Using an array of function pointers in a class

EDIT
The title was previously: 'Using 'this' in an array of function pointers in a class', title changed to match what I really wanted to know, see reply #75.

I have previously used 'switch' to select a particular function based on an index. One example would be a menu system where pressing a button changes the value of a variable that indexes the menu, and then calls a function that does what is needed for that menu option. Using 'switch' works well, but I recently learned to use an array of function pointers to do the same thing. I think using function pointers is better because an array looks so much tidier than lots of nested 'switch' statements, and I can arrange the array how I like to be easy for me to read.

As I am also trying to understand how to use classes I naturally wanted to use function pointers in a class. I can do this having learned that this can only be done if the functions pointed to are static. While no an issue for my current use I can see that it is a bit of a bodge to be forced to use static functions because of they way they are being called, and I can see this could cause problems in some situations. From what I have managed to learn I think using 'this' in an array would do what I want, but I can't find how to do so. Maybe there's another solution as well that I'm not aware of.

Here is some sample code to illustrate what I can do, and what I am trying to do.

First, the desired output from the code is:

18:48:20.229 -> C:\Users\Perry Bebbington\Documents\Projects\UPS\UPS V2\Arduino\Test code\Function_Pointers_in_class\CPPfile.cpp
18:48:20.694 -> This is function zero
18:48:21.207 -> This is function one
18:48:21.718 -> This is function two
18:48:22.189 -> This is function zero
18:48:22.708 -> This is function one
18:48:23.223 -> This is function two
etc....

Using function pointers in a class, the following code generates the desired output:

Function_Pointers_in_class.ino

#include "HeaderFile.h"

SomeClass thisClass;

void setup() {
    thisClass.startup(115200);

}

void loop() {
    thisClass.bigFunction();

}

HeaderFile.h

#ifndef headerFile
#define headerFile

class SomeClass {
    private:
        static void littleFunctionZero();       // Functions need to be static for function pointers to work.
        static void littleFunctionOne();
        static void littleFunctionTwo();
    public:
        void startup(uint32_t baudRate = 9600);
        void bigFunction();
};
#endif

CPPfile.cpp

#include "Arduino.h"
#include "HeaderFile.h"

void SomeClass::startup(uint32_t baudRate){
    Serial.begin(baudRate);
    char fileName[] = {__FILE__};
    Serial.println(fileName);
}

void SomeClass::bigFunction() {
    uint32_t currentMillis = millis();
    static uint32_t lastMillis = 0;
    static uint32_t wait = 500;
    static uint8_t index = 0;

    typedef void (*functionPtrs) (void);
    functionPtrs someActions[3] = {
            &littleFunctionZero, &littleFunctionOne, &littleFunctionTwo
    };

    if (currentMillis - lastMillis >= wait) {
        lastMillis = currentMillis;
        if(someActions[index]) {
            someActions[index]();
            index = ++index %3;
        }
    }

};

void SomeClass::littleFunctionZero() {
    Serial.println("This is function zero");
}

void SomeClass::littleFunctionOne() {
    Serial.println("This is function one");
}

void SomeClass::littleFunctionTwo() {
    Serial.println("This is function two");
}

What I would like to do is replace:

    typedef void (*functionPtrs) (void);
    functionPtrs someActions[3] = {
            &littleFunctionZero, &littleFunctionOne, &littleFunctionTwo
    };

    if (currentMillis - lastMillis >= wait) {
        lastMillis = currentMillis;
        if(someActions[index]) {
            someActions[index]();
            index = ++index %3;
        }
    }

With something that does not require the functions pointed to to be static. I think it should be possible to use 'this ->' in an array in a similar way, but I can't get it to compile. From what I have read 'this' has the type className*, so in the case of the example code above I think 'this' has the type SomeClass*. Based on this I tried:

SomeClass* somePointers[3] = {this -> littleFunctionZero, this -> littleFunctionOne, littleFunctionTwo};

With the functions still static the compiler error is:
cannot convert 'void (*)()' to 'SomeClass*' in initialization

With the functions not static the compiler error is:
cannot convert 'SomeClass::littleFunctionZero' from type 'void (SomeClass::)()' to type 'SomeClass*'

I've tried a few other things, but I don't see much point in listing them all.

I'm looking for either the correct way to do this or an alternative that achieves the same thing.

Thank you

  • Can almost guarantee you have read this thread, but until the software gurus step in, re-read this.

Thank you Larry,
I'd not seen that. The only thing in there I know nothing about is Lambda functions.

Pointers to member functions are kind of odd ducks. You can use them, but you need to get the (rather tricky) notation right: https://isocpp.org/wiki/faq/pointers-to-members

On platforms with the STL (ESP32/8266, ARM), you may consider using a std::function which is a generic function object that can wrap many different function types. You can use it to wrap a lambda calling a member function or a std::bind that links a member function pointer with an object of that class.

is this a case for virtual functions that are instantiated by inheriting them from other class?

what are these function intended to do?

Something like this? (only works with static functions tho)

#include <stdlib.h>
#include <stdio.h>

typedef void (*func_t)( );

class SomeClass {
	private:
		static void func1() { printf("func1() : hello!\n"); };
		static void func2() { printf("func2() : hello!\n"); };
		
	public:
		func_t ptrs[2];
		SomeClass() {

			ptrs[0] = &this->func1;
			ptrs[1] = &this->func2;
		};
};

SomeClass cls;

int
main() {

	func_t func;

	func = cls.ptrs[0];

	func();

	func = cls.ptrs[1];

	func();

	return 0;
}

To call a member function, you need both the function pointer and a pointer to the instance (its "this" pointer). You need to store both in an array.

https://godbolt.org/z/d3f68qW8W

#include <cstdio>

class SomeClass {
  public:
    void funcZero() { std::puts("funcZero"); }
    void funcOne() { std::puts("funcOne"); }
    void funcTwo() { std::puts("funcTwo"); }
};

struct bound_nullary_someclass_func {
    using nullary_someclass_func = void (SomeClass::*)();
    SomeClass *instance;
    nullary_someclass_func func;
    void operator()() const {
        (instance->*func)();
    }
};

int main() {
    SomeClass inst;
    bound_nullary_someclass_func functions[3] {
        {&inst, &SomeClass::funcZero},
        {&inst, &SomeClass::funcOne},
        {&inst, &SomeClass::funcTwo},
    };
    for (int i = 0; i < 3; ++i)
        functions[i]();
}

That makes perfect sense!

It will take a while for me to make sense of your example code, I shall study it carefully.

Thank you

I can already do pointers to static functions, I'm trying to learn how to do pointers to functions that are not static.

The thing I've used them for is selecting which function to call based on selections of a menu on a display. I also see this as a chance to learn something new.

This code works as well. Generates warnings but works.

class SomeClass {
	private:

void func1() { printf("func1() : hello! this == %p\n", this); };
void func2() { printf("func2() : hello! this == %p\n", this); };

		
	public:
		func_t ptrs[2];
		SomeClass() {

			ptrs[0] = (func_t)&this->func1;
			ptrs[1] = (func_t)&this->func2;
		};
};

Pointers to member functions are hard to get right and I remember struggling with arrays of them years ago when writing a large state machine with lots of entry guard functions and exit actions.

I think it's the wrong way to go. Arrays of lambdas as shown previously are a cleaner solution.

Before C++ supported lambdas, the solution I would have recommended is a different subclass for each member function you wanted to call, then load an array with each location being a pointer to a subclassed object, and calling them using a pointer to the base class and dereferencing the desired method (polymorphic behavior). That's a mouthful, but it's easier to code than to explain...

i'm guessing it's pretty common to use function ptrs in menu code.

here's my menu,h

#ifndef MENU_H
# define MENU_H

# define Clear   "\e[2J\e[0;0H"
# define Normal  "\e[0m"
# define Bold    "\e[1m"
# define Blink   "\e[5m"
# define Red     "\e[31m"
# define Yellow  "\e[33m"
# define Blue    "\e[34m"

#define MAX_DIGIT   5

// button and display function pointers
typedef struct {
    void (*menu) (void *);
    void (*sel)  (void *);
    void (*up)   (void *);
    void (*dn)   (void *);
    void (*disp) (void *);
} Func_t;

// parameter descripton
typedef struct {
    const char *text;
    int        *param;
    int         min;
    int         max;
    void       *p;
    Func_t  func;
} P_t;

// parameter types
typedef enum {
    T_NULL,
    T_NONE,
    T_MENU,
    T_PARAM,
    T_STR,
    T_LIST,
} T_t;

inline const char *
menuType (T_t t)
{
    switch (t)  {
    case T_NULL:
        return "T_NULL";
        break;

    case T_NONE:
        return "T_NONE";
        break;

    case T_MENU:
        return "T_MENU";
        break;

    case T_PARAM:
        return "T_PARAM";
        break;

    case T_STR:
        return "T_STR";
        break;

    case T_LIST:
        return "T_LIST";
        break;

    default:
        return "unknown";
        break;
    }
}

// menu description
typedef struct {
    const char *text;
    const char *text2;
    T_t         type;
    void       *ptr;
    int        *pParam;
} Menu_t;

// menu stimuli
typedef enum  {
    M_NULL,
    M_MENU,
    M_SEL,
    M_UP,
    M_DN,
    M_LAST
} MenuStim_t;

// -------------------------------------------------------------------
// menu functions

void __   (void *);     // null function
void sel  (void *);     // select list item

void sft  (void *);     // shift digit to edit next digit to the right
void sfA  (void *);     // shift digit to right, not past NULL

void deA  (void *);     // decrement value of a character (' ' to '~')
void dec  (void *);     // decrement a digit value

void inA  (void *);     // increment value of a character (' ' to '~')
void inc  (void *);     // increment a digit value

void up   (void *);     // increment value of parameter
void dn   (void *);     // decrement value of paramter

void dspA (void *);     // display string for edit
void dspP (void *);     // display parameter for edit
void dspV (void *);     // display variable for edit


void menu (MenuStim_t);

#endif

and here is how the menus are populated

// tables describing menu options

#include <stdlib.h>
#include <stdint.h>

#include "cfg.h"
#include "menu.h"
#include "menus.h"
#include "vars.h"

const char *sOnOff [] = { "Off", "On" };
const char *sWt []    = { "Light", "Low", "Med", "Heavy" };

// -------------------------------------------------------------------
// parameters
P_t pTon = { "Tonnage", &tonnage, 0, 3, sWt,    { __,   __,  up, dn,  dspP }};
P_t pLoc = { "Loco",    &locoIdx, 0, 0, NULL,   { __,  sft, inc, dec, dspV }};
P_t pCar = { "# Cars",  &cars,    0, 0, NULL,   { __,  sft, inc, dec, dspV }};

P_t pAd0 = { "Adr[0]", &locos[0].adr, 0, 0, & locos[0].adr, {sel, sft, inc, dec, dspV}};
P_t pAd1 = { "Adr[1]", &locos[1].adr, 0, 0, & locos[1].adr, {sel, sft, inc, dec, dspV}};
P_t pAd2 = { "Adr[2]", &locos[2].adr, 0, 0, & locos[2].adr, {sel, sft, inc, dec, dspV}};
P_t pAd3 = { "Adr[3]", &locos[3].adr, 0, 0, & locos[3].adr, {sel, sft, inc, dec, dspV}};

P_t pAd4 = { "Adr[4]", &locos[4].adr, 0, 0, & locos[4].adr, {sel, sft, inc, dec, dspV}};
P_t pAd5 = { "Adr[5]", &locos[5].adr, 0, 0, & locos[5].adr, {sel, sft, inc, dec, dspV}};
P_t pAd6 = { "Adr[6]", &locos[6].adr, 0, 0, & locos[6].adr, {sel, sft, inc, dec, dspV}};
P_t pAd7 = { "Adr[7]", &locos[7].adr, 0, 0, & locos[7].adr, {sel, sft, inc, dec, dspV}};

P_t pAd8 = { "Adr[8]", &locos[8].adr, 0, 0, & locos[8].adr, {sel, sft, inc, dec, dspV}};
P_t pAd9 = { "Adr[9]", &locos[9].adr, 0, 0, & locos[9].adr, {sel, sft, inc, dec, dspV}};

#if 0
P_t pHos = { "Host",  (int*)host, 0, 0,  NULL, { __,   sfA, inA, deA, dspA }};
P_t pPrt = { "Port",  &port,      0, 0,  NULL, { __,   sft, inc, dec, dspV }};

P_t pSsd = { "SSID",  (int*)ssid, 0, 0,  NULL, { __,   sfA, inA, deA, dspA }};
P_t pPsw = { "Pass",  (int*)pass, 0, 0,  NULL, { __,   sfA, inA, deA, dspA }};
#endif

// -------------------------------------------------------------------
// menus
#if 0
Menu_t menuComm [] = {
    { "Host",   "",      T_STR,    (void*) & pHos },
    { "Port",   "",      T_PARAM,  (void*) & pPrt },

    { "SSid",   "",      T_STR,    (void*) & pSsd },
    { "Pass",   "",      T_STR,    (void*) & pPsw },
    { NULL,     NULL,    T_NULL,   NULL },
};
#endif

// -------------------------------------
// DCC loco addresses
Menu_t menuAdr [] = {
    { "ADDR 0", "",      T_LIST,   (void*) & pAd0 },
    { "ADDR 1", "",      T_LIST,   (void*) & pAd1 },
    { "ADDR 2", "",      T_LIST,   (void*) & pAd2 },
    { "ADDR 3", "",      T_LIST,   (void*) & pAd3 },
    { NULL,     NULL,    T_NULL,   NULL },
};

// -------------------------------------
// main menu
Menu_t menuMain [] = {
 // { NULL,      NULL,   T_NONE,   NULL },
 // { "Loco",    "Addr", T_MENU,   (void*) menuAdr, &loco },
#if 0
    { "Comm",    "Cfg",  T_MENU,   (void*) menuComm },
#endif
    { "Cars",    "",     T_PARAM,  (void*) & pCar },
    { "Loco",    "Addr", T_MENU,   (void*) menuAdr },
    { "Loco",    "Idx",  T_PARAM,  (void*) & pLoc },
    { "Tonnage", "not used",     T_PARAM,  (void*) & pTon },
    { "Options", "",     T_NONE,   NULL },
    { "Version", version, T_NONE,   NULL },
    { "",        "",     T_NULL,   NULL },
};

// menuTop makes menuMain accessible externally
Menu_t *menuTop = & menuMain [0];

It is, but your approach is pure C (which is OK), not OO as discussed above.

That is actually the kind of solution I was hoping existed, it looks just like I imagined a solution would look. I tried to compile the code you gave and it fails with the error:

'func_t' does not name a type; did you mean 'fpos_t'?

I am using the Arduino IDE V1.8.19 and MegaCoreX.

probably Func_t with a capital F is needed

I don't see how that changes anything, I tried anyway and got:
'Func_t' does not name a type; did you mean 'FUSE_t'?

what did you compile ?

(I need to go - sailing today - so won't be able to read in a while)

I took @vvb333007 's code exactly as it is and placed in in my header file as in my OP, commented out my class and compiled. I suspect that was a naïve thing to do but I don't know what else to try with it.

Sailing sounds like fun. Never done it myself though. A friend's daughter is some kind of sailing champion at her university.

The code I posted was just the proof of concept. It shows that you can get an address of a non-static member function and call it. It also shows that this pointer is accessible within these functions (unlike static member functions)

#include <stdlib.h>
#include <stdio.h>

typedef void (*func_t)( );


class SomeClass {

	public:
		func_t ptrs[2];

	
		void func1() { printf("func1() : hello! I am at %p\n", this->ptrs[0]); };
		void func2() { printf("func2() : hello! I am at %p\n", this->ptrs[1]); };
		
		SomeClass() {

			ptrs[0] = (func_t)&func1;
			ptrs[1] = (func_t)&func2;
		};
};



int
main() {


	SomeClass cls;

	
	func_t func;

	func = cls.ptrs[0];

	func();

	func = cls.ptrs[1];

	func();

	return 0;
}