Go Down

Topic: Populating an array with functions  (Read 348 times) previous topic - next topic

sterretje

You could force that by defining all of them the same way and have the individual ones only use the arguments they need.
How about using a void pointer for the argument? That way you can pass anything to the functions (simple types, structs, ...); each function knows what to do with the argument.

Simple example
Code: [Select]
// function prototypes
bool funcWithFloat(void *params);
bool funcWithInt(void *params);

// array of function pointers
bool (*fp[])(void*)
{
  funcWithFloat,
  funcWithInt,
};

void setup()
{
  Serial.begin(57600);
  float pi = 3.14;
  fp[0](&pi);
}

void loop()
{
  static int cnt = 0;
  fp[1](&cnt);
  cnt++;
  delay(500);
}

bool funcWithFloat(void *params)
{
  float *f = (float*)params;
  Serial.print("Printing a float: ");
  Serial.println(*f);
  return true;
}

bool funcWithInt(void *params)
{
  int *i = (int*)params;

  Serial.print("Printing an integer: ");
  Serial.println(*i);
  return true;
}
If you understand an example, use it.
If you don't understand an example, don't use it.

Electronics engineer by trade, software engineer by profession. Trying to get back into electronics after 15 years absence.

PieterP

How about using a void pointer for the argument? That way you can pass anything to the functions (simple types, structs, ...); each function knows what to do with the argument.
That would undermine the entire type system. I think casting to void * like that indicates bad design.
I'm pretty sure there's a better way to achieve what OP wants to do.

Functions that operate on different types should not be in the same array.
It's too easy to call the wrong function with the wrong type. The code will have undefined behavior, and the compiler won't even tell you where it went wrong.

Even if you don't agree that they shouldn't be in the same array, there are better options, like std:: variant if you have access to the STL, or a tagged union. The compiler still can't really help you, but at least you can check the type at runtime.

Nikosant03

Even though I think this is not the right approach for your problem, here's an example of how to create an array of function pointers:

Thank you for your answer!!

What is I have different data types function, fo example int and float?

PieterP

What is I have different data types function, fo example int and float?
That would undermine the entire type system.

[...]

Functions that operate on different types should not be in the same array.
It's too easy to call the wrong function with the wrong type. The code will have undefined behavior, and the compiler won't even tell you where it went wrong.

Even if you don't agree that they shouldn't be in the same array, there are better options, like std:: variant if you have access to the STL, or a tagged union. The compiler still can't really help you, but at least you can check the type at runtime.

gfvalvo

What is I have different data types function, fo example int and float?
As pointed out, if you want to put the function pointers in to an array, the functions must all have the same signature. You could force that by defining all of them the same way and have the individual ones only use the arguments they need.
No technical questions via PM. They will be ignored. Post your questions in the forum so that all may learn.

steing

You may make an array of functions that takes variants as input and returns variants.
std::variant is c++17, boost library provide fully functional variants for older versions.

Nikosant03

Hi guys, based on your suggestions I tried to implement a sketch that creates an array of function pointers.
For this example, I used six digital pins and six functions. Assume that at every pin a sensor is connected and an IF statement checks the state of each pin. If the state is HIGH then the address of the function related with the pin-sensor is allocated on the function array in a specific index. After checking all the pins I have ended up with a function array pointers and finally, I invoke each function from the array. 

I am not sure if my procedure is correct and I believe that maybe there is a better solution. I would appreciate your suggestions. The whole concept of array function pointer is totally new to me so please do not snub me  :)

Code: [Select]
[code]


typedef int(*myFuncInt)();
typedef float(*myFuncFloat)();

myFuncInt FuncArrayInt[5];
myFuncInt FuncInt;

myFuncFloat FuncArrayFloat[5];
myFuncFloat FuncFloat;


void setup() {

  Serial.begin(115200);

  pinMode(5, INPUT);
  pinMode(6, INPUT);
  pinMode(7, INPUT);
  pinMode(8, INPUT);
  pinMode(9, INPUT);
  pinMode(10, INPUT);
}

void loop() {

  myFuncInt FuncArrayInt[5] = {0, 0, 0, 0, 0};
  myFuncFloat FuncArrayFloat[5]={0, 0, 0, 0, 0};


  byte i = 0; //initializer
  byte u = 0; //initializer
  byte EntriesCounterInt = 0;
  byte EntriesCounterFloat = 0;

  //Populate FuncArrayInt

  if (digitalRead(5) == HIGH) { //Sensor 1

    FuncArrayInt[i] = sum1;
    i++;
    EntriesCounterInt++;

  }
  if (digitalRead(6) == HIGH) { Sensor 2
    FuncArrayInt[i] = sub1;
    i++;
    EntriesCounterInt++;

  }
  if (digitalRead(7) == HIGH) {  Sensor 3
    FuncArrayInt[i] = sum2;
    EntriesCounterInt++;

  }

  Serial.print("Number of int entries: "); Serial.println(EntriesCounterInt);
  //---------------------------------------------------------------------------------------


  //Populate FuncArrayFloat

  if (digitalRead(8) == HIGH) { //Sensor 4

    FuncArrayFloat[u] = mult1;
    u++;
    EntriesCounterFloat++;

  }
  if (digitalRead(9) == HIGH) { //Sensor 5
    FuncArrayFloat[u] = mult2;
    u++;
    EntriesCounterFloat++;

  }
  if (digitalRead(10) == HIGH) { //Sensor 6
    FuncArrayFloat[u] = divide1;
    EntriesCounterFloat++;
  }

  Serial.print("Number of float entries: "); Serial.println(EntriesCounterFloat);


  //----------------------------------------------------------------------------------------

  if (EntriesCounterInt == 0 ) {
    Serial.println("Nothing to print out for int functions");
  }

  if (EntriesCounterFloat == 0) {
    Serial.println("Nothing to print out for float functions");
  }

  //***************** Print the results from the populated arrays *****************

  for (int z = 0; z < EntriesCounterInt; z++) {  //Prints the result of every function addressed on the FuncArrayInt

    FuncInt = FuncArrayInt[z];
    Serial.println(FuncInt());
  }


  for (int k = 0; k < EntriesCounterFloat; k++) {  //Prints the result of every function addressed on the FuncArrayFloat

    FuncFloat = FuncArrayFloat[k];
    Serial.println(FuncFloat());
  }

  delay(3000);
  Serial.println();

}


//FUNCTIONS #6

int sum1() { // Func0

  int a = 2; int b = 3;

  return a + b;
}

int sub1() {  // Func1

  int c = 89; int d = 10;

  return c - d;
}

int sum2() { // Func2

  int e = 7; int f = 4;

  return e + f;
}

float mult1() { // Func3

  float g = 2.3; float h = 4.2;

  return g * h;
}

float mult2() { // Func4

  float i = 5.3; float j = 6.2;

  return i * j;
}

float divide1() { // Func5

  float k = 5.8; float l = 2.1;

  return k / l;
}




[/code]

gfvalvo

#22
Aug 16, 2019, 03:54 pm Last Edit: Aug 16, 2019, 06:26 pm by gfvalvo
You may make an array of functions that takes variants as input and returns variants.
std::variant is c++17, boost library provide fully functional variants for older versions.
I agree with this opinion: std::visit is everything wrong with modern C++. The std::variant / std::visit construct is horrendously over-complicated for the simple "ask" of wanting to know what type the union is holding. I think I'd just go with the Good Old 'C' method (gasp) of having a tag tell me the type. I'll take responsibility for not shooting myself in the foot. So, this might offend the C++ purists, but:
Code: [Select]
#include <Arduino.h>

struct SensorResult {
  union {
    float floatResult;
    uint8_t byteResult;
    int32_t longResult;
  };
  enum Type {
    FLOAT, BYTE, LONG
  };
  Type tag;
};

typedef void (*ReadSensor)(SensorResult *);

void floatFunction(SensorResult *);
void byteFunction(SensorResult *);
void longFunction(SensorResult *);

ReadSensor sensorFunctions[] = { floatFunction, byteFunction, longFunction };

const uint8_t numSensors = sizeof(sensorFunctions) / sizeof(sensorFunctions[0]);

void setup() {
  SensorResult result;
  Serial.begin(115200);
  delay(1000);

  for (uint8_t i = 0; i < numSensors; i++) {
    sensorFunctions[i](&result);
    Serial.print("Sensor #");
    Serial.print(i);
    Serial.print(": ");
    switch (result.tag) {
      case SensorResult::FLOAT:
        Serial.print(" Float Result = ");
        Serial.println(result.floatResult, 5);
        break;

      case SensorResult::BYTE:
        Serial.print(" Byte Result = ");
        Serial.println(result.byteResult);
        break;

      case SensorResult::LONG:
        Serial.print(" Long Result = ");
        Serial.println(result.longResult);
        break;
    }
  }
}

void loop() {
}

void floatFunction(SensorResult *r) {
  r->floatResult = 3.14159;
  r->tag = SensorResult::FLOAT;
}
void byteFunction(SensorResult *r) {
  r->byteResult = 255;
  r->tag = SensorResult::BYTE;
}
void longFunction(SensorResult *r) {
  r->longResult = -(1UL << 18);
  r->tag = SensorResult::LONG;
}


Output:
Code: [Select]
Sensor #0:  Float Result = 3.14159
Sensor #1:  Byte Result = 255
Sensor #2:  Long Result = -262144
No technical questions via PM. They will be ignored. Post your questions in the forum so that all may learn.

Nikosant03

Thank you so much!!

Is there any way to populate this array ReadSensor sensorFunctions[] dynamically?

Because I am going to use this sketch with many modules and the number and type of sensors will vary. Therefore, in the case of two sensors connected (I have the sketch to recognize the type of sensor connected in each digital pin), the array will be populated with the corresponding functions.

gfvalvo

Just do what you did before. Set the array's size statically and then assign values to its elements in your code.
No technical questions via PM. They will be ignored. Post your questions in the forum so that all may learn.

Nikosant03

Thanks!! I am going to give a try and I will return with feedback!!

By the way, what is the double colon you use in switch case?

Code: [Select]
case SensorResult::FLOAT:

This is how you access FLOAT enum inside the SensorResult struct?


steing

#26
Aug 17, 2019, 08:14 pm Last Edit: Aug 17, 2019, 08:15 pm by steing
I agree with gfvalvo.
His 'variant' is beetter than the boost version for your purpose.
Also, the arduino boost library (the one I found) has not been maintained for years.

good luck!

Go Up