How can one create menus in the command line program? I've tried stuff like:
cin >> input;
switch (input) {
case (1):
// do stuff
case (2):
// ...
}
but then I've had the problem of sub-menus, and going back to the same menu, etc. The first program I wrote (apart from exercises) that tried to use the switch idea for the menus had goto statements because the alternative was heaps of (at the time) complicated loops.
If I tried to count the ways in which one might create a 1,2,3 menu, we'd both be dead before I'd iterated 1/2 of them. But here's one method you could try to get you started (untested, you may need to clean up a couple things):
struct menu_item
{
virtual ~menu_item() {}
virtual std::string item_text() const = 0;
virtual void go() = 0;
};
struct print_hello_item
{
std::string item_text() const { return "display greeting"; }
void go() { std::cout << "Hello there, Mr. User."; }
};
struct kill_everyone_item
{
std::string item_text() const { return "Go on murderous rampage"; }
void go() { for(;;) kill_the_world(); }
};
struct menu_menu_item
{
menu_menu_item(std::string const& text) : text_(text), items() {}
void add_item(std::unique_ptr<menu_item> item) { items.push_back(std::move(item)); }
void go()
{
std::cout << "Choose: \n";
std::for_each(items.begin(), items.end(), [](std::unique_ptr<menu_item> const& item)
{
std::cout << "\t" << item->item_text() << "\n";
});
std::cout << "\n\n\tYour choice: ";
int choice = get_number_from_console();
if (items.size() > choice) items[choice]->go();
}
std::string item_text() const { return text_; }
private:
std::string text_;
std::vector<std::unique_ptr<menu_item> > items;
};
int main()
{
menu_menu_item top_item;
top_item.add(std::unique_ptr<menu_item>(new print_hello_item));
top_item.add(std::unique_ptr<menu_item>(new kill_everyone_item));
top_item.go();
}
As an exercize, how might I define menu items like so:
top_level.add()
( "Drive off a cliff", &die_function )
( "Destroy the world", &global_thermal_nuclear_war )
( "Deeper", submenu()
( "Hey, check this shit out!", &gawk ))
;
It can be done with the above framework as a starting point.
This is the difference between OO design and what might be called "procedural". I created an abstraction behind what it means to be a menu choice (which can be another menu) that can be extended in various directions. I create the extensions I need, put them together, and tell the thing to go. Good OO design is just like that...the main part of your program is comprised of putting stuff together and telling it to go.
The key thing to take from this is not necessarily to do it the way I just did, but to think about it in a different way. If you can get the gist of the above code then you'll see that you can add new items, with new menus, to arbitrary depths, without having to deal with the kind of overly complicated code that the switch style causes.
You can integrate submenus in your menu with your method:
cin >> input;
switch (input) {
case (1):
cin >> input;
switch (input) {
case (1): //do stuff
case (2): //do stuff
}
break;
case (2):
break;
}
Is this what you're looking for? Otherwise: What do you want to solve exactly?
Edit:
So what you need is an additional loop in your sub-menus with an break condition?
do{
cin >> input;
switch (input) {
case (1):
do{
cin >> input;
switch (input) {
case (1): //do stuff
case (2): //do stuff
}
}while(input != 3);
break;
case (2):
break;
}
}while(true);
I just asked a similar question at Libraries for displaying a text-mode menu? -- it looks like ncurses will dramatically simplify the menu-displaying parts.
Related
I have these functions:
read(){
}
write(){
}
main(){
cout << "This is a menu so you can choose what to do";
}
Its actualy more complicated than that but that example will do fine for you to help me.
I just want to be able to for example write, then go back to the menu, then choose to read etc
I cant do :
read(){
main();
}
main(){
read();
}
because main was not declared before read, so then I move main above read and read was not declared before main.
Exactly this program is an agenda, so that I can creat schedules and manage events (write) and just see my calendar with all the events or just see my schedules (read). I want to be able to go from main to read, then to main again then to write you know
I think I could separate these functions into different files but I dont know how to call a function from another file.
Any help?
Here's another alternative:
int main(void)
{
bool call_read = true;
while (true)
{
if (call_read)
{
read();
}
else
{
write();
}
call_read = !call_read;
}
return 0;
}
In the above example, a bool variable is used to alternate between the read and write functions.
A nice feature of this method is that the functions called don't need to know about each other.
ok, calling main is not a good idea, but I guess the question is more about how having 2 functions calling each other.
you need to forward declare at least one:
int write(); //forward declare
int read() {
return write(); // use write which is still forward declared for now
}
int write() { // now defining write
return read();
}
obviously this sample code is meaning less (infinite loop), but it illustrates how to do that.
A good way to write menus is to use a while loop in main and a switch statement. Assuming the read and write functions do not branch somewhere else, when they complete, they return to their caller, main(). The while loop ensures that the menu does not end until the user is done.
int main()
{
char choice = '\n';
while (choice != 'q')
{
// Display menu options
cout << "r: Read" << endl << "w: Write" << endl << "Enter a choice: ";
cin >> choice;
// Implement menu interface
switch (choice)
{
case 'r':
read();
break;
case 'w':
write();
break;
case 'q':
cout << "Quitting the menu. Bye!" << endl;
break;
default:
cout << "Invalid choice!" << endl;
break;
} //end switch
} // end while
return 0;
} // end main
I am currently getting to grips with how to correctly implement multiple functions in my programs and I think I have got the right idea but I am just wanting to clarify.
When putting some logic into a particular function, should I be handling the end the result in that function or bringing it back to my "main function"? I am aware this is probably an ambiguous question so I have posted my code here to try and help matters.
The program simply adds a string to a vector but I am wondering whats the best approach to handle it.
Thank you so much in advance.
Program 1.
std::vector<std::string> favouriteGames; //Stores favourite games
int menu = 0; //Menu navigation
std::cout << "1: Add Game. 2: Remove Game. 3: List Games. 4: Exit.";
std::cin >> menu;
//Menu
switch (menu)
{
case 1:
favouriteGames.push_back(AddGame());
break;
case 2:
//favouriteGames.erase(RemoveGame);
break;
case 3:
//ListGames();
break;
case 4:
break;
default:
std::cout << "Please enter correct data.";
}
//Add game
std::string AddGame()
{
std::string gameName;
int menu = 0;
std::cout << "Enter name of game you wish to add.";
std::cin >> gameName;
return gameName;
}
Program 2.
Or like this when the function is solely handling the data and doesn't return anything.
void AddGame(std::vector<std::string> favouriteGames);
int main()
{
std::vector<std::string> favouriteGames; //Stores favourite games
int menu = 0; //Menu navigation
std::cout << "1: Add Game. 2: Remove Game. 3: List Games. 4: Exit.";
std::cin >> menu;
//Menu
switch (menu)
{
case 1:
AddGame(favouriteGames);
break;
case 2:
//favouriteGames.erase(RemoveGame);
break;
case 3:
//ListGames();
break;
case 4:
break;
default:
std::cout << "Please enter correct data.";
}
//Keep Window open
std::string barn;
std::cin >> barn;
return 0;
}
//Add game
void AddGame(std::vector<std::string> favouriteGames)
{
std::string gameName;
int menu = 0;
std::cout << "Enter name of game you wish to add.";
std::cin >> gameName;
favouriteGames.push_back(gameName);
}
Program I.
In general, your functions should have one job. It makes them re-usable and helps to keep your interfaces clean and stable.
In this case, that means your function is actually named wrongly. It should be something like requestGameName().
You could then also hive the .push_back into its own, second function (addGame()?) though that may be overkill in your initial version. Still, one day, adding a game may involve more lines of code that trigger you to move all of that into another function.
I have some problems with cin. When I enter a character instead of an integer cin doesn't work and after that I can't even enter a new value. What should I do? I have already tried fflush(stdin)
struct PersonList
{
Person person;
PersonList* personListPtr;
};
void addPerson(PersonList*& ptr, int position);
void deletePersonList(PersonList* ptr);
int main()
{
PersonList* personListPtr = NULL;
int flag = 0;
int pos = 0;
int i;
while(flag != 27)
{
system("cls");
cout << "1 - add objects\n"
<< "2 - delete objects\n"
<< "ESC - exit\n";
switch(flag)
{
case '1':
cout << "Enter position: ";
**cin >> pos;**
addPerson(personListPtr, pos);
break;
case '2':
break;
case '3':
break;
}
flag = _getch();
}
deletePersonList(personListPtr);
return 0;
}
Thanks.
You seem to be mixing several idioms. I don't know what
_getch() does, but I can't imagine that it is in anyway
compatible with std::cin; std::cin will have (or may have)
read ahead, reading your flag character, for example. You
cannot simply mix different streams from the same source.
As for the particular problem you describe, once you enter
something which can't be converted into what you are reading,
the stream goes into an error state (which you should check
before using the value), and all operations on it become no-ops
until you clear the error (std::istream::clear()). But that's
not going to fix the other problems. If you insist on using
something like _getch(), then you'll have to use it for all of
your input, building up a string for input of the position, and
using std::istringstream to convert it. Depending on what the
function actually does, you may also have to deal with things
like backspace, enter and maybe even echoing.
I have this working code:
/* Include files */
#include <iostream>
#include <string>
#include <limits>
using namespace std;
void fnMainMenu(char s);
/******************************************************************************
* Function: fnUpdateSalary
* Description: Update employee salary
******************************************************************************/
void fnUpdateSalary()
{
int choice = 0;
char data = 'a';
incorrect_input: // goto teleport exit :)
cout<<"\n\t=======================";
cout<<"\n\tWelcome\n";
cout<<"\t=======================\n\n";
cout<<"1. Update employee salary\n";
cout<<"2. Main menu\n";
cout<<"3. Exit system\n";
cout<<"\n >> ";
cin>>choice;
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(),'\n');
switch(choice){
case 1 :
//fnUpdateSalary();
break;
case 2 :
fnMainMenu(data);
break;
case 3 :
exit(1);
break;
default :
cout<<"Input not recognized. Please enter the correct input.\n";
}
}
void fnLog()
{
char data = 'b';
fnMainMenu(data); // call Main Menu and I want to pass "hello"
}
/******************************************************************************
* Function: fnMainMenu
* Description: Menu for the customer
******************************************************************************/
void fnMainMenu(char s)
{
cout << s;
if (s == 'a')
{ cout << "a = admin";
}
else
{cout << "\n\nb not admin";
}
//system("cls");
int chooice = 0;
cout<<"\n\t=======================";
cout<<"\n\tWelcome\n";
cout<<"\t=======================\n\n";
cout<<"1. Manage employee\n";
cout<<"2. Search employee\n";
cout<<"3. Employee report\n";
cout<<"4. Reset password\n";
cout<<"5. Exit system\n";
cout<<"\n >> ";
int numbers = 2;
cin>>chooice;
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(),'\n');
switch(chooice){
case 1 :
fnUpdateSalary();
break;
case 4 :
// fnChangePassword();
break;
default : exit(1);
}
}
/******************************************************************************
* Function: main
* Description: The main calling function
******************************************************************************/
int main()
{
fnLog();
return 0;
}
fnLog() send b to fnMainMenu(). When I am at fnUpdateSalary() I want to go to fnMainMenu() again. My question is, is there anyway from fnUpdateSalary() or whatever function available to call fnMainMenu() without declaring variable data again? I was hoping I could just use fnMainMenu(); instead of
char data = 'r'; fnMainMenu(data);
I get this error error C2660: 'fnMainMenu' : function does not take 0 arguments if I just called fnMainMenu();
I hope my question make sense. Thanks in advance.
The parameter doesn’t seem to serve a real purpose anyway. Passing b into the main menu function certainly achieves nothing. If you just want to distinguish between admin and non-admin access, change the type and usage of the argument:
void fnMainMenu(bool is_admin) {
if (is_admin)
cout << "Admin\n";
else
cout << "Not admin\n";
}
And call it like this:
fnMainMenu(true);
// Or, alternatively:
fnMainMenu(false);
That’s it. Incidentally, you don’t need (and shouldn’t!) declare a variable to pass as the argument here. Just pass the value directly, like I’ve done above.
Also, why are your function names prefixed by fn? Don’t do this, it’s not good practice. Just use proper names that explain well what the functions do.
If I fully understand what you are doing, you need a combination of two things. Firstly, a static variable in fnMainMenu and secondly a default parameter:
fnMainMenu(char s = '\0')
{
static char c;
if(s != '\0') c = s;
...
...
}
The "static" keyword means that the character will be preserved across function calls. The default parameter means that s will be assigned a null termination character unless you explicitly pass another value.
Since you're writing C++ code and data seems to be relatively long-lived with respect to the application logic, it would perhaps make sense to regroup these functions into a class.
data could be a data member of this class, and fnUpdateSalary, fnLog, fnMainMenu methods.
class Application {
public:
Application ()
: data ('b') // default value
{ }
void fnLog() {
data = 'b';
fnMainMenu ();
}
void fnMainMenu() {
if (data == 'a')
cout << "a = admin";
else
cout << "\n\nb not admin";
// ...
fnUpdateSalary ();
// ...
}
void fnUpdateSalary() {
// ...
fnMainMenu ();
// ...
}
private:
char data;
};
int main () {
Application app;
app.fnLog ();
}
I just started C++ but have some prior knowledge to other languages (vb awhile back unfortunately), but have an odd predicament. I disliked using so many IF statements and wanted to use switch/cases as it seemed cleaner, and I wanted to get in the practice.. But..
Lets say I have the following scenario (theorietical code):
while(1) {
//Loop can be conditional or 1, I use it alot, for example in my game
char something;
std::cout << "Enter something\n -->";
std::cin >> something;
//Switch to read "something"
switch(something) {
case 'a':
cout << "You entered A, which is correct";
break;
case 'b':
cout << "...";
break;
}
}
And that's my problem. Lets say I wanted to exit the WHILE loop, It'd require two break statements?
This obviously looks wrong:
case 'a':
cout << "You entered A, which is correct";
break;
break;
So can I only do an IF statement on the 'a' to use break;? Am I missing something really simple?
This would solve a lot of my problems that I have right now.
I would refactor the check into another function.
bool is_correct_answer(char input)
{
switch(input)
{
case 'a':
cout << "You entered A, which is correct";
return true;
case 'b':
cout << "...";
return false;
}
return false;
}
int main()
{
char input;
do
{
std::cout << "Enter something\n -->";
std::cin >> input;
} while (!is_correct_answer(input));
}
You could simply have the while loop check for a bool value that is set within one of your case statements.
bool done = false;
while(!done)
{
char something;
std::cout << "Enter something\n -->";
std::cin >> something;
//Switch to read "something"
switch(something) {
case 'a':
cout << "You entered A, which is correct";
done = true; // exit condition here
break;
case 'b':
cout << "...";
break;
}
}
Yes, C and C++ have no way to say "exit multiple breakable blocks" (where a "breakable block" is any loop or switch). Workarounds include gotos and use of boolean variables to record whether an outer "breakable block" should also break (neither is elegant, but, that's life).
Two break statements will not get you out of the while loop. The first break only gets you out of the switch statement and the second one is never reached.
What you need is to make the condition of the while loop false, assuming that there is nothing in the loop after the switch statement. If there is other code after the switch, you should check the condition after the switch, and break there.
bool done = false;
while(! done)
{
// do stuff
switch(something)
{
case 'a':
done = true; // exit the loop
break;
}
// do this if you have other code besides the switch
if(done)
break; // gets you out of the while loop
// do whatever needs to be done after the switch
}
You could try:
Using Flags
Using Goto
Having the Inner Breakable block into a function
Using Exceptions
Using longjump and setjmp
A topic very similar to this question
http://www.gamedev.net/community/forums/topic.asp?topic_id=385116
You might be interested in the named loop idiom in C++.
#define named(blockname) goto blockname; \
blockname##_skip: if (0) \
blockname:
#define break(blockname) goto blockname##_skip;
named(outer)
while(1) {
//Loop can be conditional or 1, I use it alot, for example in my game
char something;
std::cout << "Enter something\n -->";
std::cin >> something;
//Switch to read "something"
switch(something) {
case 'a':
cout << "You entered A, which is correct";
break(outer);
case 'b':
cout << "...";
break(outer);
}
}
You can also encapsulate the loop into a function and call return inside the case, for the case that the flag breaking the while is not enough.
It is not a good programming practice for some people but if you keep the function simple I don't see why not.
You could replace the switch with a slightly over-engineered OO solution...
#include <iostream>
#include <map>
#include <set>
class input_responder
{
std::set<char> correct_inputs;
std::map<char, const char*> wrong_inputs;
public:
input_responder()
{
correct_inputs.insert('a');
wrong_inputs['b'] = "...";
}
bool respond(char input) const
{
if (correct_inputs.find(input) != correct_inputs.end())
{
std::cout << "You entered " << input << ", which is correct\n";
return true;
}
else
{
std::map<char, const char*>::const_iterator it = wrong_inputs.find(input);
if (it != wrong_inputs.end())
{
std::cout << it->second << '\n';
}
else
{
std::cout << "You entered " << input << ", which is wrong\n";
}
return false;
}
}
};
int main()
{
const input_responder responder;
char input;
do
{
std::cout << "Enter something\n -->";
std::cin >> input;
} while (responder.respond(input) == false);
}
You could change your switch to an ifsystem. It will be compiled to the same thing anyway.