How do I define an array of function pointers in PROGMEM within a class?

I am looking to have an array of function pointers in PROGMEM that are accessed within a class. I can successfully use PROGMEM for other data types but not for an array of function pointers. (I can use PROGMEM for an array of function pointers not in a class). I'd appreciate some help or perhaps a simple worked example. Many thanks.

Below is testClass.h

typedef void(*GeneralFunction) ();

class testClass
{
private:

public:
void doAction0();
void doAction1();
void doAction2();

testClass();
~testClass();
const static GeneralFunction doActionsArray PROGMEM;
};

Below is testClass.cpp:

#include "testClass.h"

void testClass::doAction0()
{
return;
}

void testClass::doAction1()
{
return;
}

void testClass::doAction2()
{
return;
}

testClass::testClass()
{
}

testClass::~testClass()
{
}

const GeneralFunction testClass::doActionsArray PROGMEM =
{
testClass::doAction0,
testClass::doAction1,
testClass::doAction2,
};

Below are the compiler messages:

testClass.h: 19:46: error: expected ';' at end of member declaration
Error compiling project sources
Debug build failed for project 'testCodeArduino'
const static GeneralFunction doActionsArray PROGMEM

testClass.h: 19:48: error: 'PROGMEM' does not name a type
const static GeneralFunction doActionsArray PROGMEM

testClass.cpp: 27:51: error: expected initializer before 'PROGMEM
const GeneralFunction testClass*: doActionsArray PROGMEM =

Please remember to use Code Tags!!!

Because of the way instance functions are implemented, you can't get a pointer to one like you can a regular function. It will work if you make them static to the class:

typedef void(*GeneralFunction) ();

class testClass
{
  private:

  public:
    static void doAction0();
    static void doAction1();
    static void doAction2();


    testClass();
    ~testClass();
    const static GeneralFunction doActionsArray[] PROGMEM;
};

void testClass::doAction0()
{
  return;
}

void testClass::doAction1()
{
  return;
}

void testClass::doAction2()
{
  return;
}

testClass::testClass()
{
}


testClass::~testClass()
{
}

const GeneralFunction testClass::doActionsArray[] PROGMEM =
{
  testClass::doAction0,
  testClass::doAction1,
  testClass::doAction2,
};

testClass testObject;
void setup() {
}

void loop() {
}

Or, you could define a helper class and store pointers to instances of that class in PROGMEM. Then, after pulling the pointer to one of these objects from PROGMEM, use '->' notation to call its instance function.

you can try

#include <iostream>

using namespace std;

class testClass
{
private:

public:
   void doAction0();
   void doAction1();
   void doAction2();


   testClass();
   ~testClass();
   typedef void (testClass::*GeneralFunction) ();
    GeneralFunction doActionsArray[3]=
       {&testClass::doAction0,   &testClass::doAction1,    &testClass::doAction2};
    void action(int i);
};

void testClass::doAction0()
{
    cout << "action0" << endl;
   return;
}

void testClass::doAction1()
{
    cout << "action1" << endl;   return;
}

void testClass::doAction2()
{
   return;
}

testClass::testClass()
{

}

void testClass::action(int i)
{
    cout << "action " << i << " = " ;
    (this->*(doActionsArray[i]))();
}


testClass::~testClass()
{
}


/*const testClass::GeneralFunction doActionsArray[] =
{
   &testClass::doAction0,
   &testClass::doAction1,
   &testClass::doAction2,
};*/

int main(void)
{
    cout << " start "<< endl;
    testClass t;
    t.doAction0();
    t.action(0);
    t.action(1);
    cout << "end"<< endl;}

using GNU gcc 5.1.0 gives

 start
action0
action 0 = action0
action 1 = action1
end

horace:
you can try

But that doesn't produce the desired goal of having the function table in PROGMEM.

does this do what is required?

class testClass
{
private:

public:
   void doAction0();
   void doAction1();
   void doAction2();


   testClass();
   ~testClass();
   typedef void (testClass::*GeneralFunction) ();
   const GeneralFunction doActionsArray[3] =
       {&testClass::doAction0,   &testClass::doAction1,    &testClass::doAction2}; PROGMEM 
    void action(int i);
};

void testClass::doAction0()
{
    Serial.println(  "action0" );
   return;
}

void testClass::doAction1()
{
    Serial.println(  "action1");   return;
}

void testClass::doAction2()
{
   return;
}

testClass::testClass()
{

}

void testClass::action(int i)
{
        Serial.print( "action = " ) ;
    (this->*(doActionsArray[i]))();
}


testClass::~testClass()
{
}


/*const testClass::GeneralFunction doActionsArray[] =
{
   &testClass::doAction0,
   &testClass::doAction1,
   &testClass::doAction2,
};*/

void setup() {

  Serial.begin(115200);
    Serial.println( " start ");
    testClass t;
    t.doAction0();
    t.action(0);
    t.action(1);
    Serial.println(  "end"); // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:

}

when run serial monitor gives

 start 
action0
action = action0
action = action1
end

horace:
does this do what is required?

class testClass

{
private:

public:
  void doAction0();
  void doAction1();
  void doAction2();

testClass();
  ~testClass();
  typedef void (testClass::*GeneralFunction) ();
  const GeneralFunction doActionsArray[3] =
      {&testClass::doAction0,   &testClass::doAction1,    &testClass::doAction2}; PROGMEM
   void action(int i);
};

void testClass::doAction0()
{
   Serial.println(  "action0" );
  return;
}

void testClass::doAction1()
{
   Serial.println(  "action1");   return;
}

void testClass::doAction2()
{
  return;
}

testClass::testClass()
{

}

void testClass::action(int i)
{
       Serial.print( "action = " ) ;
   (this->*(doActionsArray[i]))();
}

testClass::~testClass()
{
}

/const testClass::GeneralFunction doActionsArray[] =
{
  &testClass::doAction0,
  &testClass::doAction1,
  &testClass::doAction2,
};
/

void setup() {

Serial.begin(115200);
   Serial.println( " start ");
   testClass t;
   t.doAction0();
   t.action(0);
   t.action(1);
   Serial.println(  "end"); // put your setup code here, to run once:

}

void loop() {
 // put your main code here, to run repeatedly:

}



when run serial monitor gives


start
action0
action = action0
action = action1
end

I'm scratching my head over WHY that even compiles! In c++, it is generally disallowed to take the address of a non-static member function, so what does the compiler allow creation of that array? From a purely logical standpoint, it makes sense, since you're taking the address of the function, and there is ONLY one copy of the code, no matter how many instances of the class are instantiated.
I'm also confused by the fact that all member functions have a "hidden" argument, the "this" pointer, which that code side-steps. It would not be possible to access any non-static member data through the functions called by action().
Regards,
Ray L.

Thanks for the suggestions so far however the proposed code does not work - in fact it will not compile for an Arduino .. Any other ideas gratefully received.

Chris.

chism2018:
.. Any other ideas gratefully received.

Post the exact code that you're trying and the associated compiler errors.

I built the code of #4 using Arduino IDE 1.8.5 and ran it on a UNO

The code in Reply #1 by gfvalvo compiles for me without error or warning. Macintosh, Arduino 1.8.7, Arduino UNO.

Thanks - the code does compile when placed in a single .ino file. However when I place the class in a separate CPP file it will not compile ... what am I doing wrong?

Many thanks.

Compiler error message is:

Arduino: 1.8.5 (Windows 10), Board: "Arduino/Genuino Uno"

sketch\testClass.cpp:15:84: error: 'PROGMEM' does not name a type

{&testClass::doAction0, &testClass::doAction1, &testClass::doAction2}; PROGMEM

^

sketch\testClass.cpp:40:29: error: no 'void testClass::action(int)' member function declared in class 'testClass'

void testClass::action(int i)

^

exit status 1
Error compiling for board Arduino/Genuino Uno.

CPP file

class testClass
{
private:

public:
void doAction0();
void doAction1();
void doAction2();

testClass();
~testClass();
typedef void (testClass::*GeneralFunction) ();
const GeneralFunction doActionsArray[3] =
{&testClass::doAction0, &testClass::doAction1, &testClass::doAction2}; PROGMEM
void action(int i);
};

void testClass::doAction0()
{
//Serial.println( "action0" );
return;
}

void testClass::doAction1()
{
// Serial.println( "action1"); return;
}

void testClass::doAction2()
{
return;
}

testClass::testClass()
{

}

void testClass::action(int i)
{
// Serial.print( "action = " ) ;
(this->(doActionsArray))();*
}
testClass::~testClass()
{
}
.ino file
#include "testClass.cpp"
void setup() {

  • Serial.begin(115200);*
  • Serial.println( " start ");*
  • testClass t;*
  • t.doAction0();*
  • t.action(0);*
  • t.action(1);*
  • Serial.println( "end"); // put your setup code here, to run once:*
    }
    void loop() {
  • // put your main code here, to run repeatedly:*
    }

you probably require

#include "arduino.h"

at the top of your testClass.cpp file

you could put the class declaration in a testClass.h file, the class definition in the testClass.cpp file and only include the .h testClass.h file in your main .ino file

yep - you're right, that now works ... so obvious I didn't see it. Many thanks for the code - a great help.

Chris.

Observations:

  1. If you’re going to define all members within the class declaration, it should be a .h file, not .cpp.

  2. I don’t think the PROGMEM here is doing anything and I believe the pointer array is in RAM, not PROGMEM:

const GeneralFunction doActionsArray[3] = {&testClass::doAction0,   &testClass::doAction1,    &testClass::doAction2}; PROGMEM

Reasons:
a. The “PROGMEM” attribute is AFTER the ‘;’.

b. When I compiled with and without the “PROGMEM”, the memory usage statistics were the same.

c. If the array really was in PROGMEM, the following statement would not work because pulling information from PROGMEM requires special code / functions on an AVR processor, hence the array must be in RAM:

(this->*(doActionsArray))();

I agree - this is not in PROGMEM ... I was a little hasty with my last comment .. it did compile but the it doesn't seem to put it in PROGMEM.

So the original problem remains How do I define an array of function pointers in PROGMEM within a class?

Thanks.

chism2018:
So the original problem remains How do I define an array of function pointers in PROGMEM within a class?

IMO, there’s little value in doing so. Pointers are small (2 bytes on AVR). Keep them handy in RAM and avoid having to use the special functions to pull them out of PROGMEM. If what they point to is big, put THAT in PROGMEM and pull out (using the pointers in RAM) only when needed.

On ARM-based machines, RAM and PROGMEM are in the same address space, so no special functions needed. If able, the compiler will automatically put stuff in Flash when it sees the ‘const’ specifier.

Ok - thanks for the comments and I understand your point. Is it possible to put an array of pointers within a class into PROGMEM?

Also, if I chose to have an array of pointers in RAM … but defined within a class how would I do this?

Thanks.

chism2018:
Ok - thanks for the comments and I understand your point. Is it possible to put an array of pointers within a class into PROGMEM?

Also, if I chose to have an array of pointers in RAM ... but defined within a class how would I do this?

Thanks.

A pointer to what?

They'd effectively be singletons, so if it's at all possible, they'd have to be static, I think.

I seem to have figured out a solution to my problem .... I must confess I don't fully understand exactly why it works .. but it compiles and seems to run correctly in my hardware; it does not seem to increase SRAM.

class header file (the class is called menu):

.
.
.

public:
typedef void(menu::*fptr)(uint8_t, uint8_t);

static const fptr func;

void function1(uint8_t type, uint8_t flags);
void function2(uint8_t type, uint8_t flags);

class CPP file:

const menu::fptr menu::func = {
&menu::function1, &menu::function2, ....
};

.
.
.

void menu::somefunction() {

.
.
.
// to access function2 in the array within the class

(*this.*func[1])(0, 0);

.
.
.

}

If I remove the pointer array and go back to simply calling the functions directly the program memory reduces and the SRAM remains unchanged.

I'll do some more testing and hopefully verify this is not some sort of fluke (which would be disappointing after the time spent trying to figure out a solution).

(Some of the things I discovered I had to change to make this work were to make the functions and the array public in addition to specifying the typedef within the class).

Thanks to all for their comments.