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

时间:2016-06-01 21:19:03

标签: c++ ncurses pdcurses

我正在为本学期的最后一个项目编写代码,但我遇到了代码重复问题。 我正在使用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代码来自一本书。我对ncurses没什么经验,所以对我来说不要太难:P。我只想让我的最终项目代码变得更好:)。

1 个答案:

答案 0 :(得分:1)

一个简单的菜单驱动程序。这是基于使用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由选项列表和选项执行指令驱动。想要另一种选择?将项目添加到列表中,并可能提供新功能以履行该选项的义务。

你所要做的就是填空。