Array of functions?

All,

I'm trying to make use of a 2 line LCD by cycling the content of one of the lines every X iterations. While I could just increase a counter, and check if it's divisible by some number, then display the content based on it, I figured it would be easier to just have an array that had what I wanted to display, then cycle through that.

In this case, I'm using TinyGPS++ as a library, so I'm trying to cycle LAT, LNG, and Time.

Note, I'm not a C programmer by trade... so my (bad) knowledge comes from things like PHP. What seems like it should be doable, doesn't seem to be obvious.

int n = 0;
int display = 0;
char* displayTitle[] = {"TME=","LAT=","LNG="};
char* displayInfo[] = {"gps.time.value()","gps.location.lat()","gps.location.lng()"};

snip

if (gps.time.isUpdated()) {
      lcd.setCursor (0,0);
      if (display == 500) {
         lcd.print(displayTitle[n]);
         lcd.setCursor(4,0);
         lcd.print(displayInfo[n]);
         display=0;
         if (n < 2) {
           n++;
         } else {
           n = 0;
         }
      } else {
         display++;
      }

Obviously "char*" is wrong, since that's just setting them as strings. Is there any way to have those evaluated at the time they're displayed, so they display the current value? I know "&" can be used to do this in some languages, but I can't seem to figure out how to do this in this case... I get a compile error.

Thanks!

displayInfo should be an array of pointers to function returning char *. Take care in what pointers you return from those functions.

Try this...

++n %= 3;

Instead of...

        if (n < 2) {
           n++;
         } else {
           n = 0;
         }
char* displayTitle[] = {"TME=","LAT=","LNG="};

try

char displayTitle[][4] = {"TME=","LAT=","LNG="};

DavidOConnor:
displayInfo should be an array of pointers to function returning char *. Take care in what pointers you return from those functions.

Try this...

++n %= 3;

Yeah, guess they're functions rather than variables. still just seeing the actual "string" of "gps.location.lat", etc rather than the latitude.

Code looks like this now (here it is in full).

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#include <TinyGPS++.h>
TinyGPSPlus gps;

#define I2C_ADDR    0x27
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7

int n = 0;
int display = 0;
char* displayTitle[4] = {"TME=","LAT=","LNG="};
char* displayInfo[] = {"gps.time.value()","gps.location.lat()","gps.location.lng()"};
int incomingByte = 0;
int lockPin = 8;
int previousSats = 0;

LiquidCrystal_I2C	lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);

void setup()
{
  Serial.begin(4800);
  lcd.begin (16,2);
  pinMode(lockPin,OUTPUT);


  lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
  lcd.setBacklight(HIGH);
  lcd.home ();
  lcd.setCursor (0,1);  
  lcd.print("Sat #: ");  
}

void loop()
{
  if (Serial.available() > 0) {
    gps.encode(Serial.read());
    if (gps.satellites.value() > 3) {
      digitalWrite(lockPin,HIGH);
    } else {
      digitalWrite(lockPin,LOW);
    }
    if (gps.time.isUpdated()) {
      lcd.setCursor (0,0);
      if (display == 500) {
         lcd.print(displayTitle[n]);
         lcd.setCursor(4,0);
         lcd.print(displayInfo[n]);
         display=0;
         ++n %= 3;
      } else {
         display++;
      }
      lcd.setCursor (8,1);
      lcd.print(" ");
      lcd.setCursor (7,1);
      lcd.print(gps.satellites.value(), DEC);
    }
  }
}

Okay, getting closer.

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#include <TinyGPS++.h>
TinyGPSPlus gps;

#define I2C_ADDR    0x27
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7

int n = 0;
int display = 0;
char* displayTitle[4] = {"TME=","LAT=","LNG="};
typedef void (* GPSdata)(int);
GPSdata displayInfo[3] = {&TinyGPSTime::value, &TinyGPSLocation::lat, &TinyGPSLocation::lng};
int incomingByte = 0;
int lockPin = 8;
int previousSats = 0;

LiquidCrystal_I2C	lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);

void setup()
{
  Serial.begin(4800);
  lcd.begin (16,2);
  pinMode(lockPin,OUTPUT);


  lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
  lcd.setBacklight(HIGH);
  lcd.home ();
  lcd.setCursor (0,1);  
  lcd.print("Sat #: ");  
}

void loop()
{
  if (Serial.available() > 0) {
    gps.encode(Serial.read());
    if (gps.satellites.value() > 3) {
      digitalWrite(lockPin,HIGH);
    } else {
      digitalWrite(lockPin,LOW);
    }
    if (gps.time.isUpdated()) {
      lcd.setCursor (0,0);
      if (display == 500) {
         lcd.print(displayTitle[n]);
         lcd.setCursor(4,0);
         lcd.print(displayInfo[n])();
         display=0;
         ++n %= 3;
      } else {
         display++;
      }
      lcd.setCursor (8,1);
      lcd.print(" ");
      lcd.setCursor (7,1);
      lcd.print(gps.satellites.value(), DEC);
    }
  }
}

Now I'm just getting:

"cannot convert 'uint32_t (TinyGPSTime::)()' to 'void () (int)' in intialization"

I think I understand what it's saying, but I'm not sure what to do about it. Damn C and it's limitations. =P

Here is a hint:

uint32_t (TinyGPSTime:smiley-kiss)()' to 'void (*) (int)

void (*) (int) is a pointer to a function that does not return a value, and takes a single 'int' parameter.

uint32_t (TinyGPSTime::*)() is a pointer to a member function which takes no parameters but returns a uint32_t.

They are completely different function prototypes.

Wrap code that does not format properly in [code][/code] tags. Otherwise :* looks like :* ( or nobbc tags )

pYro_65:
Here is a hint:

uint32_t (TinyGPSTime:smiley-kiss)()' to 'void (*) (int)

void (*) (int) is a pointer to a function that does not return a value, and takes a single 'int' parameter.

uint32_t (TinyGPSTime::*)() is a pointer to a member function which takes no parameters but returns a uint32_t.

They are completely different function prototypes.

Wrap code that does not format properly in [code][/code] tags. Otherwise :* looks like :* ( or nobbc tags )

Sorry, not sure that makes sense either.

Here's what I'm getting:

cannot convert 'uint32_t (TinyGPSTime::*)()' to 'void (*)()' in intialization

So are you saying I should change:

typedef void (* GPSdata)();
GPSdata displayInfo[3] = {&TinyGPSTime::value, &TinyGPSLocation::lat, &TinyGPSLocation::lng};

to

typedef void (* GPSdata)();
GPSdata displayInfo[3] = {uint32_t(&TinyGPSTime::value), uint32_t(&TinyGPSLocation::lat), uint32_t(&TinyGPSLocation::lng)};

Because, that doesn't work either (same error). =/

Or try:

char displayTitle[][5] = {"TME=","LAT=","LNG="};

since you need a null termination character for each string.

econjack:
Or try:

char displayTitle[][5] = {"TME=","LAT=","LNG="};

since you need a null termination character for each string.

what? displayTitle only is the "string" before the actual value returned. from the array of functions displayInfo

Now I just tried:

char* displayInfo[3] = {uint32_t(&TinyGPSTime::value)(), double(&TinyGPSLocation::lat)(), double(&TinyGPSLocation::lng)()};

But that results in:

GPS_Display:21: error: invalid cast from type 'uint32_t (TinyGPSTime::*)()' to type 'uint32_t'
GPS_Display:21: error: invalid cast from type 'double (TinyGPSLocation::*)()' to type 'double'
GPS_Display:21: error: invalid cast from type 'double (TinyGPSLocation::*)()' to type 'double'

Feel like I'm going in circles.

staze:
So are you saying I should change:

typedef void (* GPSdata)();

GPSdata displayInfo[3] = {&TinyGPSTime::value, &TinyGPSLocation::lat, &TinyGPSLocation::lng};




to



typedef void (* GPSdata)();
GPSdata displayInfo[3] = {uint32_t(&TinyGPSTime::value), uint32_t(&TinyGPSLocation::lat), uint32_t(&TinyGPSLocation::lng)};




Because, that doesn't work either (same error). =/

What? No... that is not what I said at all???

The three functions:

  • TinyGPSTime::value
  • TinyGPSLocation::lat
  • TinyGPSLocation::lng

Do they all have the same prototype? As in all take the same parameters ( or none ) and return the same type ( or none ).
Don't just reply yes or no, give details please ( what is/are the prototype(s) ).

If they are the same, you can change your typedef to that prototype.

pYro_65:

staze:
So are you saying I should change:

typedef void (* GPSdata)();

GPSdata displayInfo[3] = {&TinyGPSTime::value, &TinyGPSLocation::lat, &TinyGPSLocation::lng};




to



typedef void (* GPSdata)();
GPSdata displayInfo[3] = {uint32_t(&TinyGPSTime::value), uint32_t(&TinyGPSLocation::lat), uint32_t(&TinyGPSLocation::lng)};




Because, that doesn't work either (same error). =/

What? No... that is not what I said at all???

The three functions:

  • TinyGPSTime::value
  • TinyGPSLocation::lat
  • TinyGPSLocation::lng

Do they all have the same prototype? As in all take the same parameters ( or none ) and return the same type ( or none ).
Don't just reply yes or no, give details please ( what is/are the prototype(s) ).

If they are the same, you can change your typedef to that prototype.

Sorry, I guess I didn't understand what you said.

Sadly, no, TinyGPSTime::value returns uint32_t, the other two TinyGPSLocation::lat and TinyGPSLocation:lng are both double's.

=/

That said, I did this:

char* displayInfo[3] = {uint32_t(&TinyGPSTime::value)(), double(&TinyGPSLocation::lat)(), double(&TinyGPSLocation::lng)()};

And I still get the seemingly illogical invalid cast from type 'uint32_t (TinyGPSTime::*)()' to type "uint32"t'

So, uhh... why would I want to convert uint32_t to uint32_t, or rather, why would I need to?

Give this a try:

char displayTitle[][5] = {"TME=","LAT=","LNG="};
int (*ptr[3])();

void setup() {
  int i;

  Serial.begin(115200);
  ptr[0] = &func1;      // Initialize the array...
  ptr[1] = &func2;
  ptr[2] = &func3;
  
    for (i = 0; i < (sizeof(ptr) / sizeof(ptr[0])); i++)
    (*ptr[i])();

}

void loop() {
}

int func1()
{
  Serial.println("In func1()");
  return 1;
}
int func2()
{
  Serial.println("In func2()");
  return 2;
}
int func3()
{
  Serial.println("In func3()");
  return 3;
}

You can figure out complex definition like:

int (*ptr[3])();

using Purdum's Right-Left Rule. Go to the identifier (ptr) and look to the right. You see [3]. So far: "ptr is an array of three...". Now look left, you see an asterisk (*), so now it's "ptr is an array of three pointers pointing to...". Now look right and you see the two parentheses, so "ptr is an array of three pointers pointing to functions that return...". Look back left, you see int, so the complete definition becomes: "ptr is an array of three pointers pointing to functions that return int.", which is what I think you are asking for.

So, uhh... why would I want to convert uint32_t to uint32_t, or rather, why would I need to?

Becuase "uint32_t" is not the same as "
* *uint32_t (TinyGPSTime::*)()* *
"
As the three functions have different prototypes, they cannot go in an array.

econjack's method is sound, however it will not work due to the problem I've pointed out above.

You can put the two similar ones into an array.

typedef double ( TinyGPSTime::*GPSdata)();
GPSdata displayInfo[] = { &TinyGPSLocation::lat, &TinyGPSLocation::lng };

pYro_65 is correct. If your functions have different return data type specifiers, you would have to use unique pointer to function for each function.

You can have arrays of functions, and pointers to functions. But you really don't want to. Your problem can be solved in much more straightforward ways.

econjack:
Give this a try:

char displayTitle[][5] = {"TME=","LAT=","LNG="};

int (*ptr[3])();

void setup() {
  int i;

Serial.begin(115200);
  ptr[0] = &func1;      // Initialize the array...
  ptr[1] = &func2;
  ptr[2] = &func3;
 
    for (i = 0; i < (sizeof(ptr) / sizeof(ptr[0])); i++)
    (*ptr[i])();

}

void loop() {
}

int func1()
{
  Serial.println("In func1()");
  return 1;
}
int func2()
{
  Serial.println("In func2()");
  return 2;
}
int func3()
{
  Serial.println("In func3()");
  return 3;
}




You can figure out complex definition like:


int (*ptr[3])();




using Purdum's Right-Left Rule. Go to the identifier (*ptr*) and look to the right. You see [3]. So far: "*ptr* is an array of three...". Now look left, you see an asterisk (*), so now it's "*ptr* is an array of three pointers pointing to...". Now look right and you see the two parentheses, so "*ptr* is an array of three pointers pointing to functions that return...". Look back left, you see *int*, so the complete definition becomes: "*ptr* is an array of three pointers pointing to functions that return *int*.", which is what I think you are asking for.

Extremely helpful, alas, same issue. Errors that I'm trying to convert... and as I mentioned before, sadly, one returns uint32_t, and two return double. =(

michinyon:
You can have arrays of functions, and pointers to functions. But you really don't want to. Your problem can be solved in much more straightforward ways.

such as?

GPSdata displayInfo[3] = {&TinyGPSTime::value, &TinyGPSLocation::lat, &TinyGPSLocation::lng};

How can you expect this to work, the address of what is being sought here ?

pYro_65:

So, uhh... why would I want to convert uint32_t to uint32_t, or rather, why would I need to?

Becuase "uint32_t" is not the same as "
* *uint32_t (TinyGPSTime::*)()* *
"

That makes... nearly zero sense... I mean, sure. But if it returns a uint32_t, why would that not be a uint32_t?

So guess I just need to do things the long way, unless there's some simplier way to do it. I guess I'm just too used to non-precompiled languages....