Go Down

Topic: Passing a function another function (Read 873 times) previous topic - next topic

Jim_Socks

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:

Code: [Select]

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:

Code: [Select]
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:
Code: [Select]
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
Code: [Select]
void checkglass(void (*f))

if I leave it
Code: [Select]
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. 

olikraus

#1
Feb 10, 2013, 08:08 am Last Edit: Feb 10, 2013, 08:10 am by olikraus Reason: 1
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.

Code: [Select]
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

Jim_Socks

So I should try

Code: [Select]
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

Code: [Select]
glasscheck(VodkaOnRocks);


?

Chaul

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?

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'm looking forward to seeing the barbot in action.

Go Up