Menu control in C++

Code junkies hangout here

Moderators: ChrisThornett, LXF moderators

Menu control in C++

Postby GMorgan » Sun Feb 05, 2006 10:12 pm

I'm using a sentinel controlled while loop with a switch statement to implement a menu structure for a program. I'm using a char variable to read the selection and then converting to an integer using static_cast<int>(). This eliminates the crash that occurs when a similar system is setup and you try to enter a character into an integer variable.

The problem I am getting now is that if 12345 is entered the menu system executes all 5 rather than simply taking the first action which is what I want it to do. I was wondering how to get C++ to only use the first character rather than using them all.

I know in C something along the line of scanf("%c%s",&x,y) would take the first character into x and dump the rest into y but this requires me to declare a large character array and even them somebody could still enter a larger string leaving me with the same problem. Is there a command that would make the program ignore all characters other than the first without resorting to fixes like this (TBH I would prefer to avoid using C commands whereever posible anyway considering I'm attempting a project as a tutorial in C++).

Cheers

//edit - Also does anyone know a command to clear the screen so that previous command text is removed in order to make the system appear neater//
GMorgan
LXF regular
 
Posts: 684
Joined: Thu Jan 12, 2006 6:58 pm
Location: South Wales, UK

RE: Menu control in C++

Postby jjmac » Wed Feb 08, 2006 8:29 am

Would getcha() work there ?

Or you could greate a dedicated char[] and read the input into it. Then, using a "for loop", just read the first element ?

Just out of curiosity, why the "while loop" ?, why not just a standard "switch" statement with the last on the list just an exit or a default ? Admittedly i don't know what you mean by the term "sentinal controlled" there.



jm
jjmac
LXF regular
 
Posts: 1996
Joined: Fri Apr 08, 2005 1:32 am
Location: Sydney, Australia

RE: Menu control in C++

Postby GMorgan » Wed Feb 08, 2006 11:46 am

Getcha produces the same result as cin and scanf (without the fudge) unfortunately, rather than taking one charachter it uses them all in sequence. I use the while loop so that the program will continue until I explicitely shut it down on a case of the person entering 4 (the sentinel).
Code: Select all
while (x!=4)
{
  //switch code with exit option being x=4
}


If there is a way to achieve this without a loop I'm all ears, I'm only new to programming in C/C++ and have simply done things as I would have in Pascal.

The for loop should be the same as a while loop but without the value initilisation and control bits. Since the control values are output by the menu rather than being a counter the while loop seemed appropriate.
GMorgan
LXF regular
 
Posts: 684
Joined: Thu Jan 12, 2006 6:58 pm
Location: South Wales, UK

RE: Menu control in C++

Postby jjmac » Thu Feb 09, 2006 8:43 am

GMorgan,

>>
The problem I am getting now is that if 12345 is entered the menu system executes all 5 rather than simply taking the first action which is what I want it to do
>>

I'm visulising something like ...

Code: Select all

char ch0[1];
char ch1[256];
int len = 0;

while (x!=4)
{

// fgetc(), getc(), getchar(), gets(), fgets()
// even if the whole string isn't read untill <enter> is keyed
// man gets
// gets() reads a line from stdin into the buffer pointed to by s until
//       either  a  terminating  newline or EOF, which it replaces with '\0'.
//       No check for buffer overrun is performed (see BUGS below).
// #include <stdio.h>
// or, for ++, i suppose it would be available.
// #include <stdio>
//  char *gets(char *s);

   gets(ch1);

   len = strlen(ch1); // use len to limit a for loop, check each element ?

   // or

   ch = ch1[0];

   // test ch[0] in the switch ... or loop thru that way ...

   //switch code with exit option being x=4
 
}


Haven't tryed to test it yet (probably will :roll:), but i think it would go something along those lines. That is, read the stream into a buffer then select the first element, or each element in turn. You could then possibly zero out the buffer in a for loop if there are no matches and put the gets(char *s) call in a conditional that checks for a zeroed buffer. Possibly by setting a flag when that occures and then unsetting it after it's loaded ?. Just so there isn't any collisions with quick inputs.

I've only done similar for file input reads rather than the console, so i'm tending to think along FILE pointer lines, which isn't what you want here :).

What does the actual routine your've got look like ?


jm
http://counter.li.org
#313537

The FVWM wm -=- www.fvwm.org -=-

Somebody stole my air guitar, It happened just the other day,
But it's ok, 'cause i've got a spare ...
jjmac
LXF regular
 
Posts: 1996
Joined: Fri Apr 08, 2005 1:32 am
Location: Sydney, Australia

Postby GMorgan » Thu Feb 09, 2006 5:42 pm

This is what I've got so far (its at a very early stage :D );
Code: Select all
#include <iostream>

using namespace std;

void dismainmenu();
void mainmenu();

int main(int argc, char *argv[])
{
 mainmenu();
 return 0;
}

void mainmenu()
{
 char mchoice='a';
 int menuchoice=0;

 dismainmenu();
 cin>> mchoice;
 cin.ignore();
 menuchoice = static_cast<int>(mchoice);
 while (menuchoice != 52)
 {
  switch ( menuchoice )
  {
   case 49:
        cout<<"You have chosen the Customer Menu\n\n";
        break;
   case 50:
        cout<<"You have chosen the Stock Menu\n\n";
        break;
   case 51:
        cout<<"You have chosen the Order Menu\n\n";
        break;
   case 52:
        cout<<"You have chosen exit\n\n";
        break;
   default:
        cout<<"You have made an invalid selection please choose again\n\n";
        break;
  }
  dismainmenu();
  cin>> mchoice;
  cin.ignore();
  menuchoice = static_cast<int>(mchoice);
 }
}

void dismainmenu()
{
 cout<<"Main Menu\n\n\n";
 cout<<"1. Customer Menu\n";
 cout<<"2. Stock Menu\n";
 cout<<"3. Order Menu\n\n";
 cout<<"4. Exit\n\n\n";
 cout<<"Make a selection\n";

 return;
}


It's essentially a remake of my old A-level Pascal project that I'm doing to learn C++. My intention is obviously to replace the cout messages in the switch with functions for futher menu choices but I haven't gone any futher because I can't get this menu working correctly yet.

I need to add a clear screen type command at the start of the dismainmenu function as well to make things look a little neater.
GMorgan
LXF regular
 
Posts: 684
Joined: Thu Jan 12, 2006 6:58 pm
Location: South Wales, UK

Postby Nigel » Thu Feb 09, 2006 9:00 pm

Try this instead...

Code: Select all
#include <iostream>
#include <string>

using namespace std;

void dismainmenu();
void mainmenu();

int main(int argc, char *argv[])
{
 mainmenu();
 return 0;
}

void mainmenu()
{
 string kb_input;
 int menuchoice=0;

 dismainmenu();
 getline (cin, kb_input);
 menuchoice = static_cast<int>(kb_input[0]);
 while (menuchoice != 52)
 {
  switch ( menuchoice )
  {
   case 49:
        cout<<"You have chosen the Customer Menu\n\n";
        break;
   case 50:
        cout<<"You have chosen the Stock Menu\n\n";
        break;
   case 51:
        cout<<"You have chosen the Order Menu\n\n";
        break;
   case 52:
        cout<<"You have chosen exit\n\n";
        break;
   default:
        cout<<"You have made an invalid selection please choose again\n\n";
        break;
  }
  dismainmenu();
 getline (cin, kb_input);
 menuchoice = static_cast<int>(kb_input[0]);
 }
}

void dismainmenu()
{
 system ("clear");
 cout<<"Main Menu\n\n\n";
 cout<<"1. Customer Menu\n";
 cout<<"2. Stock Menu\n";
 cout<<"3. Order Menu\n\n";
 cout<<"4. Exit\n\n\n";
 cout<<"Make a selection\n";

 return;
}


OK, so the screen clear is a cheat, but it works :)

An alternative to using getline for the character input would be to go back to what you had but add some extra parameters to the cin.ignore calls - something like

Code: Select all
cin.ignore(255,'\n');


which ignores up to 255 characters or until a newline is found, whichever comes first.
You might find this site useful.
Hope this helps,

Nigel.
User avatar
Nigel
LXF regular
 
Posts: 1141
Joined: Fri Apr 08, 2005 8:03 pm
Location: Gloucestershire, UK

Postby jjmac » Thu Feb 09, 2006 9:11 pm

Howdy GMorgan,

Thanks for that, i will muck about with it, hopefully over the next few days.

Do you really need the 'template' cast ?

I have translated, well, attempted to translate some pascal to 'C' before. Mainly a project i found in PCPlus a few years ago (Hew) on a debugger program. Pascal does use some very nice pre-fabricated high level functions. Which i found just to problematic to translate. So i ended up using the pascal code as a guide to the programs logic flow, and then coded the 'C' from scratch. It turned into quite an exciting learning exercise.

Behind all those high-level routines/functions there can be considerable C bulk. But a person can also get some insight in process, along with possibly some reusables for the future.

This was a while ago though, and it all occured in the CBuilder++. And is also locked away on a Quantum drive in a cuboard (grin). Really got to dig a lot of stuff out and fixup my pile a bit i think.

Back to the general idea. I'd suggest you do something similar. Even if it is a bit more gross, write it from scratch using the pascal unit(s) just as a logic guide.

Then once something is working there, go over it, and refine and optimise. Like a standard cast to an int must be better than using a template. Even to just work in C but compile in ++, you get the syntatic flexability but with Cs' more or less, straight forwardness.

I'm kinda interested in this as i can see myself wanting to do something similar as the year progresses. So far i've only had to deal with gui inputs from textfields and opening files and buffering them in a char[].

It appears that console streaming involves a certain 'buffer' potential problem. How does a person know how long the string is going to be. There are sensible limits of course, but you never know just how crazy one of the people using it might be (grin).

Different story for a gui input though. You could at least check the length of an textfield component first, then create a new car[] on the fly to fit.

Just on the general theme level. Something i was mucking with this morning though might be in context ...

Code: Select all

  gets.cc


#include <cstdio>
#include <string.h>
#include <curses.h>


int ch0 = 0;

char ch1[1];
char ch2[256];

int flag = 0;

int main(int argc, char ** argv)
{

    while (flag == 0){
       printf("Please input a test string: ? ");
       strncpy(ch2, gets(ch1), 1);
       ch0 = (int)*ch2;
       //ch0 = getch();  this kept giving me an error ? concerning wgetch, (wierd)

       printf("Value of ch1: %s\n", ch1);
       printf("Value of ch2: %s\n", ch2);
       printf("Value of ch0: %d\n", ch0);

       if ( !strncmp("quit", ch1, 4) ){

           printf("quit detected, will bail ...\n");
           flag = 1;
       }else{
           if (ch0 == (int)'x'){
               printf("An 'x' was detected, will bail ...\n");
               flag = 1;
           }
       }
    }


    return 0;
}


[jmd:07:43]$ g++ -o getstest gets.cc
/tmp/ccD2c6xk.o: In function `main':
gets.cc:(.text+0x32): warning: the `gets' function is dangerous and should not be used.
[jmd:07:43]$


jmd:07:43]$ ./getstest
Please input a test string: ? qwerty
Value of ch1: qwerty
Value of ch2: q
Value of ch0: 113
Please input a test string: ? 123456789
Value of ch1: 123456789
Value of ch2: 1
Value of ch0: 49
Please input a test string: ? quitqwerty
Value of ch1: quitqwerty
Value of ch2: q
Value of ch0: 113
quit detected, will bail ...
[jmd:07:46]$ ./getstest
Please input a test string: ? qweasd
Value of ch1: qweasd
Value of ch2: q
Value of ch0: 113
Please input a test string: ? xyzgfdsa
Please input a test string: ? xyzgfdsa
Value of ch1: xyzgfdsa
Value of ch2: x
Value of ch0: 120
An 'x' was detected, will bail ...
[jmd:07:47]$



Seems that will take a string from the console, and process either for a string, or for a single char, and will deal with a char as an int. So i would be inclined to go that way. Then try to incrementally elaborate on it.


>>
I need to add a clear screen type command at the start of the dismainmenu function as well to make things look a little neater.
>>

There is the system() function ...

man system:

system - execute a shell command

#include <stdlib.h>
int system(const char *string);

Makes it a bit unix specific i guess,,,, not sure if win has anything similar. Works well, but it alway makes me feel like i'm cheating when i use it, for some wierd reason (grin).



jm
jjmac
LXF regular
 
Posts: 1996
Joined: Fri Apr 08, 2005 1:32 am
Location: Sydney, Australia

Postby GMorgan » Fri Feb 10, 2006 12:43 am

Thanks to all.

Jjmac, I think your misunderstanding my thought process slightly. While the project I'm doing is based on my original A-level Pascal project I am using the design only (which is thankfully intact) and infact I have lost the code behind the Pascal over the intervening 3 years since I did it (was buggy crap anyway, my teacher wasn't too clued up and I had to invent just about everything I did outside of basic IO, file handling etc).

So I've taken the same problem since I understand the real life mechanics but I'm using C++ to redo it from scratch so I gain some knowledge of C++ and prehaps do it a little bit better than the original. I know some C (a little, enough to understand pointers/files/functions etc) but am very much an absolute novice in C++. It doesn't matter just thought I'd point it out just for completeness more than anything.

So the system("clear") statement will only work in a Linux/Unix environment then. Could be a little bit of a problem since I'm working on different platforms at the moment but at least I have some kind of work around, I can always use an embedded environment if I use XP. It's not crucial since my medium term goal after this project is for a Linux system anyway (I'm looking towards developing for the GP2X but needed the ability to program C++ effectively first before I get seriously into SDL).

Anyway thanks to you both I will disect all the code either tonight or tomorrow when I'm more sober :) . I'm hoping that once I'm past my interface troubles the implementation will do much more smoothly though undoubtedly I'll be on here crying again for help at some point.
GMorgan
LXF regular
 
Posts: 684
Joined: Thu Jan 12, 2006 6:58 pm
Location: South Wales, UK

Postby Nigel » Fri Feb 10, 2006 11:36 am

GMorgan - the equivalent clear screen instruction under DOS (or Windows command line/DOS box/terminal) is
Code: Select all
 system("cls");


You could always allow for both in your code with something like
Code: Select all
#ifdef DOS_SYSTEM
  system("cls");
#else
  system("clear");
#endif

and then add
Code: Select all
 #define DOS_SYSTEM 1

somewhere at the top of your code if you are not running in a Linux/Unix environment.
Hope this helps,

Nigel.
User avatar
Nigel
LXF regular
 
Posts: 1141
Joined: Fri Apr 08, 2005 8:03 pm
Location: Gloucestershire, UK

Postby GMorgan » Fri Feb 10, 2006 1:21 pm

Cheers that seems to work. Have managed to get some semblence of sanity as well by using the cin.ignore(255,'\n') command as suggested. It can still go wrong but at least it is much more difficult to do it now and I can always add one of the other solutions as more permenant fixes later.

So all I have to do now is comment out the #define DOS_SYSTEM 1 statement for Linux/Unix. Is a good tip and will be useful in future. It's exactly these sorts of things that you don't get in text books that I'm looking for.

Once again cheers.
GMorgan
LXF regular
 
Posts: 684
Joined: Thu Jan 12, 2006 6:58 pm
Location: South Wales, UK

Postby GMorgan » Sat Feb 11, 2006 11:02 pm

I've now got all my menus up and running and I'm even using a class rather than having a bunch of loose functions so I'm learning new things already :D .

The question I have now is can you have a struct as a class member. My intention is to create a file handling class for every file/file type in my system (I'm intending on using random access files mostly but there are a few serial files as well as needs are) based on simple commands. I then intend on building a second series of classes (one for each menu) to implement the commands in my menu by calling the simple file handling commands in the file handlind classes. I do this simply because a few menu commands require access to several files at once (the order menu requires access to both customer and stock records on top of the order records) and during my A-level project the weakest part was a mountain of code repetition due to me rewriting a lot of a function twice because the implementation was embedded within the interface and I (wrongly) decided to push on rather than re-arrange my project in a more sensible form.

Not exactly the same as the original post but I didn't want to start a new post for something that is related to the initial problem anyway.
GMorgan
LXF regular
 
Posts: 684
Joined: Thu Jan 12, 2006 6:58 pm
Location: South Wales, UK

Postby jjmac » Sun Feb 12, 2006 2:09 pm

Howdy GMorgan (grin)

>>
The question I have now is can you have a struct as a class member
>>

Yes, a type is a type, probabably have to define it externally though :)

>>
... I then intend on building a second series of classes (one for each menu) to implement the commands in my menu by calling the simple file handling commands ...
>>

(simple !) , hmmm, yes, but, ambition none the less :wink: (good luck of course) (grin)



>>
Jjmac, I think your misunderstanding my thought process slightly. While the project I'm doing ...
>>


Aha, and they say there is no method to the madness (grin). Thank you too mate, for the fill in. I was kinda wondering . Always easier to get a handle on whats going on, once the actual info comes across, no ? :)


>>
I am using the design only (which is thankfully intact) and infact I have lost the code behind the Pascal over the intervening ...
>>

I know, i look over some of my posts and i must admit, it does sound a bit patronising, but,,, it certainly isn't meant to be. It can be really difficult to figure just where a poster is coming from sometimes. Like, the actuall info drop in the initial post(s) can be be just so sparse. All a person can do is guess. So, someones gota catch the ball and run with it i suppose.

And it also seems we are in agreement too, (grin). Still not sure how or if what fix on the original issue was achieved.

>>
So I've taken the same problem since I understand the real life mechanics but I'm using C++ to redo it from scratch so I gain some knowledge of C++ and prehaps do it a little bit better than the original. I know some C (a little, enough to understand pointers/files/functions etc) but am very much an absolute novice in C++. It doesn't matter just thought I'd point it out just for completeness more than anything.
>>

hmmm, ok, looks like we are kinda in some agreement. And i was talking to the already convinced :D, no offense again, a8. But i didn't really have much to go with. All i knew was there was a problem with extracting an elememt from a buffered stream input :wink: (grin).

Pays to hang on to those old work sheets too, does it not :) ,,, i tend to hang on to stuff like that, and i'm glad i have.

pointers !!, (hehehe). m8, ,,, C "isss" were it's at !, after all ...

Good Luck There ... (elaborate ?, maybe (grin) )


jm
http://counter.li.org
#313537

The FVWM wm -=- www.fvwm.org -=-

Somebody stole my air guitar, It happened just the other day,
But it's ok, 'cause i've got a spare ...
jjmac
LXF regular
 
Posts: 1996
Joined: Fri Apr 08, 2005 1:32 am
Location: Sydney, Australia

Postby GMorgan » Sun Feb 12, 2006 6:38 pm

No problem at all. I wasn't making a fuss for the sake of it (though I can be pedantic I'm not that much so, especially when someboies trying to help me :D ). I just thought these things were good to know for the sake of the thread, less time spent worrying about Pascal since I don't have the original code anyhow and its been a few years of alcoholism and madness since I've thought about Pascal to begin with. I agree we are entirely in agreement anybody second that (don't mind this lot, I've just watched life of brian again).

Anyway I think I can limit my 9 commands that alter my customer file to something like 4 actual commands in the file handling class. Of course in reality it will be pure madness resulting in a code explosion but I shall see once I have some kind of implementation.
GMorgan
LXF regular
 
Posts: 684
Joined: Thu Jan 12, 2006 6:58 pm
Location: South Wales, UK

Postby jjmac » Mon Feb 13, 2006 6:10 am

Howdy,

Just noticed a couple of errors in my post above, though it wouldn't prevent it from runnig.

Code: Select all

strncpy(ch2, gets(ch1), 1);  should be ...

strncpy(ch1, gets(ch2), 1);

 if ( !strncmp("quit", ch1, 4) )  should be ...

 if ( !strncmp("quit", ch2, 4) )



The ch1 and ch2 usage should have been reversed :opps:

I didn.t know 'cin' had any member functions associated with it, hmmm, And i think the getline() function would need a FILE * anyway and so probably wouldn't have helped.

>>
its been a few years of alcoholism and madness since I've thought about Pascal to begin with.
>>

(hehehe) hmmmm, i wonder if ++ will drive you back (to the glug glug that is) (grin).

A trick with pointers, there always of pointer type, not the type of the data that their address is designed to return.


jm
jjmac
LXF regular
 
Posts: 1996
Joined: Fri Apr 08, 2005 1:32 am
Location: Sydney, Australia


Return to Programming

Who is online

Users browsing this forum: Exabot [Bot] and 1 guest