0

I want to create a menu that works with embedded systems. I use C.

This is how I plan to make it:

  1. First, the pressed keys are identified by the interrupts.
  2. A array is update on those identifications.
  3. Find the corresponding term acording to array content.
  4. The operation is performed.

My array is like this. Array nth item represent different levels. Each element represent level item number.

uint8_t menu_map[5] = {2, 3, 1, 0, 0};

My question is: How to choose a function pointer or structure acording to array content?

like this(acording to array content i mentioned as before example).

function_23100(){
   // Function content
}

Is there a way to do that? Is there any other good alternative to do this kind of menu?

2 Answers2

1

There are several ways to archive but in the end, they are both hard to maintain and cause performance down. Let's say you would build a lookup table to ref to, assume you have 4 buttons, an array with 5 entries -> the size of lookup table(worse case) is 4x4x4x4x4 = 1024 entries. Finding the corresponding action in this table per each button press is not that good in term of performance.

Let's back to what you wanna to archive, check below code for your ref. By create a menu tree, bind callback to each menu item and keep track the path of current menu item, it's not that difficult to travel back and forth in menu tree. Once you enter a specific menu item, it's easy to access to its callback by the pointer that was assigned to.

#include <stdio.h>

#define MENU(name, text, nr_item, ...)  \
    static menu_t name = { \
        { text, NULL, nr_item, __VA_ARGS__ }, { 0 }, -1 \
    }

#define MENU_ITEMS(...)                 (menu_item_t[]) { __VA_ARGS__ }

#define MAX_DEPTH                       10
#define ARRAY_SIZE(x)                   (sizeof(x)/sizeof(x[0]))

typedef struct menu_item menu_item_t;
struct menu_item {
    const char *text;
    void (*action)(menu_item_t*);
    int nr_of_children;
    menu_item_t *children;
};

typedef struct {
    menu_item_t item;
    menu_item_t* _history[MAX_DEPTH];
    int _history_index;
} menu_t;

static inline void print_menu_history(menu_t *menu) {
    int i = 0;

    for (i = 0; i < MAX_DEPTH; i++) {
        if (!menu->_history[i]) break;
        printf("> %s ", menu->_history[i]->text);
    }

    printf("\n");
}

static inline void print_menu(menu_t *menu) {
    menu_item_t *item = menu->_history[menu->_history_index];
    int i;

    printf("= %s\n", item->text);
    for (i = 0; i < item->nr_of_children; i++) {
        printf("- [%2d] %s\n", i, item->children[i].text);
    }
    printf("==========================================\n");
    print_menu_history(menu);
    printf("==========================================\n");
}

static inline void add_item_to_history(menu_t *menu, int position) {
    if (menu->_history_index < 0) {
        menu->_history_index = 0;
        menu->_history[menu->_history_index] = &menu->item;
    } else {
        if (position < menu->_history[menu->_history_index]->nr_of_children)
            menu->_history[++menu->_history_index] = 
                &menu->_history[menu->_history_index]->children[position];
    }
}

static inline void exec_menu_action(menu_t *menu, int position) {
    if (menu->_history[menu->_history_index]->action)
        menu->_history[menu->_history_index]->action(menu->_history[menu->_history_index]);
}

static inline void exit_menu(menu_t *menu) {
    if (menu->_history_index > 0)
        menu->_history[menu->_history_index--] = NULL;
    print_menu(menu);
}

static inline void enter_menu(menu_t *menu, int position) {
    add_item_to_history(menu, position);
    exec_menu_action(menu, position);
    print_menu(menu);
}

static void menu_item_action(menu_item_t *item) {
    if (item) printf("=========='%s' selected\n", item->text);
}

MENU(main_menu, "Menu", 2, 
    MENU_ITEMS(
        { "Item 1", menu_item_action, 3, 
            MENU_ITEMS(
                { "Item 1.1", menu_item_action, 3, 
                    MENU_ITEMS(
                        { "Item 1.1.1", menu_item_action, 0, NULL },
                        { "Item 1.1.2", menu_item_action, 0, NULL },
                        { "Item 1.1.3", menu_item_action, 0, NULL }
                    )  
                },
                { "Item 1.2", menu_item_action, 0, NULL },
                { "Item 1.3", menu_item_action, 0, NULL }
            ) 
        }, 
        { "Item 2", menu_item_action, 2,
            MENU_ITEMS(
                { "Item 2.1", menu_item_action, 0, NULL },
                { "Item 2.2", menu_item_action, 0, NULL }
            )  
        }
    )
); 

int main(int argc, char *argv[]) {
    unsigned char c = 0;
    int samples[] = {1, 0, 1, 0, 0};

    enter_menu(&main_menu, -1);

    for (c = 0; c < ARRAY_SIZE(samples); c++) {
        enter_menu(&main_menu, c);
    }

    while(c != 'q') {
        c = getchar();
        if ('b' == c) exit_menu(&main_menu);
        if ((c <= '9') && (c >= '0')) enter_menu(&main_menu, c - '0');
    }

    return 0;
}
S Dao
  • 555
  • 4
  • 7
0

You can use simple conditionals and assign a dictionary for each value.

Firstly, convert the array of numbers into just a singular number-

int arr_to_int(int* arr, int len)
{
    int number = 0;
    for (int i = 0, val; i < len; i++)
    {
        val = arr[i];
        while (val != 0)
        {
            val = val / 10;
            number = number * 10;
        }
        number = number + arr[i];
    }
    return number;
}

Now pass the number to a switch case-

switch(number)
{
    case 23100:
        function_23100();
        break;
.....
}

Unfortunately, there's no easier way. Perhaps you were thinking about some hacky c macro. But that's not going to work because the preprocessor cannot evaluate a variable as it happens before the code is compiled.

Edit: Something I should mention, this would work for languages that support reflection. i.e languages that can call a function according to its string representation. So if you could do find_method("function_23100"), it'd find the corresponding function and call it. And really all you need to get the string function_23100 is just have a helper function, that can concatenate function_ and the string representation of num. Now, there are ways to implement reflection in C. If you'd like to do that (and think it's not absolutely overkill), then read this

Chase
  • 5,315
  • 2
  • 15
  • 41