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");
}
}
4 Answers 4
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– Andy Braham2015年12月05日 01:29:58 +00:00Commented Dec 5, 2015 at 1:29
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 ofPROGMEM
: the compiler knows how to properly dereference a __flash pointer, whereas with PROGMEM you have to explicitly call one of thepgm_read_*()
functions.Edgar Bonet– Edgar Bonet2016年02月04日 16:10:57 +00:00Commented Feb 4, 2016 at 16:10
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.
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);