0

I am trying to create an array of structs that contain pairs of Strings and Callbacks the problem I am having is assigning the callback function. I have the following code:

#define UI_ROUTINE_ITEMS 10

struct MenuItem{
    String label;
    typedef void (*callback)();
};

class MenuSystem{
   MenuItem routineMenu[UI_ROUTINE_ITEMS];

   MenuSystem(){
      routineMenu[0] = {"Test", &Test::TestCallback};
   }
}

class Test{
   void TestCallback(){
      Serial.println("Callback called");
   }
}

When I try to compile this I get:

error: no match for 'operator=' (operand types are 'MenuItem' and '<brace-enclosed initializer list>')

I know this is a Syntax issue I just can not figure out how this should be done. I have looked at several examples but I can't figure out how to do this with Classes without using free/malloc, what am I doing wrong?


EDIT


I finally got this working, the problem was that the function that I was trying to call was not static and the declaration in the structure was not quite right. Below is the corrected example:

#define UI_ROUTINE_ITEMS 10

#include <Arduino.h>

struct MenuItem{
    String label;
    void (*callback)(void);
};

class MenuSystem{
   MenuItem routineMenu[UI_ROUTINE_ITEMS];

   MenuSystem(){
      routineMenu[0] = {"Test", &Test::TestCallback};
   }
}

class Test{
   public static void TestCallback(){
      Serial.println("Callback called");
   }
}
1

Several issue with this code. First, use public/protected/private for classes. Second, use a const char* instead of String or even better str_P. It is the usage of String that is giving the compile error. Third, the Test::TestCallback function reference is not possible. It is a member function. To correct that either have an instance or define the callback function as static.

You could define an abstract MenuItem class with the string and a virtual member function. A Menu class would hold a collection of MenuItems. All the static data should be put in program memory.

There is a Menu system in Cosa. Please see the example sketch. This implementation follows the MVC design pattern.

Cheers!

  • That was what I was missing, I forgot that in order to pass a reference like I wanted it needed to be static. I knew it was something simple. Thanks for getting me on the right track, unfortunately I can not figure out how to get the Cosa libraries compiling correctly in eclipse. – Andy Braham Dec 5 '15 at 1:29
0

Your system works in pure C too:

typedef struct _MenuItem
{
    const char *label; // or String if you like
    void (*callback)();
} MenuItem;

void testCallback()
{
    Serial.println("testCallback() Called");
}

MenuItem menu_system[10];    
void setup()
{
    menu_system[0].label    = "Test";
    menu_system[0].callback = testCallback;

    ... # more menu definitions with callbacks
}

void loop()
{
    unsigned int user_menu_selection;

    ... # some menu code, user selection in <user_menu_selection>

    menu_system[user_menu_selection].callback();  // TODO: error checking
}

It might be a good idea to store any static Menu Strings into PROGMEM.

  • In pure C I would use __flash const instead of PROGMEM: the compiler knows how to properly dereference a __flash pointer, whereas with PROGMEM you have to explicitly call one of the pgm_read_*() functions. – Edgar Bonet Feb 4 '16 at 16:10
0

A pointer to a member instance function is possible:

void (Some_class::*Some_fnc_ptr)();

And in order to call different classes than just "Test" you'd have to play some games with templates and probably virtual functions. It would get pretty involved, but doable.

0

I know that you've resolved your dilemma, but I just wanted to show you a trick: personally, I hate the pointer-to-function mechanism that everyone uses, when there's a perfectly good alternative.

Here's a function that takes an int and returns a char:

char SomeFn(int i);

Here's a typedef describing that function as a type:

typedef char Fn(int i);

Looks pretty similar, huh? What's the point? Now that you've got a type, you can use that instead:

void SetCallback(Fn *fn);

SetCallback takes as a parameter a pointer to a function of type Fn. The * is necessary, and (to me) makes more sense than trying to barrel the pointer-ness inside the type itself - which is what leads to the parenthesis overload. You can call it like this:

SetCallback(&SomeFn);

What could be easier? If SomeFn was a static member of a class, you could just as easily use that:

SetCallback(&Class::SomeFn);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.