Passing a function another function

Hello everybody!

So, I am trying to get my checkglass(); function to accept another function as an input condition, so it can use it throughout it's switch case parameters. In my usage, the function I will be passing will be a drink recipe. If the recipe is a shot, dataread counts how many shot glasses are present via switches attached by ShiftIn. It then rotates the wheel between each shot, pouring the passed recipe each time before finally returning the wheel back to it's starting position. If I can figure out how to pass a function another function, I can use the checkglass(); function like this:

checkglass(adiosMFer);

And that will make things very simple for me when I am typing up 200 some recipes. The recipe functions have no input conditions.
Here is what I tried, and it didn't work:

void checkglass(void (*f))
{

 switch (dataread)
  {
  case 1:
   Serial.println("1 shotglass present");
   turnwheel(1);
   (*f);
   turnwheel(0);
    break;

  case 3:
   Serial.println("2 shotglasses present");
   turnwheel(1);
   (*f);
   turnwheel(2);
   (*f);
   turnwheel(0);
    break;
    
  case 7:
   Serial.println("3 shotglasses present");
   turnwheel(1);
   (*f);
   turnwheel(2);
   (*f);
   turnwheel(3);
   (*f);
   turnwheel(0);
    break;
    
  case 15:
   Serial.println("4 shotglasses present");
   turnwheel(1);
   (*f);
   turnwheel(2);
   (*f);
   turnwheel(3);
   (*f);
   turnwheel(4);
   (*f);
   turnwheel(0);
    break;
    
  case 31:
   Serial.println("5 shotglasses present");
   turnwheel(1);
   (*f);
   turnwheel(2);
   (*f);
   turnwheel(3);
   (*f);
   turnwheel(4);
   (*f);
   turnwheel(5);
   (*f);
   turnwheel(0);
    break;
    
  case 32:
   Serial.println("1 old fashioned glass present");
   turnwheel(6);
   (*f);
   turnwheel(0);
    break;
    
  case 64:
   Serial.println("1 highball glass present");
   turnwheel(7);
   (*f);
   turnwheel(0);
    break;

     }
}

any suggestions? The error code comes up as:

barbotV18.ino: In function 'void loop()':
barbotV18:218: error: 'checkglass' was not declared in this scope
barbotV18.ino: In function 'void checkglass(void*)':
barbotV18:1288: error: 'void*' is not a pointer-to-object type

However it only tells me "'checkglass' was not declared in this scope" when I make it

void checkglass(void (*f))

if I leave it

void checkglass()

It doesn't give me any crap about not being declared in this scope. Additionally, "'void*' is not a pointer-to-object type" is a mystery to me... I still absolutely blow at using pointers. I'll get it eventually... I hope.

Hi

The best thing is to typedef your function declaration. This makes the code more readable.
Then: In C you just pass a function pointer, not the function itself.
But the good thing is, C treats function pointer just like functions, so a fn-ptr can be called like a function.

typedef void f_type(void);

void g( f_type *f) {
  f();
}

void fff(void) {
  puts("abc");
}

int main(void) {
  g(fff);
  return 0;
}

In the example, g calls fff, which outputs "abc".

Oliver

So I should try

typedef void f_type(void);

void glasscheck( f_type *f) {
  switch (dataread)
  {
  case 1:
   Serial.println("1 shotglass present");
   turnwheel(1);
   (f);
   turnwheel(0);
    break;

  case 3:
   Serial.println("2 shotglasses present");
   turnwheel(1);
   (f);
   turnwheel(2);
   (f);
   turnwheel(0);
    break;
    
  case 7:
   Serial.println("3 shotglasses present");
   turnwheel(1);
   (f);
   turnwheel(2);
   (f);
   turnwheel(3);
   (f);
   turnwheel(0);
    break;
    
  case 15:
   Serial.println("4 shotglasses present");
   turnwheel(1);
   (f);
   turnwheel(2);
   (f);
   turnwheel(3);
   (f);
   turnwheel(4);
   (f);
   turnwheel(0);
    break;
    
  case 31:
   Serial.println("5 shotglasses present");
   turnwheel(1);
   (f);
   turnwheel(2);
   (f);
   turnwheel(3);
   (f);
   turnwheel(4);
   (f);
   turnwheel(5);
   (f);
   turnwheel(0);
    break;
    
  case 32:
   Serial.println("1 old fashioned glass present");
   turnwheel(6);
   (f);
   turnwheel(0);
    break;
    
  case 64:
   Serial.println("1 highball glass present");
   turnwheel(7);
   (f);
   turnwheel(0);
    break;

     }
}


void VodkaOnRocks(void) {
    pour(1, vodkas, 8);
     End();
}

And then call it like

glasscheck(VodkaOnRocks);

?

I think (f); should read f(); in the function, but otherwise that should work. It's either that or (*f)(); but those parentheses should appear at the end anyways, because it's a function call, I think.

Maybe irrelevant, but personally, I find function pointers a little awkward to work with. You are in effect doing code injection in C, when this kind of thing would be a little easier to handle in C++, in my opinion. You could have a base class that wraps the function as a virtual and then inherit the base class and redefine that function in each inherited class. Or you could have a single class with a pointer to another class that executes the function. Surely, the Arduino environment can compile all this, right?

You can certainly use a pointer to a function for this purpose, but I'm a little unsure as to why it's necessary here. I'd expect all you're really doing is specifying ingredients and amounts, so you just need to pass that data, which might well be just an index into an array of structs held in progmem. Even if there are special handling instructions like "stir" or "add ice", these can be specified in the data as flags.

Finally, if there really are drinks that do need a function to handle them, you might consider specifying them in the struct. Most of these function pointers would be null, but it would enable you to override the default handling for drinks that need something unusual. Disclaimer: I haven't done this on an Arduino so I'm not sure of the subtleties that the Harvard architecture brings here - it would likely take a bit of time figuring out with progmem directives.

I'm looking forward to seeing the barbot in action.

You can do it, but you are asking for trouble.

Why does your drink recipe have to be a function ?

@Jim_Socks

I seem to remember some quirkyness to Arduino typedefs, boiling down to requiring that certain functions must be "prototyped" . Here is some working code that shows the technique you are looking for. If you remove the "Arduino Quirk" prototype line, the compile will fail

typedef void (*drinkFun)(void);

void glasscheck( drinkFun fptr);  // Arduino quirk; must be prototyped

void glasscheck( drinkFun fptr){
    // ...
    fptr();
    // ...
}

void VodkaOnRocks(void) {
    Serial.println("Vodka On Rocks Fun");
}
void Margarita(void) {
    Serial.println("Margarita Fun");
}


void setup(){ 
   Serial.begin(19200);
   glasscheck( VodkaOnRocks);
   // or
   drinkFun rita=Margarita;
   glasscheck( rita);
}


void loop(){}

Cheers,
John

edit: renamed functions for domain

Thanks guys!

I worked your code into mine Johncc, and it is telling me

barbotV18:40: error: variable or field 'checkglass' declared void
barbotV18:40: error: 'drinkfunction' was not declared in this scope

How should I declare drinkfunction- what type of pointer is it?

Edit: spelling. Drives me nuts lol

Jim_Socks:
I worked your code into mine Johncc, and it is telling me

barbotV18:40: error: variable or field 'checkglass' declared void

barbotV18:40: error: 'drinkfunction' was not declared in this scope



How should I declare drinkfunction- what type of pointer is it?

Hmm, my code should translate directly into yours, with names of your choosing. If you don't want to post your whole code, probably need to post at least your:

typedef
function prototype
declaration of drinkFunction
declaration of checkglass
code that calls checkglass

John

wildbill:
You can certainly use a pointer to a function for this purpose, but I'm a little unsure as to why it's necessary here. I'd expect all you're really doing is specifying ingredients and amounts, so you just need to pass that data, which might well be just an index into an array of structs held in progmem. Even if there are special handling instructions like "stir" or "add ice", these can be specified in the data as flags.

Finally, if there really are drinks that do need a function to handle them, you might consider specifying them in the struct. Most of these function pointers would be null, but it would enable you to override the default handling for drinks that need something unusual. Disclaimer: I haven't done this on an Arduino so I'm not sure of the subtleties that the Harvard architecture brings here - it would likely take a bit of time figuring out with progmem directives.

I would use a class to represent drinks. That way, you can represent most recipes just by storing different data; but if you really do need special steps for some drinks, you can override the function that does the dispensing and mixing as well - which is neater than using function pointers.

I would use a class to represent drinks. That way, you can represent most recipes just by storing different data; but if you really do need special steps for some drinks, you can override the function that does the dispensing and mixing as well - which is neater than using function pointers.

I did consider that, but I'm not sure how you would initialize the necessary array of classes with the drink data at compile time.

Classes and structs are the same thing, except that the members are private in classes and public in structs until you specify otherwise. So you can initialize classes in exactly the same way as structs. Having said that:

  1. It is more usual to initialise classes by calling their constructors; and

  2. If you are going to make use of function overloading, then you actually need an array of pointers (or references) to objects. Here is one way of achieving that:

const Drink d1("Vodka Martini", 1, 3, ...);
const ShakenDrink d2("Vodka Martini shaken not stirred", 1, 3, ...);
const Drink d3("Screwdriver, 0, 4, ...);
const 
...

const Drink *drinks[] = {&d1, &d2, &d3, ...};

I've assumed you have declared suitable constructors, and that ShakenDrink derives from drink.