相同子菜单的 C++ 代码缩减 [英] C++ code reduction for identical submenus

查看:50
本文介绍了相同子菜单的 C++ 代码缩减的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am coding my way to the last project of the semester and I have a code duplication issue. I am using ncurses or pdcurses to make a menu to interact with the user.

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

The problem: For each choice of the menu(Five in total) I need a submenu. The submenu's only difference from the main menu is, the array of Items to be printed, and the parameters that go into some functions, as a result of the Items array size. Since I need five submenus, I need five times the same code(six if you add the main menu).

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

Could any of you help me make a function that does the same thing, which I'll then call six times to create my menu?

这是我的代码

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.s The code is from a book. I have little experience with ncurses

推荐答案

一个简单的菜单驱动程序.这是基于使用 std::map 代替条件逻辑.该映射存储了一个 menuitem 结构列表,这些结构定义了菜单的外观和每个选项的作用.

A simple menu-driven program. This is based on using std::map instead of conditional logic. This map stores a list of menuitem structures that define what the menu looks like and what each option does.

最好在我们处理代码时对此进行解释,所以让我们深入研究!

This is best explained as we work through the code, so let's dive in!

// 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 函数和一些隐藏在标准库中看不见的代码,由可能比你或我更有经验的人编写.只要有可能,就站在巨人的肩膀上.

This will keep the code down to a minimum. All of the duplicated code is reduced to the domenu function and a smurfload of code hidden from sight in the standard library and written by folks who likely have far more experience in getting this stuff right than you or I. Whenever possible, stand on the shoulders of giants.

domenu 由选项列表和选项的执行指令驱动.想要另一种选择吗?向列表中添加一个项目,并可能提供一个新功能来履行该选项的义务.

domenu is driven by lists of options and execution instructions for the option. Want another option? Add an item to a list and possibly provide a new function to fulfill the obligations of that option.

你所要做的就是填空.

这篇关于相同子菜单的 C++ 代码缩减的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆