Search code examples
c++recursionvectorfunctional-programmingmenu

Is there a way to store functions in a vector?


I'm attempting to create a menu system within my C++ program and I'm having trouble implementing past the initial main menu.

I want to have multiple Menu objects that I pass to my function called showMenu() that calls the function that pairs with the respective menu item.

Should I be storing function pointers in the vector? Any advice is helpful - the cleaner the solution, the better.

Here's my code:

struct Menu {
    vector<string> items;
    vector<function> functions;       // incorrect
};
Menu mainMenu = {
    { "Quit",   "Income Menu",             "Expenses Menu"          },
    { return,   showMenu(incomeOptions),   showMenu(expenseOptions) }       // incorrect
};

Menu incomeOptions = {
    {"Back" ,   "Add Income",   "Edit Income"},
    { return,   addIncome(),    editIncome()}        // incorrect
};

Menu expenseOptions = {
    {"Back" ,   "Add Expense",   "Edit Expense"},
    { return,   addExpense(),    editExpense()}        // incorrect
};
void showMenu(Menu menu, string title = "") {

    system("cls");
    if (title != "") {
        cout << "================ [" << title << "] ================" << endl;
        cout << endl;
    }
    for (int i = 0; i < menu.items.size(); i++) {
        cout << " (" << i << ") " << menu.items[i] << endl;
    }

    int selection;
    cout << "\t> ";
    cin >> selection;

    if (selection >= 0 && selection < menu.items.size()) {
        // call function that's paired with selected item
        menu.functions[selection];
    }
    else {
        // invalid selection made, display menu again
        showMenu(menu, title);
    }
}
int main() {
        showMenu(mainMenu, "Main Menu");
}

I've attempted to research the c++ library . I'm pretty sure that's what I need, but like I said, am having trouble implementing it. I'd love to get some suggestions.


Solution

  • You have to provide template parameters for std::function:

    struct Menu
    {
        vector<string> items;
        vector<function<void(void)>> functions;
    };
    

    And than you can pass lamdas to it:

    Menu incomeOptions = {
        {"Add Income", "Edit Income"},
        {[]{ addIncome(); }, []{ editIncome(); }}
    };
    
    Menu expenseOptions = {
        {"Add Expense", "Edit Expense"},
        {[]{ addIncome(); }, []{ editIncome(); }}
    };
    
    Menu mainMenu = {
        {"Income Menu", "Expenses Menu"},
        {[]{ showMenu(incomeOptions); }, []{ showMenu(expenseOptions); }}
    };
    

    You will have to handle the return differently.

    And anyway, it it not a good idea to store your menus as global variables. You should change this! But pay attention to the fact that for the mainMenu you must add the other menus to the capture of its lambdas.

    int main()
    {
        Menu incomeOptions = {
            {"Add Income", "Edit Income"},
            {[]{ addIncome(); }, []{ editIncome(); }}
        };
    
        Menu expenseOptions = {
            {"Add Expense", "Edit Expense"},
            {[]{ addIncome(); }, []{ editIncome(); }}
        };
    
        Menu mainMenu = {
            {"Income Menu", "Expenses Menu"},
            {[&incomeOptions]{ showMenu(incomeOptions); }, [&expenseOptions]{ showMenu(expenseOptions); }}
        };
        showMenu(mainMenu, "Main Menu");
    }
    

    Passing the menus by value to the showMenu function is a waste of resources. Feel free to modify it for reference.

    void showMenu(Menu &menu, string title = "")
    {
        // ...
    }
    

    And in you showMenu function. Calling the function also requires the use of the parentheses.

    menu.functions[selection]();
    

    Calling showMenu on invalid input is not the best idea because these recursive calls will eat up your stack after a while. I recommend changing this to a loop.

    And also, adding using namespace std; to your code is a bad practice in the long run.