How to Structure Array of Function Pointers

C allows for some pretty complex data definitions. The Right-Left Rule can help figure out what you have really defined:

http://jdurrett.ba.ttu.edu/3345/handouts/RL-rule.html

Would I be able to use the array of function pointers to functions of varying number of arguments by way of
typedef void (* GenericFP)(int) or would this definition need to be modified in some way?

Thanks everyone. I'll have to try your examples and post my results.

daduck748:
Would I be able to use the array of function pointers to functions of varying number of arguments by way of
typedef void (* GenericFP)(int) or would this definition need to be modified in some way?

Thanks everyone. I'll have to try your examples and post my results.

Sorry it's not possible. Every array element must be of same type.
For a function the number of parameters is the signature of the function and is very important for compiler.

nid69ita: What about the ellipsis operator? Also, you could have an array of pointers to functions where the argument is a structure, and then "fill in" only those structure elements that are needed for a particular function, setting the unused members to null or zero might work. Ugly, but it might meet his needs.

Wouldn't that be the same thing as a function pointer with say, 15 arguments, and each function "picking and choosing" the arguments necessary?

However, passing a data structure is much more elegant.

I did come across a forum that talked about an array of function pointers with different arguments. I wish I had saved it. However, it seems things that work in c don't necessarily work under the Arduino platform.

econjack:
nid69ita: What about the ellipsis operator? Also, you could have an array of pointers to functions where the argument is a structure, and then "fill in" only those structure elements that are needed for a particular function, setting the unused members to null or zero might work. Ugly, but it might meet his needs.

Not easy using a structure.
I don't undestand Ellipsis operator. They are for variant argument?

If you mean variadic functions, you can do that, for example:

typedef void (* GenericFP)(void*, ...); //function pointer prototype to a function which takes an 'int' an returns 'void'

void Page1(void* skip, ...);
void Page2(void* skip, ...);
void Page3(void* skip, ...);
GenericFP MenuFP[3] = {&Page1, &Page2, &Page3}; //create an array of 'GenericFP' function pointers. Notice the '&' operator

void setup()
{
  Serial.begin(115200);
}

void loop()
{
  MenuFP[0](NULL,1);
  MenuFP[1](NULL,5,3.444);
  MenuFP[2](NULL,7,2.77,"Hello");
}

void Page1(void* skip, ...)
{
  va_list args;
  va_start(args, skip);
  int foo1 = va_arg(args, int);
  
  Serial.println("Page1=");
  Serial.println(foo1);
}

void Page2(void* skip, ...)
{
  va_list args;
  va_start(args, skip);
  int foo1 = va_arg(args, int);
  float foo2 = (float)va_arg(args, double);
  
  Serial.println("Page2=");
  Serial.println(foo1);
  Serial.println(foo2);
}

void Page3(void* skip, ...)
{
  va_list args;
  va_start(args, skip);
  int foo1 = va_arg(args, int);
  float foo2 = (float)va_arg(args, double);
  const char* foo3 = va_arg(args, const char*);
  
  
  
  Serial.println("Page3=");
  Serial.println(foo1);
  Serial.println(foo2);
  Serial.println(foo3);
}

Note that all functions have to be of the same prototype, hence the void* followed by the variable arguments list. You can just ignore the void* in all of your functions, you just have to make sure that you pass a NULL as the argument - its just there to start the arguments list.

daduck748:
Does anyone know know how to structure array of function pointers for Arduino?

This compiles and runs as expected:

typedef void (* GenericFP)(int);

GenericFP MenuFP[] = {Page1, Page2, Page3};

int i = 0;

void setup()
  {
  Serial.begin (115200);
  }

void loop()
  {
  
  MenuFP [i++] (42);
  
  if (i >= 3)
    i = 0;
    
  delay (1000);
  }

void Page1(int foo)
  {
  Serial.println ("page1");
  }

void Page2(int foo)
  {
  Serial.println ("page2");
  }

void Page3(int foo)
  {
  Serial.println ("page3");
  }

However, it seems things that work in c don't necessarily work under the Arduino platform.

The arduino is programmed in C/C++ using the gcc compiler, so you should expect any C constructs to work as they do elsewhere. It is true that some library functionality is missing to save space - e.g. sprintf can't handle float arguments, but there's no "arduino platform" per se, it's all regular C/C++ under the covers.

Nick: You could also do away with the typedef:

void  (*MenuFP[])(int) = {Page1, Page2, Page3};

int i = 0;

void setup()
  {
  Serial.begin (9600);
  }

void loop()
  {
  
  MenuFP [i++] (42);
  
  if (i >= 3)
    i = 0;
    
  delay (1000);
  }

void Page1(int foo)
  {
  Serial.println ("page1");
  }

void Page2(int foo)
  {
  Serial.println ("page2");
  }

void Page3(int foo)
  {
  Serial.println ("page3");
  }

In the first line, you can use the Right-Left rule I mention above to see that MenuFP is: An array of pointers to functions with an int parameter returning void.

econjack:
Nick: You could also do away with the typedef:

Good point. I was just used to making one because I usually put a function pointer inside a struct (hmmm - maybe that doesn't totally explain it) but I'm sure there is usually a reason for it. :slight_smile:

I'm sorry to bump this thread, but I used your example successfully.
I'm a bit puzzled about the (42) though. It seem to run with any int I fill in.
What does it mean? Milliseconds reserved for each function?

The function takes an int argument. the (42) is that argument.

Still not clear to me.
Why doesn't anything change when I fill in another number?
I mean, if it's an argument, it should do something no?

Sorry for the noob question.

42 is the answer that "deep thought", the computer in the Hitchhiker's Guide to the Galaxy produced. You can give it any number because given that number, it will take a million times as long for the Arduino to figure out what the question was.

When creating a function that returns or accepts (or both) a function pointer, or even then an array of them things can become a mess. Typedefs are always handy (except I hate them being used to define structs that do not provide any additional substance, C-isms).

Imagine trying to explain this function to someone:

void (*(&doStuff(void (*(&in)[5])(int)))[5])(int){
  return in;
}

Whereas typedefs make this nice.

I know, that this thread is more than 5 years old, butr my question refers to Nick Gammon's post #11.

Question : Why does IDE 1.8.8 generating the error message

"ESP8266_Menu_Funktionen_Tes2t.ino:9:4: error: 'Page1' was not declared in this scope
&Page1,
^
ESP8266_Menu_Funktionen_Tes2t.ino:10:4: error: 'Page2' was not declared in this scope
&Page2,
^
ESP8266_Menu_Funktionen_Tes2t.ino:11:4: error: 'Page3' was not declared in this scope
&Page3"

if the code in post #11 is structured like that :

typedef void (* GenericFP)();

GenericFP MenuFP[3] = {
   &Page1, 
   &Page2, 
   &Page3
};

int i = 0;

void setup()
  {
  Serial.begin (115200);
  }

void loop()
  {
  
  MenuFP [i++] ();
  
  if (i >= 3)
    i = 0;
    
  delay (1000);
  }

void Page1()
  {
  Serial.println ("page1");
  }

void Page2()
  {
  Serial.println ("page2");
  }

void Page3()
  {
  Serial.println ("page3");
  }

if you structure "GenericFP MenuFP[3] = {&Page1, &Page2, &Page3};" this way, it compiles without any error and runs as expected

Looks like it's something to do with Arduino's "Helpful" automatic function prototype generation. Try supplying your own prototypes:

void Page1();
void Page2();
void Page3();
typedef void (* GenericFP)();

GenericFP MenuFP[] = {
  &Page1,
  &Page2,
  &Page3
};

Hi gfvalvo,

with your suggested "own prototyping", the compilation run through flawlessly. But still the root cause is somehow unclear to me. This "helpful" automatic prototyping generation in IDE 1.8.8 was preventing me in migrating an older sketch, writting in former IDE times to the new age, and I couldn't imagine, that the error message is based on the difference between
A) GenericFP MenuFP[] = {&Page1, &Page2, &Page3}; and
B) GenericFP MenuFP[] = {
&Page1,
&Page2,
&Page3
};

I bet, that these "new features" can newbies drive nuts.

Anyhow, thank you very much for your help !

Maro