我正在为本学期的最后一个项目编写代码,但我遇到了代码重复问题。
我正在使用ncurses
或pdcurses
制作菜单以与用户进行互动。
问题:对于菜单的每个选择(总共五个),我需要一个子菜单。子菜单与主菜单的唯一区别是,要打印的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。我只想让我的最终项目代码变得更好:)。
答案 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
由选项列表和选项执行指令驱动。想要另一种选择?将项目添加到列表中,并可能提供新功能以履行该选项的义务。
你所要做的就是填空。