0

I'm trying to create a series of nested menus using pure ncurses in C++. If I create a menu and post it in main(), it works fine. But if I take the same code and put it in a function that returns a MENU*, it doesn't work at all. Am I missing something?

Code that works:

int main() 
{
  /*
   * snipped out the curses startup code
   */ 
  vector<char*> options;
  options.push_back("List");
  options.push_back("Add");
  options.push_back("Delete");
  options.push_back("Exit");

  vector<ITEM*> menu_items(options.size());
  for (int i = 0; i < options.size(); i++)
    menu_items[i] = new_item(options[i], NULL);

  MENU *options_menu;
  options_menu = new_menu(&menu_items[0]);

  set_menu_win(options_menu, main_window);
  set_menu_sub(options_menu, derwin(main_window, 6, 20, 3, 3));
  set_menu_mark(options_menu, ">");

  refresh();
  post_menu(options_menu); // this works fine
  wrefresh(main_window);
  /* 
   * snipped out the rest of the stuff
   */
}

Code that doesn't work:

MENU *make_menu()
{
  /*
   * same as code in previous main()
   */

  return options_menu;
}

int main()
{
  /*
   * snip
   */

  MENU *options_menu = make_menu();
  refresh();
  post_menu(options_menu); // this doesn't do anything
  wrefresh(main_window);

  /*
   * snip
   */
}
sagargp
  • 1,030
  • 11
  • 24

2 Answers2

2

I'll answer this for future searchers. It turns out that new_menu takes a pointer to an ITEM list and hangs onto it. If the ITEM list is on the stack of a function, it'll get deleted when the function returns, making the ITEM list invalid.

The solution to this problem is to create the ITEM list on the heap, either via new in C++, or using malloc (remember to delete or free!). Another option is to wrap the MENU in a class and keep the ITEM list as a member variable.

The simplest solution is to just make the MENU (or ITEM list) global.

sagargp
  • 1,030
  • 11
  • 24
0

Undefined behavior: new_menu expects a NULL-terminated array of ITEM* as input, you need a menu_items.push_back(0);.

Casey
  • 41,449
  • 7
  • 95
  • 125
  • Actually in this case it works out because the default size of the vector is 5, and I've only filled it with 4 items, so the 5th one is already NULL. It'll crash with 5 or more items but I can fix it with your suggestion. Either way, it still doesn't post the menu. I think it's because those ITEMs are out of scope when the `post_menu()` gets called. `post_menu` returns `E_NOT_CONNECTED`. – sagargp Jul 11 '13 at 19:09
  • 1
    What happens if you (hack) make the `vector` a global static? – Casey Jul 11 '13 at 19:18
  • it works in that case. It turns out new_menu() takes a pointer to the ITEM list and just holds onto it, so if that list goes out of scope, the menu can't find it any more. I fixed it by wrapping it all up in a class. – sagargp Jul 11 '13 at 20:08
  • Hrrm, I guess "connected to a specified item pointer array" on [the man page](http://linux.die.net/man/3/new_menu) is supposed to suggest that, I'd say it's non-obvious. – Casey Jul 11 '13 at 20:13