Проблема с цикличной реализацией консольного меню в C - PullRequest
0 голосов
/ 02 января 2019

Я искал способ реализовать консольное меню, затем я нашел это ответ от LXSoft на «Как написать консольное меню в ANSI / ISO C?» поэтому я попытался реализовать его в своем коде, используя Visual studio 2017

Код:

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <windows.h>
// LXSoft
// mod: cui/menu_021
// stdarg.h  -> used for variable list of arguments (va_list, va_start ...)
// windows.h -> used for Sleep function, for *nix use unistd.h

typedef unsigned short int usint_t;
// Menu function prototype
int menu(char* name, char* prefix, char* cursor, usint_t orientation,
    usint_t padding, usint_t start_pos, usint_t delay,
    usint_t num_childs, ...);

int main()
{
    int exit;
    do {
        exit = 1;
    int response = menu("List des commandes", "-", "-->", 1, 3, 1, 0, 2,
        "1- Ajouter un etudiant (ou un ensemble d'etudiants)",
        "2- exit");
    printf("\n");
    switch (response)
    {
    case 1:
        // doSomethingFoo1();
        exit = 0;
        break;
    case 2:
        //doSomethingFoo2();
        exit = 1;
        break;
    default:
        exit = 0;
    }
        printf("\nYour choice is: %d", response);
    } while (!exit);
    return 0;
}

// Menu implementation
int menu
(
    char *name,        // Menu name (eg.: OPTIONS)
    char *prefix,      // Menu prefix (eg.: [*])
    char *cursor,      // Menu cursor (eg.: ->)
    usint_t orient,    /*
                        * Menu orientation vertical or horzontal.
                        * 0 or false for horizontal
                        * 1 or true for vertical
                        */
    usint_t padding,   // Menu childrens padding (eg.: 3)
    usint_t start_pos, // Menu set active child (eg.: 1)
    usint_t delay,     // Menu children switch delay
    usint_t childs,    // Number of childrens
    ...                /*
                        * Variable list of arguments char* type.
                        * Name of the childrens.
                        */
)
{
    va_list args;
    int tmp = 0, pos;
    char chr=0;
    usint_t opt = start_pos;
    char* format = malloc
    (
        (
            strlen(name) + strlen(prefix) + strlen(cursor) +
            3 + /* menu suffix (1 byte) and backspace (2 bytes) */
            (2 * childs) + /* newline (2 bytes) times childs */
            (padding*childs) + /* number of spaces times childs */
            childs * 60 /* children name maxlen (15 bytes) times childs*/
            ) * sizeof(char)
    );
    do
    {

        if (tmp != 0)chr = _getch();
        if (chr == 0x48 || chr == 0x4B)
            (opt > 1 && opt != 1) ? opt-- : (opt = childs);
        else if (chr == 0x50 || chr == 0x4D)
            (opt >= 1 && opt != childs) ? opt++ : (opt = 1);
        else {/* do nothing at this time*/ }
        strcpy(format, "");
        strcat(format, prefix);
        strcat(format, name);
        strcat(format, ":");
        va_start(args, childs);
        for (tmp = 1; tmp <= childs; tmp++)
        {
            (orient) ? strcat_s(format, SizeFormat, "\n") : 0;
            pos = padding;
            while ((pos--) > 0) strcat_s(format, SizeFormat, " ");
            if (tmp == opt)
            {
                strcat(format, "\b");
                strcat(format, cursor);
            }
            strcat(format, va_arg(args, char*));
        }
        /*if(tmp!=childs)
        {
            fprintf(stderr,"%s: recieved NULL pointer argument,"
                           " child not named", __func__);
            return -1;
        }*/
        Sleep(delay);
        system("cls");
        fputs(format, stdout);
        va_end(args);
    } while ((chr = _getch()) != 0x0D);
    return opt;
}

и все работало нормально, но мне кажется, что я не могу вызвать функцию «меню» более одного раза (case1 :), потому что я пытался зациклить ее, но она ломается и становится непригодной для использования, другими словами, я не могу использовать курсор больше. (код был отредактирован для большей простоты)

Я делаю что-то глупое и неправильное?

Или это невозможно зациклить? если нет, то как?

заранее спасибо

EDIT: я нашел с помощью отладчика, что if (tmp != 0)chr = _getch(); Не указывайте правильное значение ввода при нажатии клавиши со стрелкой после первой итерации цикла. Почему это?

Ответы [ 2 ]

0 голосов
/ 02 января 2019

Я добавил _getch(); перед циклом, и теперь все снова было хорошо, почему? я действительно не знаю, но я знаю, что этот код работает (я ставлю _getch(); перед возвратом значения menu(...);):

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <windows.h>
// LXSoft
// mod: cui/menu_021
// stdarg.h  -> used for variable list of arguments (va_list, va_start ...)
// windows.h -> used for Sleep function, for *nix use unistd.h

typedef unsigned short int usint_t;
// Menu function prototype
int menu(char* name, char* prefix, char* cursor, usint_t orientation,
    usint_t padding, usint_t start_pos, usint_t delay,
    usint_t num_childs, ...);

int main()
{
    int exit;
    do {
        exit = 1;
    int response = menu("List des commandes", "-", "-->", 1, 3, 1, 0, 2,
        "1- Ajouter un etudiant (ou un ensemble d'etudiants)",
        "2- exit");
    printf("\n");
    switch (response)
    {
    case 1:
        // doSomethingFoo1();
        exit = 0;
        break;
    case 2:
        //doSomethingFoo2();
        exit = 1;
        break;
    default:
        exit = 0;
    }
        printf("\nYour choice is: %d", response);
    } while (!exit);
    return 0;
}

// Menu implementation
int menu
(
    char *name,        // Menu name (eg.: OPTIONS)
    char *prefix,      // Menu prefix (eg.: [*])
    char *cursor,      // Menu cursor (eg.: ->)
    usint_t orient,    /*
                        * Menu orientation vertical or horzontal.
                        * 0 or false for horizontal
                        * 1 or true for vertical
                        */
    usint_t padding,   // Menu childrens padding (eg.: 3)
    usint_t start_pos, // Menu set active child (eg.: 1)
    usint_t delay,     // Menu children switch delay
    usint_t childs,    // Number of childrens
    ...                /*
                        * Variable list of arguments char* type.
                        * Name of the childrens.
                        */
)
{
    va_list args;
    int tmp = 0, pos;
    char chr=0;
    usint_t opt = start_pos;
    char* format = malloc
    (
        (
            strlen(name) + strlen(prefix) + strlen(cursor) +
            3 + /* menu suffix (1 byte) and backspace (2 bytes) */
            (2 * childs) + /* newline (2 bytes) times childs */
            (padding*childs) + /* number of spaces times childs */
            childs * 60 /* children name maxlen (15 bytes) times childs*/
            ) * sizeof(char)
    );
    do
    {

        if (tmp != 0)chr = _getch();
        if (chr == 0x48 || chr == 0x4B)
            (opt > 1 && opt != 1) ? opt-- : (opt = childs);
        else if (chr == 0x50 || chr == 0x4D)
            (opt >= 1 && opt != childs) ? opt++ : (opt = 1);
        else {/* do nothing at this time*/ }
        strcpy(format, "");
        strcat(format, prefix);
        strcat(format, name);
        strcat(format, ":");
        va_start(args, childs);
        for (tmp = 1; tmp <= childs; tmp++)
        {
            (orient) ? strcat_s(format, SizeFormat, "\n") : 0;
            pos = padding;
            while ((pos--) > 0) strcat_s(format, SizeFormat, " ");
            if (tmp == opt)
            {
                strcat(format, "\b");
                strcat(format, cursor);
            }
            strcat(format, va_arg(args, char*));
        }
        /*if(tmp!=childs)
        {
            fprintf(stderr,"%s: recieved NULL pointer argument,"
                           " child not named", __func__);
            return -1;
        }*/
        Sleep(delay);
        system("cls");
        fputs(format, stdout);
        va_end(args);
    } while ((chr = _getch()) != 0x0D);
     _getch(); //THE EDIT 
    return opt;
}

Я хотел бы знать, почему, если кто-нибудь знает!

0 голосов
/ 02 января 2019

Вот упрощенная форма, на мой взгляд:

int main()
{
    int exit;

    do {
        exit = 1;
        int response = menu(...);

        switch (response)
        {
        case 1:
        case 2:
            break;
        default:
            exit = 0;
        }

    } while (!exit);

    return 0;
}

Пока menu() возвращает 1 или 2, exit == 1, следовательно, (!exit) == 0.Это приводит к короткому замыканию do...while.

Я бы порекомендовал либо

exit = 0;       // exit is meaningful, this means "exit - false"
...
default:
    exit = 1;   // "exit - true"
...
while (!exit);  // while not exiting, loop

, либо

keep_looping = 1;      // you keep the original value '1' but the variable
                       // name is meaningful; i.e. "keep_looping - true"
...
default:
    keep_looping = 0;  // "keep_looping - false"
...
while (keep_looping);  // "keep looping while keep_looping"
...