A basic primitive interactive system

S

sugaray

Right now, the code I wrote below seems works fine, i know it's definitely
not the best solution, so if there are better solutions to
eschew from using goto statements to jump out of multiple loops ?
and any other suggestions or good ideas that are pertain to construct
a more powerful and common interactive menu system is also welcomed,
and if there's any primitive but better interactive system that is
written in C suitable for educational purpose you happen to know of,
let me know please, i need a good and relatively easy one to tamper
with in order to build my skill, thanx in advance for your help.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static const char prompt = '#';
int prompt_mode = 0;

static void
promptmode (void)
{
int i;
for (i = 0; i < prompt_mode; ++i)
printf ("%c", prompt);
printf (">");
}

static void
level1menu (void)
{
printf ("\n=========== LEVEL-1 MENU ============\n");
printf ("| 1. HELP MESSAGES |\n");
printf ("| 2. ENTER LEVEL-2 |\n");
printf ("| 3. DISPLAY THIS MENU |\n");
printf ("| 4. QUIT THE SYSTEM |\n");
printf ("=====================================\n\n");
}

static void
level2menu (void)
{
printf ("\n*********** LEVEL-2 MENU ************\n");
printf ("| 1. HELP MESSAGES |\n");
printf ("| 2. FUNCTIONALITIES |\n");
printf ("| 3. DISPLAY THIS MENU |\n");
printf ("| 4. BACK TO THE UPPER LEVEL |\n");
printf ("*************************************\n\n");
}

static int
getchoice (void)
{
char input[4];

fgets (input, sizeof input, stdin);
fflush (stdout);
input[strlen (input) - 1] = '\0';
return atoi (input);
}


int
main (void)
{
int choice;

level1:
level1menu ();
for (;;)
{
prompt_mode = 1;
fflush(stdin);
promptmode ();

choice = getchoice ();

if (choice < 1 || choice > 4)
continue;

switch (choice)
{
case 1:
printf ("help messages for level 1.\n");
break;
case 2:
prompt_mode = 2;
level2menu ();
for (;;)
{
fflush(stdin);
promptmode ();

choice = getchoice ();

if (choice < 1 || choice > 4)
continue;

switch (choice)
{
case 1:
printf ("display help messages for level 2.\n");
break;
case 2:
printf ("implement functionalities.\n");
break;
case 3:
level2menu ();
break;
case 4:
printf ("back to the upper level.\n");
goto level1;
default:
printf ("invalid option in level 2.\n");
}
}
break;
case 3:
level1menu ();
break;
case 4:
goto end;
default:
printf ("invalid option in level 1.\n");
}

}
end:
printf ("\nsee ya ;)\n");

return 0;
}
 
D

Darrell Grainger

Right now, the code I wrote below seems works fine, i know it's definitely
not the best solution, so if there are better solutions to
eschew from using goto statements to jump out of multiple loops ?
and any other suggestions or good ideas that are pertain to construct
a more powerful and common interactive menu system is also welcomed,
and if there's any primitive but better interactive system that is
written in C suitable for educational purpose you happen to know of,
let me know please, i need a good and relatively easy one to tamper
with in order to build my skill, thanx in advance for your help.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static const char prompt = '#';
int prompt_mode = 0;

static void
promptmode (void)
{
int i;
for (i = 0; i < prompt_mode; ++i)
printf ("%c", prompt);
printf (">");

You want to have a fflush(stdout) here. Without it, you might not see this
prompt.
}

static void
level1menu (void)
{
printf ("\n=========== LEVEL-1 MENU ============\n");
printf ("| 1. HELP MESSAGES |\n");
printf ("| 2. ENTER LEVEL-2 |\n");
printf ("| 3. DISPLAY THIS MENU |\n");
printf ("| 4. QUIT THE SYSTEM |\n");
printf ("=====================================\n\n");
}

static void
level2menu (void)
{
printf ("\n*********** LEVEL-2 MENU ************\n");
printf ("| 1. HELP MESSAGES |\n");
printf ("| 2. FUNCTIONALITIES |\n");
printf ("| 3. DISPLAY THIS MENU |\n");
printf ("| 4. BACK TO THE UPPER LEVEL |\n");
printf ("*************************************\n\n");
}

static int
getchoice (void)
{
char input[4];

fgets (input, sizeof input, stdin);
fflush (stdout);

Why are you flushing stdout here? You have not output anything so this
seems pointless. I'd remove this.
input[strlen (input) - 1] = '\0';

This line assumes you want to get rid of the last character. Are you
assuming the user input less than 3 characters plus the newline? With
input holding 4 elements you will have 2 significant characters, the
newline and the terminating null character. What if the user inputs a long
string of text? I'm not clear on what you want to do here so I'm wondering
if this will handle the negative test scenarios.
return atoi (input);

You understand that if the user inputs "hohoho" this function will return
zero.
}


int
main (void)
{
int choice;

level1:
level1menu ();
for (;;)
{
prompt_mode = 1;
fflush(stdin);

Undefined behaviour. Don't flush input streams. There is no guarantee what
this will do.
promptmode ();

choice = getchoice ();

if (choice < 1 || choice > 4)
continue;

This makes the default case in the switch unreachable.
switch (choice)
{
case 1:
printf ("help messages for level 1.\n");
break;
case 2:
prompt_mode = 2;
level2menu ();
for (;;)
{
fflush(stdin);

Undefined behaviour.
promptmode ();

choice = getchoice ();

if (choice < 1 || choice > 4)
continue;

This makes the default case in the switch unreachable.
switch (choice)
{
case 1:
printf ("display help messages for level 2.\n");
break;
case 2:
printf ("implement functionalities.\n");
break;
case 3:
level2menu ();
break;
case 4:
printf ("back to the upper level.\n");
goto level1;
default:
printf ("invalid option in level 2.\n");
}
}
break;
case 3:
level1menu ();
break;
case 4:
goto end;
default:
printf ("invalid option in level 1.\n");
}

}
end:
printf ("\nsee ya ;)\n");

return 0;
}

First, the formatting is a bother. I like to have all the text on one
screen. Having to scroll up and down in the main function makes it a little
difficult to read.

Even when I do reformat it, I look at it and think this is still not the
easiest thing to read. Obviously, you are thinking about the use of goto.
You cannot 'break' out of the inner for loop because you are using switch
statements.

I tend to use switch statements if they make the code more readable. To
most compilers a set of if/else if/else statements and a switch statement
are not going to be a huge different. Even if they are, you can change it
later (after profiling). When I have something like:

if(n == 1 || n == 7 || n == 9 || n == 11 || n == 16 || n == 21)
/* do something */

and the number of 'expr#' is actually much longer, this can get a little
ugly to read. It reads better, to me, as:

switch(n) {
case 1:
case 7:
case 9:
case 11:
case 16:
case 21:
/* do something */
break;
}

In your case, using the switch statement does not seem to help readability
and it is actually causing problems when you want to break out of the
loops. Just switch to if statements.
 
K

Karthik

sugaray said:
Right now, the code I wrote below seems works fine, i know it's definitely
not the best solution, so if there are better solutions to
eschew from using goto statements to jump out of multiple loops ?
and any other suggestions or good ideas that are pertain to construct
a more powerful and common interactive menu system is also welcomed,
and if there's any primitive but better interactive system that is
written in C suitable for educational purpose you happen to know of,
let me know please, i need a good and relatively easy one to tamper
with in order to build my skill, thanx in advance for your help.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static const char prompt = '#';
int prompt_mode = 0;

static void
promptmode (void)
{
int i;
for (i = 0; i < prompt_mode; ++i)
printf ("%c", prompt);
printf (">");
}

static void
level1menu (void)
{
printf ("\n=========== LEVEL-1 MENU ============\n");
printf ("| 1. HELP MESSAGES |\n");
printf ("| 2. ENTER LEVEL-2 |\n");
printf ("| 3. DISPLAY THIS MENU |\n");
printf ("| 4. QUIT THE SYSTEM |\n");
printf ("=====================================\n\n");
}

static void
level2menu (void)
{
printf ("\n*********** LEVEL-2 MENU ************\n");
printf ("| 1. HELP MESSAGES |\n");
printf ("| 2. FUNCTIONALITIES |\n");
printf ("| 3. DISPLAY THIS MENU |\n");
printf ("| 4. BACK TO THE UPPER LEVEL |\n");
printf ("*************************************\n\n");

Generally printfs are costly. So you need to avoid the number of
times it is invoked.
printf ("\n*********** LEVEL-2 MENU ************\n"
"| 1. HELP MESSAGES |\n"
"| 2. FUNCTIONALITIES |\n"
"| 3. DISPLAY THIS MENU |\n"
"| 4. BACK TO THE UPPER LEVEL |\n" );

is way ahead, in terms of performance compared to the previous code.

Smarter programmers use macros , whenever they need to write constants
in the code ( as above ).
}

static int
getchoice (void)
{
char input[4];

fgets (input, sizeof input, stdin);

What happens, if i enter more than 3 chars.
fflush (stdout);
input[strlen (input) - 1] = '\0';
return atoi (input);
}


int
main (void)
{
int choice;

level1:
level1menu ();
for (;;)

Some people prefer a while (1) loop , though it is more a matter of
style.
{
prompt_mode = 1;
fflush(stdin);
promptmode ();

choice = getchoice ();

if (choice < 1 || choice > 4)
continue;

switch (choice)
{
case 1:
printf ("help messages for level 1.\n");
break;
case 2:
prompt_mode = 2;
level2menu ();
for (;;)
{
fflush(stdin);
promptmode ();

choice = getchoice ();

if (choice < 1 || choice > 4)
continue;

switch (choice)
{
case 1:
printf ("display help messages for level 2.\n");
break;
case 2:
printf ("implement functionalities.\n");
break;
case 3:
level2menu ();
break;
case 4:
printf ("back to the upper level.\n");
goto level1;
default:
printf ("invalid option in level 2.\n");
}
}
break;
case 3:
level1menu ();
break;
case 4:
goto end;

gotos are evil. That should be your last resort , really when
nothing else on earth works for you.
 
T

Thomas Matthews

sugaray said:
Right now, the code I wrote below seems works fine, i know it's definitely
not the best solution, so if there are better solutions to
eschew from using goto statements to jump out of multiple loops ?
and any other suggestions or good ideas that are pertain to construct
a more powerful and common interactive menu system is also welcomed,
and if there's any primitive but better interactive system that is
written in C suitable for educational purpose you happen to know of,
let me know please, i need a good and relatively easy one to tamper
with in order to build my skill, thanx in advance for your help.
[Sound of knuckles crackling as I stretch out my fingers]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static const char prompt = '#';
You may be performing "premature optimization" with this one.
One byte may not be enough reason to declare it as a constant.
Check with you compiler for that.

int prompt_mode = 0;
Global variable. Get rid of it.
If you can't get rid of it, make it static, to hide it from
other modules.

static void
promptmode (void)
{
int i;
for (i = 0; i < prompt_mode; ++i)
printf ("%c", prompt);
printf (">");
}
static void display_prompt(unsigned int prompt_level)
{
unsigned int i;
for (i = 0; i < prompt_level; ++i)
{
putchar('#');
}
putchar('>');
return;
}

The above function eliminates the dependency on a global
variable, and also doesn't use "printf" because there
is no need for the output to be formatted.

static void
level1menu (void)
{
printf ("\n=========== LEVEL-1 MENU ============\n");
printf ("| 1. HELP MESSAGES |\n");
printf ("| 2. ENTER LEVEL-2 |\n");
printf ("| 3. DISPLAY THIS MENU |\n");
printf ("| 4. QUIT THE SYSTEM |\n");
printf ("=====================================\n\n");
}
A direct replacement:
static void level1menu(void)
{
static const char menu_text[] =
"\n"
"=========== LEVEL-1 MENU ============\n"
"| 1. HELP MESSAGES |\n"
"| 2. ENTER LEVEL-2 |\n"
"| 3. DISPLAY THIS MENU |\n"
"| 4. QUIT THE SYSTEM |\n"
"=====================================\n"
"\n";
fwrite(menu_text,
sizeof(char),
sizeof(menu_text) - 1,
stdout);
return;
}

I would prefer to make this as static data local to the module,
and use a table driven system to print them out (discussed below).

static void
level2menu (void)
{
printf ("\n*********** LEVEL-2 MENU ************\n");
printf ("| 1. HELP MESSAGES |\n");
printf ("| 2. FUNCTIONALITIES |\n");
printf ("| 3. DISPLAY THIS MENU |\n");
printf ("| 4. BACK TO THE UPPER LEVEL |\n");
printf ("*************************************\n\n");
}
static void level2menu(void)
{
static const char menu_text[] =
"\n"
"*********** LEVEL-2 MENU ************\n"
"| 1. HELP MESSAGES |\n"
"| 2. FUNCTIONALITIES |\n"
"| 3. DISPLAY THIS MENU |\n"
"| 4. BACK TO THE UPPER LEVEL |\n"
"*************************************\n"
"\n"
fwrite(menu_text,
sizeof(char),
sizeof(menu_text) - 1,
stdout);
return;
}

Note how the code of the two above functions is the
same, but only the data changes. Hmmmm....

static int
getchoice (void)
{
char input[4];

fgets (input, sizeof input, stdin);
fflush (stdout);
input[strlen (input) - 1] = '\0';
return atoi (input);
}
This doesn't return any kind of error, especially
if the user types in text rather than numbers.

int
main (void)
{
int choice;

level1:
level1menu ();
for (;;)
{
prompt_mode = 1;
fflush(stdin);
promptmode ();

choice = getchoice ();

if (choice < 1 || choice > 4)
continue;

switch (choice)
{
case 1: "help messages for level 1.\n"
break;
case 2:
prompt_mode = 2;
level2menu ();
for (;;)
{
fflush(stdin);
promptmode ();

choice = getchoice ();

if (choice < 1 || choice > 4)
continue;

switch (choice)
{
case 1:
"display help messages for level 2.\n"
break;
case 2: "implement functionalities.\n"
break;
case 3:
level2menu ();
break;
case 4: "back to the upper level.\n"
goto level1;
default: "invalid option in level 2.\n"
}
}
break;
case 3:
level1menu ();
break;
case 4:
goto end;
default: "invalid option in level 1.\n"
}

}
end: "\nsee ya ;)\n"

return 0;
}
Arrrrgggghhh: labels, gotos and switch statement.

/* Create a typedef for a pointer to a function that
* will process a menu selection.
*/
typedef (void) (*Ptr_Menu_Proc_Func)(void);

struct MenuItem
{
unsigned int number;
Ptr_Menu_Proc_Func processing_function;
};

void Level1_Help_Messages(void)
{
puts("Processed Level 1 Help Messages.\n");
return;
}

void Enter_Level_2(void)
{
puts("Level 2 requested.\n");
return;
}

void Null_Entry(void)
{
return;
}

static struct MenuItem menu_choices[] =
{
{0, Null_Entry},
{1, Level1_Help_Messages},
{2, Enter_Level_2},
{3, Null_Entry},
{4, Null_Entry}
};
static const unsigned int NUM_CHOICES =
sizeof(menu_choices) / sizeof(menu_choices[0]);

static const char menu_text[] =
"\n"
"=========== LEVEL-1 MENU ============\n"
"| 1. HELP MESSAGES |\n"
"| 2. ENTER LEVEL-2 |\n"
"| 3. DISPLAY THIS MENU |\n"
"| 4. QUIT THE SYSTEM |\n"
"=====================================\n"
"\n";


#define MAX_LENGTH_OF_CHOICE 16

int main(void)
{
unsigned long choice;
char choice_text[MAX_LENGTH_OF_CHOICE];
char * s;

do
{
do
{
/* display the menu */
fwrite(menu_text,
sizeof(char),
sizeof(menu_text) - 1,
stdout);

/* display prompt */
display_prompt(1);

/* Get user selection */
fgets(choice_text, MAX_LENGTH_OF_CHOICE, stdin);
choice = strtoul(choice_text, &s, 10);
} while ((choice < 1) || (choice >= NUM_CHOICES));

/* Execute menu processing function */
menu_choices[choice].processing_function();

} while (choice != 4);

return EXIT_SUCCESS;
}

This could be upgraded so that the menu displaying
function uses the numbers in the menu_choices. One
could also expand the Menu_Item structure to contain
the selection text. Thus this task would be data
driven. Just pass in a pointer to an array of
Menu_Item and the number of items.


--
Thomas Matthews

C++ newsgroup welcome message:
http://www.slack.net/~shiva/welcome.txt
C++ Faq: http://www.parashift.com/c++-faq-lite
C Faq: http://www.eskimo.com/~scs/c-faq/top.html
alt.comp.lang.learn.c-c++ faq:
http://www.raos.demon.uk/acllc-c++/faq.html
Other sites:
http://www.josuttis.com -- C++ STL Library book
 
A

Arthur J. O'Dwyer

Generally printfs are costly. So you need to avoid the number of
times it is invoked.
printf ("\n*********** LEVEL-2 MENU ************\n"
"| 1. HELP MESSAGES |\n"
"| 2. FUNCTIONALITIES |\n"
"| 3. DISPLAY THIS MENU |\n"
"| 4. BACK TO THE UPPER LEVEL |\n" );

is way ahead, in terms of performance compared to the previous code.

You might be interested to know that the *reason* printfs are
costly is because the 'printf' function has to parse through the
whole string looking for format specifiers before it can print
any of it (format specifiers being things like "%s" and "%d").[1]
So you'll find an even bigger performance advantage to just write
what you mean:

puts("");
puts("*********** LEVEL-2 MENU ************");
puts("| 1. HELP MESSAGES |");
puts("| 2. FUNCTIONALITIES |");
puts("| 3. DISPLAY THIS MENU |");
puts("| 4. BACK TO THE UPPER LEVEL |");
puts("*************************************");
puts("");

Merging all your strings into one big string might save you a couple
of cycles, but it tends to hurt readability more.
Smarter programmers use macros , whenever they need to write constants
in the code ( as above ).

Do you mean this line?

Using my current style, I'd have made that

static const char *PromptIndent = "#";

and made the appropriate changes elsewhere, thus setting the
program up to allow the user to change the prompt-indent string
to something other than "#". The macro solution, FWIW, might be

#define PROMPT '#'

with appropriate changes elsewhere.

static int
getchoice (void)
{
char input[4];
fgets (input, sizeof input, stdin);

What happens, if i enter more than 3 chars.
fflush (stdout);
input[strlen (input) - 1] = '\0';
return atoi (input);

And if all you're doing is trying to read an integer, without
caring about error-checking or anything else (which seems to be
the case), then

int input = -1;
scanf("%d", &input);
while (getchar() != '\n');
return input;

seems like the way to go. No muss, no fuss.

-Arthur

[1] - FWIW, 'gcc' is smart enough to use 'puts' in place of
'printf' whenever the latter is called with only one argument.
Other compilers *may* not be so smart, though.
 
S

sugaray

Thomas Matthews said:
/* Create a typedef for a pointer to a function that
* will process a menu selection.
*/
typedef (void) (*Ptr_Menu_Proc_Func)(void);

struct MenuItem
{
unsigned int number;
Ptr_Menu_Proc_Func processing_function;
};

void Level1_Help_Messages(void)
{
puts("Processed Level 1 Help Messages.\n");
return;
}

void Enter_Level_2(void)
{
puts("Level 2 requested.\n");
return;
}

void Null_Entry(void)
{
return;
}

static struct MenuItem menu_choices[] =
{
{0, Null_Entry},
{1, Level1_Help_Messages},
{2, Enter_Level_2},
{3, Null_Entry},
{4, Null_Entry}
};
static const unsigned int NUM_CHOICES =
sizeof(menu_choices) / sizeof(menu_choices[0]);

static const char menu_text[] =
"\n"
"=========== LEVEL-1 MENU ============\n"
"| 1. HELP MESSAGES |\n"
"| 2. ENTER LEVEL-2 |\n"
"| 3. DISPLAY THIS MENU |\n"
"| 4. QUIT THE SYSTEM |\n"
"=====================================\n"
"\n";


#define MAX_LENGTH_OF_CHOICE 16

int main(void)
{
unsigned long choice;
char choice_text[MAX_LENGTH_OF_CHOICE];
char * s;

do
{
do
{
/* display the menu */
fwrite(menu_text,
sizeof(char),
sizeof(menu_text) - 1,
stdout);

/* display prompt */
display_prompt(1);

/* Get user selection */
fgets(choice_text, MAX_LENGTH_OF_CHOICE, stdin);
choice = strtoul(choice_text, &s, 10);
} while ((choice < 1) || (choice >= NUM_CHOICES));

/* Execute menu processing function */
menu_choices[choice].processing_function();

} while (choice != 4);

return EXIT_SUCCESS;
}

This could be upgraded so that the menu displaying
function uses the numbers in the menu_choices. One
could also expand the Menu_Item structure to contain
the selection text. Thus this task would be data
driven. Just pass in a pointer to an array of
Menu_Item and the number of items.

Thanx for your suggestions and code samples, Tom ! ;-)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top