Creating a menu system with standard C functions

  • Thread starter Philipp.Weissenbacher
  • Start date
P

Philipp.Weissenbacher

Hi all!
I wrote the following as a test case for a menu based programme:

#include <iostream>
#include <cstring>
using namespace std;

#define cls system("cls")

const int SIZE = 10;
int numbers[SIZE];

void list() {
cls;
cout << "List" << endl << endl;
for(int i=0; i<SIZE; i++) {
cout << i << endl;
}

getchar(); // Doesn't work!
cls;
mainMenu();
}

void add() {};

void remove() {};

int mainMenu() {
char c;
do {
cout << "Main menu" << endl << endl;

cout << "(L)ist" << endl
<< "(A)dd" << endl
<< "(R)emove" << endl << endl
<< "(Q)uit" << endl << endl
<< "Selection: ";

cin >> c;
switch(c) {
case 'l': ; // Fallthrough to uppercase L
case 'L': list(); break;
case 'a': add(); break;
case 'r': remove(); break;
case 'q': break;
}
} while(c!='q');

return 0;
}

int main() {
// Init numbers
memset(numbers, 0, sizeof(int)*SIZE-1);
mainMenu();

return 0;
}

Why isn't the programme pausing and waiting for a RETURN key press
after listing the contents of numbers in the function list()?

Many thanks in advance,
Philipp
 
C

ciccio

Hi,

What you are having is a classic getchar() problem for interactive use.

try to run the following example code

#include <iostream>
int main(void) {
std::cout << "test getchar()" << std::endl;
getchar();
std::cout << "test cin" << std::endl;
char c;
std::cin >> c;
std::cout << "test getchar() again" << std::endl;
getchar();
return 0;
}

The first time you type 'a' when it pauses followed by ENTER
Then when it pauses type 'b' again followed by enter.
You will notice that the second getchar() command is not pausing.

[]$ ./a.out
test getchar()
a
test cin
b
test getchar() again
[]$

Run it again and type at the first time when it pauses, abc followed by
ENTER. The two next pauses will fail.

[]$ ./a.out
test getchar()
abc
test cin
test getchar() again
[]$

So the problem is the following.

Normally, getchar() is implemented in such a way that it buffers input
until ENTER is pressed. This is called line-buffered input. You have
to press ENTER before anything you typed is actually sent to the
program. Also since getchar() inputs only one character each time it is
called, line-buffering may leave one or more characters waiting in the
input queue, which is annoying in interactive environments like you want
here. Even though Standard C/C++ specifies that getchar() can be
implemented as an interactive function, it seldom is. Therefore it does
not work.

So in the above first run we had the following.


[]$ ./a.out
test getchar()
a << ENTER >> // buffer contains "a,ENTER"
// getchar() reads 'a', buffer contains "ENTER"
test cin // cin ignores ENTER and waits for real input,
// buffer empty
b << ENTER >> // buffer contains "b,ENTER"
// cin reads 'b', buffer contains "ENTER"
test getchar() again
// getchar() sees ENTER in buffer and uses it, no
// pause!
[]$

In the second test the first buffer contains "a,b,c,ENTER". getchar()
reads a, cin reads b and still the buffer contains "c,ENTER", hence
getchar() takes the 'c' and the programs exits without any pause.

The problem you have is that you call first cin and then getchar() with
the "ENTER" left in the buffer. so what you need to do is empty your
buffer. This can be done in the following way in your code

int mainMenu() {
char c[10]; // CHANGE THIS
do {
cout << "Main menu" << endl << endl;

cout << "(L)ist" << endl
<< "(A)dd" << endl
<< "(R)emove" << endl << endl
<< "(Q)uit" << endl << endl
<< "Selection: ";

cin >> c; // READS THE TYPED WORDS BUT LEAVES
// ENTER IN BUFFER,
// hope you don't type more then 10 chars
getchar(); // REMOVES THE ENTER
switch(c[0]) { // ALTER THIS
case 'l': ; // Fallthrough to uppercase L
case 'L': list(); break;
case 'a': add(); break;
case 'r': remove(); break;
case 'q': break;
}
} while(c[0]!='q'); // ALTER THIS

return 0;
}


When you did this, the use of getchar() in your list function will work,
but only with the use of the "ENTER" keystroke.
If you press at that time any other key and then followed by ENTER your
buffer is full again, and you will have a problem with the next cin in
mainmenu.

The best would be again to change that getchar() in your list function by

char c[10];
std::cin >> c;
getchar();

If you did this, your program will work as expected ... at least i think :p

Enjoy and regards


ps take a look at
http://www.parashift.com/c++-faq-lite/input-output.html#faq-15.17
 
P

Philipp.Weissenbacher

Thanks a million for this very detailed explanation.
Because I am the only user of this programme I can be sure that the
user knows not to type more than one char at a time. So I just call
getchar() twice to have the desired effect.

BTW: What's so ironic about my situation is that I'm forced to use gcc
3.4 under Windows (involving cmd.exe), but to assure it'll run under
Sun Solaris with gcc 4.1.1 and bash.
Altough this is off-topic now, is there any portable solution to this?

Hi,

What you are having is a classic getchar() problem for interactive use.

try to run the following example code

#include <iostream>
int main(void) {
std::cout << "test getchar()" << std::endl;
getchar();
std::cout << "test cin" << std::endl;
char c;
std::cin >> c;
std::cout << "test getchar() again" << std::endl;
getchar();
return 0;

}

The first time you type 'a' when it pauses followed by ENTER
Then when it pauses type 'b' again followed by enter.
You will notice that the second getchar() command is not pausing.

[]$ ./a.out
test getchar()
a
test cin
b
test getchar() again
[]$

Run it again and type at the first time when it pauses, abc followed by
ENTER. The two next pauses will fail.

[]$ ./a.out
test getchar()
abc
test cin
test getchar() again
[]$

So the problem is the following.

Normally, getchar() is implemented in such a way that it buffers input
until ENTER is pressed. This is called line-buffered input. You have
to press ENTER before anything you typed is actually sent to the
program. Also since getchar() inputs only one character each time it is
called, line-buffering may leave one or more characters waiting in the
input queue, which is annoying in interactive environments like you want
here. Even though Standard C/C++ specifies that getchar() can be
implemented as an interactive function, it seldom is. Therefore it does
not work.

So in the above first run we had the following.

[]$ ./a.out
test getchar()
a << ENTER >> // buffer contains "a,ENTER"
// getchar() reads 'a', buffer contains "ENTER"
test cin // cin ignores ENTER and waits for real input,
// buffer empty
b << ENTER >> // buffer contains "b,ENTER"
// cin reads 'b', buffer contains "ENTER"
test getchar() again
// getchar() sees ENTER in buffer and uses it, no
// pause!
[]$

In the second test the first buffer contains "a,b,c,ENTER". getchar()
reads a, cin reads b and still the buffer contains "c,ENTER", hence
getchar() takes the 'c' and the programs exits without any pause.

The problem you have is that you call first cin and then getchar() with
the "ENTER" left in the buffer. so what you need to do is empty your
buffer. This can be done in the following way in your code

int mainMenu() {
char c[10]; // CHANGE THIS
do {
cout << "Mainmenu" << endl << endl;

cout << "(L)ist" << endl
<< "(A)dd" << endl
<< "(R)emove" << endl << endl
<< "(Q)uit" << endl << endl
<< "Selection: ";

cin >> c; // READS THE TYPED WORDS BUT LEAVES
// ENTER IN BUFFER,
// hope you don't type more then 10 chars
getchar(); // REMOVES THE ENTER
switch(c[0]) { // ALTER THIS
case 'l': ; // Fallthrough to uppercase L
case 'L': list(); break;
case 'a': add(); break;
case 'r': remove(); break;
case 'q': break;
}
} while(c[0]!='q'); // ALTER THIS

return 0;

}

When you did this, the use of getchar() in your list function will work,
but only with the use of the "ENTER" keystroke.
If you press at that time any other key and then followed by ENTER your
buffer is full again, and you will have a problem with the next cin in
mainmenu.

The best would be again to change that getchar() in your list function by

char c[10];
std::cin >> c;
getchar();

If you did this, your program will work as expected ... at least i think :p

Enjoy and regards

ps take a look athttp://www.parashift.com/c++-faq-lite/input-output.html#faq-15.17
 
S

Stéphane Perras

Hi all!
I wrote the following as a test case for a menu based programme:
said:
Why isn't the programme pausing and waiting for a RETURN key press
after listing the contents of numbers in the function list()?

Many thanks in advance,
Philipp

I reckon that 'cin>>c' does not remove the carriage return from
the input stream. When getchar( ) is called later, it immediately retrieves
the carriage
return that was waiting there.

HTH
Stéphane Perras
 
C

ciccio

Thanks a million for this very detailed explanation.
Because I am the only user of this programme I can be sure that the
user knows not to type more than one char at a time. So I just call
getchar() twice to have the desired effect.

BTW: What's so ironic about my situation is that I'm forced to use gcc
3.4 under Windows (involving cmd.exe), but to assure it'll run under
Sun Solaris with gcc 4.1.1 and bash.
Altough this is off-topic now, is there any portable solution to this?

Could you elaborate a bit on this? What is actually the thing you want?
Using the same executable, the same source, you don't want to use
commandline?
 

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

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top