How to efficiently structure a terminal application with multiple menus? - c++

I'm writing a console based program for my coursework, and am wondering how best to structure it so that it is both stable and efficient. I currently have
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
int choice;
do
{
cout << "\E[H\E[2J" // Clear the console
<< "Main menu" << endl << endl
<< "Please select one of the following options by entering it's "
<< "number in the prompt below and pressing [ENTER]. "
<< endl << endl
<< "1. Pay my bill as a guest" << endl
<< "3. Log in" << endl
<< "2. Create an account" << endl
<< "4. Quit program" << endl;
cin >> choice;
switch (choice)
{
case 1: // Pay the bill as a guest to the system
case 2: // Log in to the system
case 3: // Create an account with the system
case 4: // Quit the program
default: // Prompt the user to choose again
}
} while !(default);
// Await user input to terminate the program
cout << "Please press [ENTER] to continue...";
cin.get();
return 0;
}
The purpose of the above code is to provide a list of options for the user to choose from, with the do-while loop working alongside the default statement in the switch to catch any unexpected input. Each case would call a function that presented another menu with it's own list of options, which would be structured using the same do-while, switch method. My concern is that as my program grows, the number of function calls being nested within other functions is going to increase, so that I would eventually end up with a function being called from within a function being called from within a function and so on. This would obviously have severe implications for the maintainability of the program, with the function calls moving further and further away from main(), and the output of these functions weaving a tangled path about the program.
Is it possible to structure my program in such a way as to return execution to main() as often as possible, or is the problem described above simply a consequence of this kind of programming?
NB: I ask this question in the understanding that user-defined functions are supposed to be ancillary to main(), and that they should perform a task before returning control to main() as the earliest possible convenience. I've only been at this a couple of months now so please bear with my ignorance/misunderstanding. Also, ignore any potential compiler errors in the code, I've not tested it yet and it's only provided as an aide to my conceptual question.

I would apply some OO-design and create a menu-class which basically stores items/sub-menus in a vector. This would make it easy to extemd to hierarchical menus

There is nothing particularly wrong with what you've done.
I don't see why it harms maintainability to have functions called from functions and so on. If anything it AIDS maintainability as you can move common code operations into seperate functions. This way you make a fix in one place and instantly fix the rest of the places its used as well.

Well, you can implement your menu structure as a state-machine, so you will be almost always in your main loop. But this can bring your code to the lower level, because you will be effectively programming not in C++ but in your state-machine processor-near code. If your state machine will be good enough, this is not a problem.
Or you can a simple menu-runner class, which will output as a result a request for submenu, so you will just exchange (perhaps using a stack) the description of the currently running menu.
By the way, I don't see any problems in deep nesting of the functions.

Another possible approach is to make a class defined as a list of (menu_option, function) pairs and the know-how to turn them into menus. Then the function can be a call to another class instance's menu or it can do some operation on your database. That lets you keep your data organized away from the business "how to display this menu" logic and add menus and menu items easily.
Don't worry about that or your current approach spending too much time away from main though. As you've structured it, your program won't automatically turn itself into a horrible mess just because you're calling functions from functions. More functions will tend to add to maintainability, as long as you keep them focused.
Think of it this way: a function does one thing, but at a higher level of abstraction than its body. So main() runs your program. create_account() will create an account, which is part of running the program. create_account itself calls several other things that do the building blocks necessary for creating an account. Is determining the new account's name one thing? It goes in its own function. Determining the index of the new account in the database? Too low-level. Put it in the "stuff it in the database" function.

The complexity of the code will correlate to the functionality offered by the program. I would not worry about this right now, revisit refactoring once you have two or three hundred lines.

for use this code you must add:
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "kharid");
memcpy(st_menues[cnt++].layer, "100000", sizeof(st_menues[cnt].layer));
this block code for insert menu item .
for change item to subitem you must change:
"100000"
to
"010000"
for do a difference work per every item you must define multiple:
void tmp_func(char* str)
void tmp_func1(char* str)
void tmp_func2(char* str)
...
and insert in
st_menues[cnt].function_pointer = tmp_func1;
...
st_menues[cnt].function_pointer = tmp_func2;
...
and insert your source code in these functions.
this source can compile in vc++.
i didn't test its in linux . but perhups works.
#include <iostream>
#include <stdio.h>
#include <conio.h>
#include<stdlib.h>
char title_str[20] = " main menu \n";
void print_this(bool with_title,const char* str, ...)
{
if(with_title)printf(title_str);
printf(str);
}
void clear()
{
#ifdef _WIN32
system("cls");
#else
std::cout << "\033[2J\033[1;1H";
#endif
print_this(true,"");
}
struct def_struct_menu
{
void (*function_pointer)(char*);
char menu_string[24];
char layer[7];
int pos;
};
void set_title(char* str)
{
sprintf(title_str," %s \n",str);
}
void tmp_func(char* str)
{
clear();
set_title(str);
printf("calc okokok");
_getch();
}
def_struct_menu st_menues[100] = { 0 };
def_struct_menu st_cur_menues[100] = { 0 };
void back_to_main_menu(int& highlight_line, int& cur_layer, int& start)
{
highlight_line = 0;
cur_layer = 0;
start = 0;
set_title((char*)"main menu");
}
int main()
{
int cnt = 0;
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "kharid");
memcpy(st_menues[cnt++].layer, "100000", sizeof(st_menues[cnt].layer));
{
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "asan");
memcpy(st_menues[cnt++].layer, "010000", sizeof(st_menues[cnt].layer));
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "shenase");
memcpy(st_menues[cnt++].layer, "010000", sizeof(st_menues[cnt].layer));
}
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "sharj");
memcpy(st_menues[cnt++].layer, "100000", sizeof(st_menues[cnt].layer));
{
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "ramz");
memcpy(st_menues[cnt++].layer, "010000", sizeof(st_menues[cnt].layer));
{
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "yekbarmasraf");
memcpy(st_menues[cnt++].layer, "001000", sizeof(st_menues[cnt].layer));
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "qrcode");
memcpy(st_menues[cnt++].layer, "001000", sizeof(st_menues[cnt].layer));
}
st_menues[cnt].pos = cnt;
st_menues[cnt].function_pointer = tmp_func;
strcpy_s(st_menues[cnt].menu_string, "mostaghim");
memcpy(st_menues[cnt++].layer, "010000", sizeof(st_menues[cnt].layer));
}
const int ST_SIZE = cnt;
int input = 0;
int highlight_line = 0;
int cur_layer = 0;
int start = 0;
while (input != -1)
{
int size = 0;
memset(st_cur_menues, 0, sizeof(def_struct_menu) * ST_SIZE);
for (int i = start; i < ST_SIZE; i++)
{
if (cur_layer > 0)
{
if (st_menues[i].layer[cur_layer - 1] == '1')
{
break;
}
}
if (st_menues[i].layer[cur_layer] == '1')
{
memcpy(&st_cur_menues[size++], &st_menues[i], sizeof(def_struct_menu));
}
}
clear();
if (size == 0)
{
back_to_main_menu(highlight_line, cur_layer, start);
}
for (int i = 0; i < size; i++)
{
if (highlight_line == i)
print_this(false,"*");
else
print_this(false," ");
print_this(false,st_cur_menues[i].menu_string);
print_this(false,"\n");
}
//print_this("enter number\n");
input = _getch();
switch (input)
{
case 'x':
{
exit(0);
}
case 27://escape button
{
back_to_main_menu(highlight_line, cur_layer, start);
break;
}
case 13://enter button
{
if (size == 0)
{
back_to_main_menu(highlight_line, cur_layer, start);
break;
}
st_cur_menues[highlight_line].function_pointer(st_cur_menues[highlight_line].menu_string);
start = st_cur_menues[highlight_line].pos + 1;
cur_layer++;
highlight_line = 0;
}
break;
case 72://up arrow key
{
if (highlight_line == 0)
highlight_line = (size - 1);
else
highlight_line--;
}
break;
case 80://down arrow key
{
if (highlight_line == (size - 1))
highlight_line = 0;
else
highlight_line++;
}
break;
default:
break;
}
}
return 0;
}
tnx

Related

Is this a good method for counting threads in C++?

I've been looking for a reliable way to count the number threads accessible to a program to be used. I didn't want to use a constant though and make the assumption that every system had the same number of accessible threads. I've devised this method of trying to figure it out. Is it a good method?
#include "pch.h"
#include <iostream>
#include <thread>
using namespace std;
struct list
{
void *data;
list *next;
list(list *x = nullptr)
{
data = x;
next = nullptr;
}
void add()
{
next = new list;
}
};
void sleepo(int xz)
{
for (int x = 0; x < 10000000; x++)
{
xz++;
}
}
int main()
{
int count = 1;
list *iterator = new list;
cout << "Attmepting to count threads..." << endl;
while (true)
{
try
{
iterator->data = new thread(sleepo, count);
iterator->add();
iterator = iterator->next;
count++;
}
catch(system_error)
{
break;
}
}
cout << "There are " << count << " threads." << endl;
}
No, there is no standard way to count the number of threads that have been started by a program. Neither in the C++ standard, nor for example in the POSIX standard.
I've devised this method of trying to figure it out. Is it a good method?
If you are in control of the creation of every thread, then that would work just fine. But it won't work if you for example use a library that also creates threads unless you can somehow inject code that increments your counter.
In order to know the current number of threads instead of total number of started threads, you would need to decrement the counter each time you join.

Shared container accessed by different threads

I have a concurrent_vector (ppl) container declared as a global variable that represents the entry to two functions/threads.
I want it to be accessed by the threads simultaneously (one for reading and one for writing/resizing). My program (in C++) includes a section where I check if the container is empty. Since one thread shows that the buffer is empty while the other desn't, it seems to me that both threads operate on two distinct containers although I defined only one.
#include "stdafx.h"
#include "ppl.h"
concurrent_vector<dataElm> ResultImage;
int AcquireImages(CameraPtr pCam){
continue_recording = true;
pCam->BeginAcquisition();
int imageCnt = 0;
while (continue_recording == true)
{
ImagePtr _p = pCam->GetNextImage(1000);
imageCnt = imageCnt + 1;
dataElm obj = constructelm(_p, &loc, imageCnt - 1);
ResultImage.push_back(obj);
cout << "is buffer empty? " << ResultImage.empty() << endl;
}
//...
}
void Cam(){
//...
pCam->Init();
INodeMap& nodeMap = pCam->GetNodeMap();
result = result | AcquireImages(pCam);
pCam = nullptr;
//...
}
void saveImages() {
//...
cout << "ResultImage.empty() = " << ResultImage.empty() << endl;
if (ResultImage.empty() == false) {
//saving the image
}
else
{
Sleep(20);
}
}
int main(int, char**){
std::thread producer(Cam);
std::thread consumer1(saveImages);
producer.join();
consumer1.join();
return 0;
}
error message
Also, do I need to add synchronization primitives even though I'm using concurrent_vector?
I'm new to multi-threading so I'm sorry if my question seems stupid and excuse my english, i'm not american native.

Using Composite Pattern to make game menu

I'm making a game menu using a composite pattern. I want to achieve a tree structure game menu, where some leaves are pushing new state on the top of my state machine and another in options should show for example slider to change the volume without making new state and another (exit) should close the game by running sfml method.
Can someone give me a better idea than returning string or enum by operation() method to menu state to run expected action by using value in if/switch?
Here is an example of a menu state table using a struct:
typedef void (*Function_Pointer)(); // Declares a synonym for Function_Pointer
struct Table_Entry
{
char expected_selection;
char * prompt;
Function_Ptr processing_function;
};
// Forward declarations
void Turn_On_Lights();
void Turn_Right();
void Open_Treasure();
// State table
static const Table_Entry menu1[] =
{
{'1', "Turn on lights", Turn_On_Lights},
{'2', "Turn right (pivot)", Turn_Right},
{'3', "Open Treasure", Open_Treasure},
};
static size_t menu_entry_quantity =
sizeof(menu1) / sizeof(menu1[0]);
void Display_Menu()
{
for (unsigned int i = 0, i < menu_entry_quantity; ++i)
{
std::cout << menu1[i].expected_selection
<< ". "
<< menu1[i].prompt
<< "\n";
}
std::cout << "Enter selection: ";
}
void Execute_Menu_Selection(char selection)
{
for (unsigned int i = 0, i < menu_entry_quantity; ++i)
{
if (selection == menu1[i].expected_selection)
{
(*menu1[i].processing_function)();
break;
}
}
}
The above code allows you to change the quantity of entries or the entry content, without having to retest the functions. (Nice)
Since the data is static constant, it can be accessed directly and doesn't need to be initialized before the program starts.
You can expand this by using a "transition" column or member. For example, list the next states (or menus) to transition to when given a transition ID.

Is there a way to run a statement when a condition in a loop is met at least once?

I am currently doing games on my free time and am currently working on a hangman game. However, I have stumbled upon a problem and I think I could solve it if there was a way to run a statement if a condition inside a loop is met at least once, and if the condition isn't met even once, it'll do another thing. Is it possible to do? Does anyone have any ideas?
I appreaciate any suggestions.
I tried doing something like this:
for (){
if (string [i] == letter that the player inputed){
// replace underscores with the letter guessed
// and also turn a bool statement true
}
else {
// turn the bool statement false
}
}
if (!bool variable){
// print that the letter guessed was not in the answer
// and substract one from the number of guesses available
}
However I noticed that it doesn't work because the loop will run and if the last letter that it checks is not in the answer, the bool will turn false, thus printing that the letter was not in the answer and substracting one from the score. (It's also my first time posting here, and I don't know if that's how I'm supposed to write a code, so I apologize beforehand if I'm not doing it correctly)
`
You should approach this problem from the different angle:
for( ... ) {
if( your condition is met ) {
do_whatever_you_have_to();
break; // <<--- exit the loop, so it's done only once
}
}
You don't have to put flag guessed off if the comparation fails
string s;
bool guessed = false;
char inputted_letter; // comes from somewhere
for (size_t i = 0; i < s.size(); ++i) {
if (s[i] == inputted_letter) {
// replace underscores with the letter guessed
guessed = true;
}
}
if (!guessed) {
// print that the letter guessed was not in the answer
// and substract one from the number of guesses available
}
You don't have to set false in the loop:
bool has_found = false;
for (auto& c : word_to_guess)
{
if (input_letter == c) {
// replace _ by current letter...
has_found = true;
}
}
if (!has_found){
// print that the letter guessed was not in the answer
// and substract one from the number of guesses available
}
But I suggest that your loop does only one thing at a time:
bool contains(const std::string& word_to_guess, char input_letter)
{
return std::any_of(word_to_guess.begin(),
word_to_guess.end(),
[&](char c){ return input_letter == c; })
/*
for (auto& c : word_to_guess)
{
if (input_letter == c) {
return true;
}
}
return false;
*/
}
if (contains(word_to_guess, input_letter)
{
// show current letter...
for (std::size_t i = 0; i != hangman_word.size(); ++i) {
if (word_to_guess[i] == input_letter) {
hangman_word[i] = word_to_guess[i];
}
}
} else {
// print that the letter guessed was not in the answer
// and substract one from the number of guesses available
}
Can you do what you are asking; possibly, however you stated you were making the game Hangman in C++ and I think you are going about this with the wrong approach, therefore, choosing or implementing the wrong algorithms. You are trying to traverse through two strings with possible different lengths from the back end which if it isn't done correctly can lead to issues, will tend to be hard to track especially if their comparisons determine loop conditions, exit or return statements.
I have implemented my version of "Hangman", now albeit the formatting isn't the prettiest, nor are the level dictionaries being generated from a large pool of random words. I express this in the comments of the code that these would generally be read in from a text file and saved into these structures. For simplicity's sake, I initialized them with random words directly in the code.
Take a look to see what I've done:
#include <string>
#include <iostream>
#include <vector>
#include <map>
#include <random>
class Game;
int main() {
using namespace util;
try {
Game game("Hangman");
game.start();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
class Game {
private:
std::string title_;
bool is_running_{ false };
std::string answer_;
std::string guessed_;
std::map<unsigned, std::vector<std::string>> dictionary_; // unsigned represents difficulty level of word
unsigned choosen_difficulty_;
std::string guessed_characters_{"\n"};
public:
Game(const std::string& title) : title_{ title }, choosen_difficulty_{ 0 } {
initialize();
start_over();
}
void start() {
is_running_ = true;
// the player has as many attempts as twice the length of hidden answer's word length.
int number_tries = answer_.size() * 2;
while (is_running_ || number_tries > 0) {
displayGuessedWord();
displayGuessedCharacters();
// ask the player to guess a character;
char guess;
// get a character and make sure it is a valid alphabet character
do {
std::cout << "Guess a character ";
std::cin >> guess;
// Note: I'm using ascii characters in this case
// but for demonstration purposes only!
if ((guess < 'a' && guess > 'z') ||
(guess < 'A' && guess > 'Z')) {
std::cout << "invalid entry ";
}
} while ( (guess < 'a' && guess > 'z') ||
(guess < 'A' && guess > 'Z') );
// test character and update guessed word and number of tries.
test_character(guess);
update_guessed_characters(guess);
number_tries--;
// failed condition
if (number_tries <= 0 && guessed_ != answer_) {
std::cout << "\nGame Over!\n";
is_running_ = try_again(number_tries);
// winning condition
} else if (number_tries > 0 && guessed_ == answer_) {
std::cout << "\nCongratulations!\n";
is_running_ = try_again(number_tries);
}
if (!is_running_) break;
}
}
private:
void displayGuessedWord() {
std::cout << '\n' << guessed_ << '\n';
}
void displayGuessedCharacters() {
std::cout << guessed_characters_ << '\n';
}
void initialize() {
// Normally this would be read in from a file upon game initialization
// but for demonstration purpose, I'll generate a few small vectors of strings
// and associate them to their difficulty level
// levels are based on 3 factors, the length of the word, the repetitive occurance
// of common characters, and the amount of less commonly used characters.
std::vector<std::string> level_1{ "ate", "cat", "dog", "coat", "coal", "does" };
std::vector<std::string> level_2{ "able", "believe", "balloon", "better", "funny", "happy" };
std::vector<std::string> level_3{ "ability", "carpenter", "dogmatic", "hilarious", "generosity", "hostility" };
// ... etc. I'll use just these here for simplicty where each vector has six entries, however,
// with random number generators, this can be done generically for any size
// or number of elements in each of the different vectors.
// create generate the map:
dictionary_[1] = level_1;
dictionary_[2] = level_2;
dictionary_[3] = level_3;
}
std::string getWordFromDictionary(unsigned difficulty, std::map<unsigned, std::vector<std::string>>& dict) {
auto level = dict[difficulty]; // extract the vector based on difficulty level
auto size = level.size(); // get the size of that vector
std::random_device dev; // create a random device
std::mt19937 rng(dev()); // create a pseudo random generator
// create a uniform int distribution type with the range from 0 to size-1
std::uniform_int_distribution<std::mt19937::result_type> dist(0, size - 1);
return level[dist(rng)]; // return a random string from this level.
}
void start_over() {
system("cls"); // Note: I'm running visual studio on Windows!
std::cout << "Welcome to " << title_ << '\n';
// We can use a random generator to pick a word from the given difficulty
// but first we need to get user input for the chosen level.
do {
std::cout << "Choose your difficulty [1-3]\n";
std::cin >> choosen_difficulty_;
if (choosen_difficulty_ < 1 || choosen_difficulty_ > 3) {
std::cout << "Invalid entry:\n";
}
} while (choosen_difficulty_ < 1 || choosen_difficulty_ > 3);
answer_ = getWordFromDictionary(choosen_difficulty_, dictionary_);
// clear and resize guessed word to be that of answer_ and add bunch of hypens.
guessed_.clear();
guessed_.resize(answer_.size(), '-');
// also reset the guessed_characters
guessed_characters_ = std::string("\n");
}
bool try_again(int& tries) {
std::cout << "Would you like to try again?\n";
char c;
std::cin >> c;
if (c == 'y' || c == 'Y') {
start_over();
// don't forget to update this so that the loop can repeat
tries = answer_.size() * 2;
return true;
}
else {
std::cout << "Thank you for playing " << title_ << '\n';
return false;
}
}
void test_character(const char c) {
// here is where you would use the standard library for taking the character
// passed into this function, updating the guessed_characters
// get all indexes
std::vector<unsigned> locations;
for (unsigned i = 0; i < answer_.size(); i++)
if (answer_[i] == c)
locations.push_back(i);
// now update the guessed word
if ( locations.size() > 0 )
for (size_t n = 0; n < locations.size(); n++)
guessed_[locations[n]] = c;
}
void update_guessed_characters(const char c) {
guessed_characters_.insert(0, &c); // just push to the front
}
};
If you noticed how I structured the game class above; I am using while and do-while loops in conjunction with for-loops and if-statements and a single boolean flag to determine the state of the game. The game state is also determined from the update to the guessed characters and guessed word. Then I compare that to the answer. Depending on certain conditions the loop will continue seeking input from the user or will exit.
I am not guaranteeing that this code is 100% bug-free for I didn't do any rigorous testing or checking corner cases or special cases but the code has run without error and I've tested all primary game state cases. It appears to be working fine.
I know that there could be many improvements and simplifications made if I had chosen to use some of the standard library functions for working with strings, but I wanted to illustrate the individual steps that are involved in the design or thinking process of making a game with states and their transitions. I could of also put the game class declaration into its own header file with its implementation in a cpp file, but I left that as a single class that is shown in main.cpp for easy copy and paste and compilation.
With this particular game, I did not use a switch and case statements, I just stuck with some while and do-while loops, a few for loops, and if statements since there are only a few game states and transitions to worry about. This implementation also demonstrates the algorithms that are involved and shows how they interconnect with each other. I hope this helps to give you a better understanding of the design process.
When making a game that has different states with a bit of complexity to it, you should start by making your state table first and list all of its transitions before you even write any code. Then you should list your starting, continuing, winning, failing and exiting states or cases. Then you need to draw up how you would transition from one state to another by their required conditions. This will help you in the long run!
Once you have the game state and its transitions laid out properly, then you can start to make your required functions for those states and begin to connect them together. After that is when you would write the internal of the functions or their implementation of what they would do.
Finally, after you have that down is where you want to do some debugging and unit and case testing and if everything appears to be okay, then it would be safe to improve your current algorithms or choosing better ones for peak or most efficient performance.

how to replace a value of a variable inside code from user input?

i am trying to add a developer mode in my program. since duty of car defers every month,i want give my user permission to change every single variables in my program alike duty lccost yen2taka freight
#include <iostream>
using namespace std;
class A
{
public:
int carbid,duty;
void Input()
{
cout<<"please insert the car price you want to bid for(in yen): ";
cin>>carbid;
cout<<"duty of the car: ";
cin>>duty;
}
int Exportcost()
{
int exportcost;
int servicechrg=10;
int freight=20;
exportcost=servicechrg+freight+carbid;
return exportcost;
}
int Yen2taka()
{
int yen2taka;
int taka2dollarrate=10;
int dollar2yen=1;
yen2taka=((Exportcost())/dollar2yen)*taka2dollarrate;
return yen2taka;
}
int Importcost()
{
int importcost;
int lccost=10;
int cnfcost=20;
importcost=lccost+cnfcost;
return importcost;
}
int Totalcosting()
{
int total;
int myprofit=10; //80000
total=myprofit+Importcost()+Yen2taka();
cout<<total;
return total;
}
void summary()
{
cout<<
}
};
int main()
{
x:
A ob;
ob.Input();
ob.Exportcost();
ob.Yen2taka();
ob.Importcost();
ob.Totalcosting();
int ch;
cout<<"press 1 to see the summery of the costing or 2 to restart costing again"<<endl;
cin>>ch;
switch(ch)
{
case 1:
ob.summary();
break;
case 2:
goto x;
}
}
At first, you should collect these parameters in a separate class:
class Configuration // maybe you find a better name...
{
int m_servicechrg = 10; // default
int m_freight = 20;
// ...
public:
int servicechrg() { return m_servicechrg; }
void servicechrg(int value); { /* check some limits? */ m_servicechrg = value; }
int freight() { return m_freight; }
void freight(int value); { /* check some limits? */ m_freight = value; }
// ...
};
// will allow you to do:
// C c; std::cout << c;
ostream& operator<<(ostream& s, Configuration const& c)
{
// which ever formatting is appropriate...
s << c.servicechrg() << ' ' << c.freight();
return s;
}
The setters could alternatively return bool to indicate invalid values.
Now you can use this class within main:
Configuration c;
A a;
int cost = a.exportCost(c); // you'd have to adjust signatures...
int value;
switch(ch)
{
case 4:
if(stc::cin >> freight) // catches invalid user input!
// one ALWAYS should do, otherwise you might end up in
// your program not working any more
{
c.freight(value);
// or, if you have:
if(!c.freight(value))
{
// some appropriate error message
// (it's better not to output in the setter, you are more flexible this
// way – maybe you want different messages at different occasions?)
}
}
else
{
// appropriate error handling
}
break;
default:
// handling invalid user input
// again, you always should; but stream state is not in error state,
// so you just can print appropriate error message
break;
}
See this answer for how to correctly handle stream errors.
If you wonder about the differences in error handling: First case is met if user enters non-numerical input, such as ss, second case, if input is numerical, but out of valid range (77).
Now if you don't want to pass the configuration as parameter all the time, you could make a global variable from (but careful, there are some dangers with global variables, use them as sparely as possible) or implement the singleton pattern.
Side notes: goto can be a fine tool sometimes, but it is a dangerous one (and the label's name x isn't a good one, prefer a name that clearly shows intention, such as REENTRY_POINT, LOOP_START, ...). If you can get along without unreasonable effort, prefer such variants:
bool isRunning = true;
do
{
// ...
case 2:
isRunning = false;
break;
}
while(isRunning);
Sure, an additional variable, an additional check; unfortunately, you cannot use break to exit a (pseudo-) endless loop (for(;;)) (but don't apply this pattern for nested loops, then it gets more and more unreadabla – and ineffcient: bool isExit = false; for(int i = 0; !isExit && i < n; ++i) { for(j = 0; j < n; ++j) { isExit = true; break; } } – see what I mean?). A variant might be:
for(;;)
{
switch(ch)
case 1:
// ...
//break; <- replace
continue;
case 2:
//
break;
} // end of switch
break; // break the surrounding for(;;) loop
}
But that's not really nice either.
A pretty nice variant allowing to exit the loop in the given case, as there isn't anyhting to do afterwards:
for(;;)
{
switch(ch)
{
case 2:
// maybe yet some cleaning up here
return 0;
default:
// ...
break;
}
}
Drawback: The function's exit point possibly is deeply nested inside the code.
There are yet other tricks to allow this pattern, like packing sub-sections of code in a lambda having a return inside and call that one directly. But that now really starts going beyond the scope...
Finally, if you insist on goto, my variant would rather be:
for(;;)
{
switch(ch)
{
case 2:
// ...
goto LOOP_EXIT;
default:
// ...
break;
}
}
LOOP_EXIT:
return 0; // e. g. main
(void)0; // if there isn't anything to do in the function any more
// (labels require an instruction afterwards!)
There won't be a hidden loop now and it is more obvious what you actually are doing. Currently, not really an issue, but if your code grows, the hidden loop gets more and more difficult to spot.
In such cases, I clearly mark the gotos so that another coder can immediately spot the critical code points:
///////////////////////////////////////////////////
// possibly some comment why applying this pattern
goto SOME_LABEL;
///////////////////////////////////////////////////
One could do the same with deeply nested function exit points (return).