Search code examples
c++cmenuncursescurses

Issues with ncurses, menu.h, and current_item()


I'm having trouble with menus in ncurses. I'm trying to set up a menu, have the user select an option, and set an int called num_players depending upon their selection. I do this with boost::lexical_cast and item_name(current_item(my_menu)) but every time I call current_item(my_menu) I'm just getting NULL. Here's a sample of the code in question:

char *choices[] = {"1", "2", "3", "4", "5", "6"};
    //create the dynamic array for the items and their description
    ITEM** my_items;
    MENU *my_menu;
    int num_choices = 6;
    my_items = new ITEM*;
    for (int x = 0; x < num_choices; x++)
    {
       my_items[x] = new_item(choices[x], choices[x]); 
    }
    my_items[6] = (ITEM*)NULL;
    my_menu = new_menu((ITEM**)my_items);
    set_menu_mark(my_menu, " * ");
    set_current_item(my_menu, my_items[0]);
    post_menu(my_menu);
    wrefresh(scr);

    int c;
    while((c = wgetch(scr)) != '\n')
    {   switch(c)
        {   case KEY_DOWN:
                menu_driver(my_menu, REQ_DOWN_ITEM);
                break;
            case KEY_UP:
                menu_driver(my_menu, REQ_UP_ITEM);
                break;
        }
    }
    //right here, calling current_item just gives me null
    //am I supposed to unpost the menu first?
    //what am I doing wrong? this is frustrating
    ITEM* cur = current_item(my_menu);
    setNumPlayers((char*) item_name(cur));
    unpost_menu(my_menu);
    free_item(my_items[0]);
    free_item(my_items[1]);
    free_item(my_items[2]);
    //etc etc

Solution

  • This statement:

    my_items = new ITEM*;
    

    allocates enough space for a single ITEM* and assigns the pointer to that space to my_items. Subsequently attempting to write to my_items[i] for any value of i other than 0 will overwrite random memory, which is -- to say the least -- undefined behaviour. Whatever other problems your code might have, you need to fix that before proceeding.

    It's clear from the code that you expect to be able to store num_choices + 1 ITEM*s in the array, so you need to allocate an array of at least that size.

    my_items = new ITEM*[num_choices + 1];
    

    (Really, you should replace the 6 in my_items[6] = NULL; with num_choices; otherwise, you have a bug waiting to bite you.)

    Don't forget to use delete[] instead of delete, when you are done.

    But since you are using C++, you might as well make use of it:

    std::vector<ITEM*> my_items;
    for (int x = 0; x < num_choices; x++) {
       my_items.emplace_back(new_item(choices[x], choices[x])); 
    }
    my_items.emplace_back(nullptr);
    /* Unfortunately, new_menu requires a non-const ITEM**,
     * even though it should not modify the array. */
    my_menu = new_menu(const_cast<ITEM**>(my_items.data()));
    

    At the end, you still have to call free_item on each ITEM* before letting the std::vector destruct:

    for (auto& item : my_items) free_item(item);