Pages: [1]   Go Down
Author Topic: How to Structure Array of Function Pointers  (Read 1819 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Most Google searches result in "array of function pointers in c", and very little for Arduino.  Either these examples/explanations don't work on Arduino, the newer IDE can't handle array or function pointers, or I don't really understand array of function pointers.  I have found this link from here:

http://forum.arduino.cc/index.php/topic,40842.0.html

This thread is ~5 years old and it doesn't seem to work with Arduino 1.0.3, or Arduino 1.0.5.

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

My application is a generic menu library, which would respond to GPIO input and display the button-navigated menu on a LCD/LCD-I2C.

So far, this is what I have, since I've just started a couple days ago and I'm stuck at the array of function pointers:

Code:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Bounce.h>

#define Button_1  9
#define LED_1 13

LiquidCrystal_I2C lcd(0x3F, 20, 4);
Bounce button_1 = Bounce(Button_1, 5);

void (*Page_1)(int foo);

typedef void (* GenericFP)();
GenericFP MenuFP[2] = {Page2, Page3};

int button_1_toggle = LOW;

void setup()
{
  pinMode(Button_1, INPUT);
  pinMode(LED_1, OUTPUT);
 
  lcd.init();
  lcd.backlight();

  Page_1 = &Page1;
}

void loop()
{
  toggle(&button_1, &button_1_toggle);
  Page1(1);
 
  switch(button_1_toggle)
  {
    case HIGH:
      lcd.backlight();
      break;
    case LOW:
      lcd.noBacklight();
      break;
  }
  lcd.setCursor(0,0);
}

void toggle(Bounce* Button, int *ToggleValue)
{
  if (Button->update())
  {
    if (Button->read() == HIGH)
    {
      if (*ToggleValue == LOW)
      {
        *ToggleValue = HIGH;
      }
      else
      {
        *ToggleValue = LOW;
      }
    }
  }
}

void Page1(int foo)
{
};

void Page2(int foo)
{
};

void Page3(int foo)
{
};

Excuse the jumble.  toggle() is a working function that takes input from a switch on pin D9 and toggles an LED on pin D13 and the LCD backlight.

The part that is not working is the array portion starting with the typedef.  The typedef is based on the link above.  The IDE is failing on:
GenericFP MenuFP[2] = {Page2, Page3};

The error message that is presented is "expected primary-expression before 'int'

These two lines are basically what is presented in the link but does not work with the newer IDE.

Any assistance is appreciated.
Thanks in advance.
« Last Edit: June 10, 2013, 04:16:34 pm by daduck748 » Logged

ivrea (to)
Offline Offline
Faraday Member
**
Karma: 87
Posts: 4956
miaaao ^-^
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

typedef wrong. Must be:
  typedef void (* GenericFP)(int);
It's not so generic, it is a pointer to a function with one int parameter and no return

If you declare a typedef, use it also for the pointer. It's more readable:
void (*Page_1)(int foo);    =>      GenericFP *Page_1=NULL;

Code:
typedef void (* GenericFP)(int);
GenericFP *Page_1=NULL;
GenericFP MenuFP[2] = {Page2, Page3};

P.S. Please @daduck use special tag for the code (button with #)
« Last Edit: June 10, 2013, 02:05:01 am by nid69ita » Logged

my name is IGOR, not AIGOR

Des Moines, WA - USA
Offline Offline
God Member
*****
Karma: 25
Posts: 779
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

<http://forum.arduino.cc/index.php?topic=170230.msg1266379#msg1266379>
Logged

Leeds, UK
Offline Offline
Edison Member
*
Karma: 80
Posts: 1726
Once the magic blue smoke is released, it won't go back in!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Here is an example of function pointers:
Code:
typedef void (* GenericFP)(int); //function pointer prototype to a function which takes an 'int' an returns 'void'

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](1); //call MenuFP element 0 with the parameter 1.  This is equivalent to: Page1(1)
  MenuFP[1](5); //call MenuFP element 1 with the parameter 5.  This is equivalent to: Page2(5)
  MenuFP[2](7); //and so on.                                   This is equivalent to: Page3(7)
}

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

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

void Page3(int foo)
{
  Serial.print("Page3=");
  Serial.println(foo);
}
Logged

~Tom~

Cincinnati, OH
Offline Offline
God Member
*****
Karma: 48
Posts: 812
I'm not bossy...I just know what you should be doing.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

ivrea (to)
Offline Offline
Faraday Member
**
Karma: 87
Posts: 4956
miaaao ^-^
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

my name is IGOR, not AIGOR

Cincinnati, OH
Offline Offline
God Member
*****
Karma: 48
Posts: 812
I'm not bossy...I just know what you should be doing.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

ivrea (to)
Offline Offline
Faraday Member
**
Karma: 87
Posts: 4956
miaaao ^-^
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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?
Logged

my name is IGOR, not AIGOR

Leeds, UK
Offline Offline
Edison Member
*
Karma: 80
Posts: 1726
Once the magic blue smoke is released, it won't go back in!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Code:
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.
Logged

~Tom~

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 485
Posts: 18807
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

This compiles and runs as expected:

Code:
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");
  }
Logged


New Jersey
Offline Offline
Faraday Member
**
Karma: 67
Posts: 3701
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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.
Logged

Cincinnati, OH
Offline Offline
God Member
*****
Karma: 48
Posts: 812
I'm not bossy...I just know what you should be doing.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Nick: You could also do away with the typedef:

Code:
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.
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 485
Posts: 18807
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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. smiley
Logged


Pages: [1]   Go Up
Jump to: