C++相同子菜单的代码减少

C++ code reduction for identical submenus

本文关键字:代码 菜单 C++      更新时间:2023-10-16

我正在编码到学期的最后一个项目,我遇到了代码重复问题。我正在使用ncursespdcurses来制作菜单以与用户交互。

问题:对于菜单的每个选择(总共五个),我需要一个子菜单。子菜单与主菜单的唯一区别是,要打印的Items数组以及由于数组大小Items而进入某些函数的参数。由于我需要五个子菜单,我需要五倍相同的代码(如果您添加主菜单,则需要六倍)。

你们中的任何人能帮我做一个做同样事情的函数,然后我会调用它六次来创建我的菜单吗?

这是我的代码

void Menu(){
    const char* Items[]={
        "[1]...New tax declaration",
        "[2]...Modify tax declaration",
        "[3]...Cancel tax declaration",
        "[4]...Additional Information",
        "[5]...Exit"
    };
    int Cur=0;
    int ch, i;
    int flag=0;
    do{
        werase(wm);
        mvwaddstr(wm, 2, 16, "MENU");
        for(int i=0; i<5;i++){
            if(i==Cur)
                wattr_on(wm, A_REVERSE, 0);
            mvwaddstr(wm, 4+i, 4, Items[i]);
            wattr_off(wm, A_REVERSE, 0);
        }
        mvwaddstr(wm, 14, 3, "Choice: ");
        wprintw(wm, "%1d", Cur+1);
        wrefresh(wm);
        ch=wgetch(wm);
        switch(ch){
            case '1':Cur=0;Sub2();break;
            case '2':Cur=1;Sub1();break;
            case '3':Cur=2;break;
            case '4':Cur=3;break;
            case '5':flag=1;break;
            case KEY_UP:
            case KEY_LEFT: Cur--; if (Cur<0) Cur=4; break;
            case KEY_DOWN:
            case KEY_RIGHT: Cur++; if(Cur>4) Cur=0; break;
            case 27: flag=1; break;
            case 32:
            case 13:
                switch (Cur){
                    case 0:Sub2();break;
                    case 1:Sub1();break;
                    case 2:break;
                    case 3:break;
                    case 4:flag=1;break;
                }
        }
    }while(!flag);
}

谢谢。

p.s 代码来自一本书。我对诅咒的经验很少

一个简单的

菜单驱动的程序。这是基于使用std::map而不是条件逻辑。此地图存储了menuitem结构的列表,这些结构定义了菜单的外观以及每个选项的功能。

在我们完成代码时,最好地解释这一点,所以让我们深入了解吧!

// headers for everything used in this example
#include <iostream>
#include <string>
#include <map>
#include <functional>
#include <cctype>
// function to perform menu option B sub option 1
void optionB1()
{
    std::cout << "perfoming B1" << std::endl;
}
// function to perform menu option B sub option 2
void optionB2()
{
    std::cout << "perfoming B2" << std::endl;
}
// function to perform menu option A
void optionA()
{
    std::cout << "perfoming A" << std::endl;
}
// defines a menu item. Good naming can often save the need to comment
struct menuitem 
{
    std::function<void()> doIt; // function to run if option chosen
    std::string description; // pretty message describing option
};
// draw menu and wait for the user to select an option.
void domenu(const std::map<char, menuitem> & menu)
{
    while (true) // loop until user gives a good option. Or use a retry count. 
                 // You decide.
    {
        for (auto &items : menu)
        { // for all items in the menu, print out the item and it's description text
          // for what first and second mean, read up on std::map and std::pair
            std::cout << items.first << ") " << items.second.description << std::endl;
        }
        char ch;
        std::cin >> ch; // get the user's choice
        // often you may want to eliminate one of the cases to reduce the amount 
        // of possible inputs you need to provide handling code for.
        // the line below allows us to use the same code for input of A and a.
        ch = std::tolower(ch); // convert input to lower case
        try
        {
            menu.at(ch).doIt(); // call the function mapped to user's choice.
                                // this may do produce something or it may
                                // display another menu. It could end the wor--
            return; // done.
        }
        catch (...)
        { // print error message on unsupported input
            std::cout << "Error. Invalid option!" << std::endl;
        }
    }
}

// the B menu
std::map<char, menuitem> bmenu
{ // User input  doIt function      Description
    {'1',       {optionB1,          "Option B1"}},
    {'2',       {optionB2,          "Option B2"}}
    // add more options here. Or don't. Up to you.
};
// the main menu
std::map<char, menuitem> mainmenu
{ // User input  doIt function              Description
    {'a',       {optionA,                   "Option A"}},
    {'b',       {std::bind(domenu, bmenu),  "Option B"}}
    // OK, so that last one was a bit weird. std::bind makes a function and
    // specifies the arguments with which it will be called. This takes
    // domenu binds it with bmenu so that std::function<void()> is
    // satisfied. As far as the world is concerned, the bound function
    // returns nothing and takes no parameters. Very complicated functions 
    // can be bound so long as the end result returns nothing and requires 
    // no parameters. 
    // what it's doing here is allowing us to call domenu to draw the B 
    // submenu, wait for valid input, and call the chosen function.
};
// good 'ol trusty main so we can test that the above code isn't utter BS.
int main()
{
    while (true) // loop forever. Or use whatever exit logic is required.
    {
        domenu(mainmenu); // kick-start by calling do menu to run the main menu
    }
    return(0);
}

这将使代码降至最低。所有重复的代码都被简化为domenu函数和隐藏在标准库中的蓝精灵代码负载,这些代码由可能比你或我更有经验的人编写。

domenu由选项列表和选项的执行指令驱动。想要其他选择?将项目添加到列表中,并可能提供新功能以履行该选项的义务。

您所要做的就是填写空白。