开发者

How to efficiently structure a terminal application with multiple menus?

开发者 https://www.devze.com 2023-01-22 08:50 出处:网络
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

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

0

精彩评论

暂无评论...
验证码 换一张
取 消