I'm writing a console based program for my coursework, and am wondering how best to structure it so that it is both stable and efficient. I currently have
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
int choice;
do
{
cout << "\E[H\E[2J" // Clear the console
<< "Main menu" << endl << endl
<< "Please select one of the follo开发者_JAVA百科wing options by entering it's "
<< "number in the prompt below and pressing [ENTER]. "
<< endl << endl
<< "1. Pay my bill as a guest" << endl
<< "3. Log in" << endl
<< "2. Create an account" << endl
<< "4. Quit program" << endl;
cin >> choice;
switch (choice)
{
case 1: // Pay the bill as a guest to the system
case 2: // Log in to the system
case 3: // Create an account with the system
case 4: // Quit the program
default: // Prompt the user to choose again
}
} while !(default);
// Await user input to terminate the program
cout << "Please press [ENTER] to continue...";
cin.get();
return 0;
}
The purpose of the above code is to provide a list of options for the user to choose from, with the do-while
loop working alongside the default
statement in the switch
to catch any unexpected input. Each case
would call a function that presented another menu with it's own list of options, which would be structured using the same do-while, switch
method. My concern is that as my program grows, the number of function calls being nested within other functions is going to increase, so that I would eventually end up with a function being called from within a function being called from within a function and so on. This would obviously have severe implications for the maintainability of the program, with the function calls moving further and further away from main()
, and the output of these functions weaving a tangled path about the program.
Is it possible to structure my program in such a way as to return execution to main()
as often as possible, or is the problem described above simply a consequence of this kind of programming?
NB: I ask this question in the understanding that user-defined functions are supposed to be ancillary to main()
, and that they should perform a task before returning control to main()
as the earliest possible convenience. I've only been at this a couple of months now so please bear with my ignorance/misunderstanding. Also, ignore any potential compiler errors in the code, I've not tested it yet and it's only provided as an aide to my conceptual question.
I would apply some OO-design and create a menu-class which basically stores items/sub-menus in a vector. This would make it easy to extemd to hierarchical menus
There is nothing particularly wrong with what you've done.
I don't see why it harms maintainability to have functions called from functions and so on. If anything it AIDS maintainability as you can move common code operations into seperate functions. This way you make a fix in one place and instantly fix the rest of the places its used as well.
Well, you can implement your menu structure as a state-machine, so you will be almost always in your main loop. But this can bring your code to the lower level, because you will be effectively programming not in C++ but in your state-machine processor-near code. If your state machine will be good enough, this is not a problem.
Or you can a simple menu-runner class, which will output as a result a request for submenu, so you will just exchange (perhaps using a stack) the description of the currently running menu.
By the way, I don't see any problems in deep nesting of the functions.
Another possible approach is to make a class defined as a list of (menu_option, function) pairs and the know-how to turn them into menus. Then the function can be a call to another class instance's menu or it can do some operation on your database. That lets you keep your data organized away from the business "how to display this menu" logic and add menus and menu items easily.
Don't worry about that or your current approach spending too much time away from main though. As you've structured it, your program won't automatically turn itself into a horrible mess just because you're calling functions from functions. More functions will tend to add to maintainability, as long as you keep them focused.
Think of it this way: a function does one thing, but at a higher level of abstraction than its body. So main()
runs your program. create_account()
will create an account, which is part of running the program. create_account
itself calls several other things that do the building blocks necessary for creating an account. Is determining the new account's name one thing? It goes in its own function. Determining the index of the new account in the database? Too low-level. Put it in the "stuff it in the database" function.
The complexity of the code will correlate to the functionality offered by the program. I would not worry about this right now, revisit refactoring once you have two or three hundred lines.
for use this code you must add:
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "kharid");
memcpy(st_menues[cnt++].layer, "100000", sizeof(st_menues[cnt].layer));
this block code for insert menu item .
for change item to subitem you must change:
"100000"
to
"010000"
for do a difference work per every item you must define multiple:
void tmp_func(char* str)
void tmp_func1(char* str)
void tmp_func2(char* str)
...
and insert in
st_menues[cnt].function_pointer = tmp_func1;
...
st_menues[cnt].function_pointer = tmp_func2;
...
and insert your source code in these functions.
this source can compile in vc++. i didn't test its in linux . but perhups works.
#include <iostream>
#include <stdio.h>
#include <conio.h>
#include<stdlib.h>
char title_str[20] = " main menu \n";
void print_this(bool with_title,const char* str, ...)
{
if(with_title)printf(title_str);
printf(str);
}
void clear()
{
#ifdef _WIN32
system("cls");
#else
std::cout << "\033[2J\033[1;1H";
#endif
print_this(true,"");
}
struct def_struct_menu
{
void (*function_pointer)(char*);
char menu_string[24];
char layer[7];
int pos;
};
void set_title(char* str)
{
sprintf(title_str," %s \n",str);
}
void tmp_func(char* str)
{
clear();
set_title(str);
printf("calc okokok");
_getch();
}
def_struct_menu st_menues[100] = { 0 };
def_struct_menu st_cur_menues[100] = { 0 };
void back_to_main_menu(int& highlight_line, int& cur_layer, int& start)
{
highlight_line = 0;
cur_layer = 0;
start = 0;
set_title((char*)"main menu");
}
int main()
{
int cnt = 0;
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "kharid");
memcpy(st_menues[cnt++].layer, "100000", sizeof(st_menues[cnt].layer));
{
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "asan");
memcpy(st_menues[cnt++].layer, "010000", sizeof(st_menues[cnt].layer));
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "shenase");
memcpy(st_menues[cnt++].layer, "010000", sizeof(st_menues[cnt].layer));
}
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "sharj");
memcpy(st_menues[cnt++].layer, "100000", sizeof(st_menues[cnt].layer));
{
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "ramz");
memcpy(st_menues[cnt++].layer, "010000", sizeof(st_menues[cnt].layer));
{
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "yekbarmasraf");
memcpy(st_menues[cnt++].layer, "001000", sizeof(st_menues[cnt].layer));
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "qrcode");
memcpy(st_menues[cnt++].layer, "001000", sizeof(st_menues[cnt].layer));
}
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "mostaghim");
memcpy(st_menues[cnt++].layer, "010000", sizeof(st_menues[cnt].layer));
}
const int ST_SIZE = cnt;
int input = 0;
int highlight_line = 0;
int cur_layer = 0;
int start = 0;
while (input != -1)
{
int size = 0;
memset(st_cur_menues, 0, sizeof(def_struct_menu) * ST_SIZE);
for (int i = start; i < ST_SIZE; i++)
{
if (cur_layer > 0)
{
if (st_menues[i].layer[cur_layer - 1] == '1')
{
break;
}
}
if (st_menues[i].layer[cur_layer] == '1')
{
memcpy(&st_cur_menues[size++], &st_menues[i], sizeof(def_struct_menu));
}
}
clear();
if (size == 0)
{
back_to_main_menu(highlight_line, cur_layer, start);
}
for (int i = 0; i < size; i++)
{
if (highlight_line == i)
print_this(false,"*");
else
print_this(false," ");
print_this(false,st_cur_menues[i].menu_string);
print_this(false,"\n");
}
//print_this("enter number\n");
input = _getch();
switch (input)
{
case 'x':
{
exit(0);
}
case 27://escape button
{
back_to_main_menu(highlight_line, cur_layer, start);
break;
}
case 13://enter button
{
if (size == 0)
{
back_to_main_menu(highlight_line, cur_layer, start);
break;
}
st_cur_menues[highlight_line].function_pointer(st_cur_menues[highlight_line].menu_string);
start = st_cur_menues[highlight_line].pos + 1;
cur_layer++;
highlight_line = 0;
}
break;
case 72://up arrow key
{
if (highlight_line == 0)
highlight_line = (size - 1);
else
highlight_line--;
}
break;
case 80://down arrow key
{
if (highlight_line == (size - 1))
highlight_line = 0;
else
highlight_line++;
}
break;
default:
break;
}
}
return 0;
}
tnx
精彩评论