Again, if you're running on an AVR and not using the special pgm-access functions (pgm_read_word(), etc), then you're just fooling yourself.
I think you are right ... I've done some more testing and added many more entries into the function pointer array and SRAM is increasing - I can't explain why it didn't in my earlier tests. However I can now add the pointer array to PROGMEM (my original question) via the following:
In the header file change the above to:
static const fptr func[] PROGMEM;
and in the Class CPP file change the above to:
const menu::fptr menu::func[] PROGMEM = {
&menu::function1, &menu::function2, ....}
of course this now requires the special pgm-access functions (pgm_read_word(), etc) to access the pointers.
(note: function1, function2 etc. can be private functions ... not sure what happened with my earlier testing - anyway I am happy as the code works with a function pointer array in seemingly SRAM or PROGMEM).
Well, I had to get a little under-handed in order to pull the values from PROGMEM and then get them into a variable of the proper pointer type. None of my attempts using the various types of C++ casting would compile. So, used brute force with memcpy(). The code below works with the pointers in PROGMEM. Interesting as an exercise, but it still appears that the actual value added of doing so hovers around nil.
class TestClass;
typedef void (TestClass::*MemberFunctionPointer)();
class TestClass {
public:
private:
static const MemberFunctionPointer functPtr[2];
void action0() {
Serial.println("Action 0");
}
void action1() {
Serial.println("Action 1");
}
public:
MemberFunctionPointer doAction(int8_t a) {
MemberFunctionPointer ptr;
void *xxx;
if ((a < 0) || (a > 1)) {
Serial.print("Illegal Action: ");
Serial.println(a);
return nullptr;
}
Serial.print("Doing ");
Serial.print(a);
Serial.println(":");
xxx = (void *)pgm_read_word(functPtr + a);
memcpy(&ptr, &xxx, 2);
(this->*ptr)();
return ptr;
}
};
const MemberFunctionPointer PROGMEM TestClass::functPtr[2] = {&TestClass::action0, &TestClass::action1};
TestClass dog;
TestClass *objectPtr = &dog;
void setup() {
MemberFunctionPointer ptr;
Serial.begin(115200);
delay(1000);
Serial.println("Starting");
Serial.println();
for (int8_t i = -1; i < 3; i++) {
ptr = dog.doAction(i);
if (ptr) {
(dog.*ptr)();
(objectPtr->*ptr)();
}
Serial.println();
}
}
void loop() {}
I'm surprised that this doesn't compile (but it doesn't):
ptr = (MemberFunctionPointer)(void *)pgm_read_word(functPtr + a);
An alternative to copying the pointer memory is to use a 'union' to assign two meanings to the same memory:
MemberFunctionPointer doAction(int8_t a)
{
union
{
MemberFunctionPointer mfp;
void *vp;
} ptr;
if ((a < 0) || (a > 1))
{
Serial.print("Illegal Action: ");
Serial.println(a);
return nullptr;
}
Serial.print("Doing ");
Serial.print(a);
Serial.println(":");
ptr.vp = (void *)pgm_read_word(functPtr + a);
(this->*ptr.mfp)();
return ptr.mfp;
}
};
Yea, a union would automatically adjust to accommodate 4-byte pointers on a 32-bit machine. My memcpy method should have been:
memcpy(&ptr, &xxx, sizeof(ptr));