How to make this C++ Program more robust? - c++

Sorry for the vague title but i really do not know how to describe the problem concisely..and the following is the simple codes:
#include <iostream>
using namespace std;
bool func(int a,char c,int b,int& result)
{
switch(c)
{
case '-':
result=a-b;
break;
case '+':
result=a+b;
break;
default:
return false;
}
return true;
}
int main()
{
cout<<"Usage:A {+|-} B"<<endl;
while(true)
{
int a,b,result;
char c;
cin>>a>>c>>b;
if(func(a,c,b,result))
{
cout<<result<<endl;
}
else
{
cout<<"Input Error!"<<endl;
}
}
return 0;
}
The right method to use the program is to input A {+|-} B.
For example,you can input 1[SPACE]+[SPACE]2[ENTER](which I mean "1 + 2" and then press ENTER) then it will spit out "3" for me.For the purpose of making the program more robust,I try to input 1[SPACE]+[SPACE]2[SPACE]+[ENTER] It just give me many "2" printed on the shell.Is there anyone who could tell me how to fix the bug?Many thanks!
PS:The codes is available at github:bug0x001.cpp

You fail to check if the input was read correctly. That's the first thing to do in a robust program. Also, if it failed, you should fix the input source. The endless stream of 2 is caused by a broken cin.
if( cin>>a>>c>>b &&
func(a,c,b,result))
{
}
else
{
cin.reset();
}

Your problem is that you are trying to get ints and char with cin, this is ok if you need that exact input, but crash when reading strange things (like a letter as int), as it reads garbage
The best solution (for me) is to read everything as a string (check cin.getline and stuff like that), then you "parse" the input as you whant, for example:
if you read "19+ 32 3" you can "easily" eliminate all spaces and split by any non numeric symbol, getting 3 strings: s1="19",s2="+" and s3="323", then you just parse each string as int (or whatever you need) and the symbol as char or whatever.
If you find anythng weird, you can try to understand it, eliminate it or just show an input error.
Is more complex than doing a cin.reset after testing, but allow you to "understand" more types of data input

Related

How to make program working properly after using goto?

After any selection, I want to ask the user if they want to use the program again, or quit. But if y is inputted code doesn't work properly again. I tried the other solutions like clearing memory etc but I am not very experienced so I don't know what I am doing. Also sorry about my language. This is a part of my code. I have 13 different selections each of them work the same
char str[100];
int selection;
char selection2;
int i;
begin:
printf("Welcome to the Character/String Specification program\n");
printf("Please enter whatever you want \n");
scanf(" %s", str);
printf("\nSelect the specification you want\n");
printf("1) is your input a letter?\n");
printf("2) is your input a whitespace?\n");
printf("3) is your input a decimal digit?\n");
printf("4) is your input a hexadecimal digit?\n");
printf("5) is your input an upper-case letter?\n");
printf("6) is your input a lower-case letter?\n");
printf("7) is your input a letter or a decimal digit?\n");
printf("8) is your input a control character?(ACSII 0..31 and 127)\n");
printf("9) is your input a not a letter, digit, whitespace, or invisible control character?\n");
printf("10)is your input a printable (ASCIII ' '..'~')\n");
printf("11)is your input have a graphical representation\n");
printf("12)Do you want to see your input as all upper characters?\n");
printf("13)Do you want to see your input as all lower characters?\n\n");
scanf(" %d",&selection);
printf("\n");
while(true)
{
if(selection == 1)
{
while(str[i])
{
if(isalpha(str[i]))
{
printf("%c is an alphabet\n",str[i]);
}
else
{
printf("%c is not an alphabet\n",str[i]);
}
i++;
}
printf("Do you want to go back to the start point? (y) for yes,(n) for no\n");
scanf(" %c",&selection2);
if(selection2=='y')
{
goto begin;
}
else if(selection2=='n')
{
printf("Goodbye\n");
break;
}
instead of using goto, I would do something like this:
int value = 1;
do {
//enter your code here
if(selection2=='n')
{
printf("Goodbye\n");
value = 0;
break;
}
} while(value)
This will cause the code to run at least once and continue running based on user input.
Goto is not the best practice as it makes it harder to read.

c++ if(cin>>input) doesn't work properly in while loop

I'm new to c++ and I'm trying to solve the exercise 6 from chapter 4 out of Bjarne Stroustrups book "Programming Principles and Practise Using C++ and don't understand why my code doesn't work.
The exercise:
Make a vector holding the ten string values "zero", "one", ...,
"nine". Use that in a program that converts a digit to its
corresponding spelled-out value: e.g., the input 7 gives the output
seven. Have the same program, using the same input loop, convert
spelled-out numbers into their digit form; e.g., the input seven gives
the output 7.
My loop only executes one time for a string and one time for an int, the loop seems to continue but it doesn't matter which input I'm giving, it doesn't do what it's supposed to do.
One time it worked for multiple int inputs, but only every second time. It's really weird and I don't know how to solve this in a different way.
It would be awesome if someone could help me out.
(I'm also not a native speaker, so sorry, if there are some mistakes)
The library in this code is a library provided with the book, to make the beginning easier for us noobies I guess.
#include "std_lib_facilities.h"
int main()
{
vector<string>s = {"zero","one","two","three","four","five","six","seven","eight","nine"};
string input_string;
int input_int;
while(true)
{
if(cin>>input_string)
{
for(int i = 0; i<s.size(); i++)
{
if(input_string == s[i])
{
cout<<input_string<<" = "<<i<<"\n";
}
}
}
if(cin>>input_int)
{
cout<<input_int<<" = "<<s[input_int]<<"\n";
}
}
return 0;
}
When you (successfully) read input from std::cin, the input is extracted from the buffer. The input in the buffer is removed and can not be read again.
And when you first read as a string, that will read any possible integer input as a string as well.
There are two ways of solving this:
Attempt to read as int first. And if that fails clear the errors and read as a string.
Read as a string, and try to convert to an int. If the conversion fails you have a string.
if(cin >> input) doesn't work properly in while loop?
A possible implementation of the input of your program would look something like:
std::string sentinel = "|";
std::string input;
// read whole line, then check if exit command
while (getline(std::cin, input) && input != sentinel)
{
// use string stream to check whether input digit or string
std::stringstream ss(input);
// if string, convert to digit
// else if digit, convert to string
// else clause containing a check for invalid input
}
To discriminate between int and string value you could use peek(), for example.
Preferably the last two actions of conversion (between int and string) are done by separate functions.
Assuming the inclusion of the headers:
#include <iostream>
#include <sstream>

Strange behaviour when reading in int from STDIN

Suppose we have a menu which presents the user with some options:
Welcome:
1) Do something
2) Do something else
3) Do something cool
4) Quit
The user can press 1 - 4 and then the enter key. The program performs this operation and then presents the menu back to the user. An invalid option should just display the menu again.
I have the following main() method:
int main()
{
while (true)
switch (menu())
{
case 1:
doSomething();
break;
case 2:
doSomethingElse();
break;
case 3:
doSomethingCool();
break;
case 4:
return 0;
default:
continue;
}
}
and the follwing menu():
int menu()
{
cout << "Welcome:" << endl
<< "1: Do something" << endl
<< "2: Do something else" << endl
<< "3: Do something cool" << endl
<< "4: Quit" << endl;
int result = 0;
scanf("%d", &result);
return result;
}
Entering numerical types works great. Entering 1 - 4 causes the program to perform the desired action, and afterwards the menu is displayed again. Entering a number outside this range such as -1 or 12 will display the menu again as expected.
However, entering something like 'q' will simply cause the menu to display over and over again infinitely, without even stopping to get the user input.
I don't understand how this could possibly be happening. Clearly, menu() is being called as the menu is displayed over and over again, however scanf() is part of menu(), so I don't understand how the program gets into this error state where the user is not prompted for their input.
I originally had cin >> result which did exactly the same thing.
Edit: There appears to be a related question, however the original source code has disappeared from pastebin and one of the answers links to an article which apparently once explained why this is happening, but is now a dead link. Maybe someone can reply with why this is happening rather than linking? :)
Edit: Using this example, here is how I solved the problem:
int getNumericalInput()
{
string input = "";
int result;
while (true)
{
getline(cin, input);
stringstream sStr(input);
if (sStr >> result)
return result;
cout << "Invalid Input. Try again: ";
}
}
and I simply replaced
int result = 0;
scanf("%d", &result);
with
int result = getNumericalInput();
When you try to convert the non-numeric input to a number, it fails and (the important part) leaves that data in the input buffer, so the next time you try to read an int, it's still there waiting, and fails again -- and again, and again, forever.
There are two basic ways to avoid this. The one I prefer is to read a string of data, then convert from that to a number and take the appropriate action. Typically you'll use std::getline to read all the data up to the new-line, and then attempt to convert it. Since it will read whatever data is waiting, you'll never get junk "stuck" in the input.
The alternative is (especially if a conversion fails) to use std::ignore to read data from the input up to (typically) the next new-line.
1) Say this to yourself 1000 times, or until you fall asleep:
I will never ever ever use I/O functions without checking the return value.
2) Repeat the above 50 times.
3) Re-read your code: Are you checking the result of scanf? What happens when scanf cannot convert the input into the desired format? How would you go about learning such information if you didn't know it? (Four letters come to mind.)
I would also question why you'd use scanf rather than the more appropriate iostreams operation, but that would suffer from exactly the same problem.
You need to verify if the read succeeded. Hint: it did not. Always test after reading that you successfully read the input:
if (std::cin >> result) { ... }
if (scanf("%d", result) == 1) { ... }
In C++ the failed state is sticky and stays around until it gets clear()ed. As long as the stream is in failed state it won't do anything useful. In either case, you want to ignore() the bad character or fgetc() it. Note, that failure may be due to having reached the end of the stream in which case eof() is set or EOF is returned for iostream or stdio, respectively.

Stack implementation to judge expressions

I wrote the following code to judge whether an expression entered by user has correct sequence of brackets or not, e.g. if user enters [a*(b+c)] its ok. But if he enters [a*(b+c)[ its not correct.
Stacklist.cpp is a file which contains the linked list implementation of stacks and definitions of functions of push and pop. Display is the function which just shows the top entry.
#include<iostream>
#include<exception>
using namespace std;
#include"stacklist.cpp"
int main()
{
string s;
cin>>s;//user inputs the string
stacklist<int> stack1;//the class in stacklist.cpp...int because all bracket's ascii values are ints
char c;
while((c=cin.get())!=EOF)
{
switch('c')
{
case '(': case '{': case '[':
stack1.push('c');
break;
case ')':
{char s=stack1.display();
try
{
if(s=='(')
{ stack1.pop();
continue;}
else
throw 5;
}//try block
catch(5) //.......(a)
{
cout<<"unmatched bracket error";
exit(-1);
}//catch over
}//')' case
break;
case '}': //.......(b)
{char s=stack1.display();
try
{
if(s=='{')
{ stack1.pop();
continue;}
else
throw 6;
}//try block
catch(6) //......(a)
{
cout<<"unmatched bracket error";
exit(-1);
}//catch over
}//'}' case
break;
case ']': ........(c)
{char s=stack1.display();
try
{
if(s==']')
{ stack1.pop();
continue;}
else
throw 7;
}//try block
catch(7) //.............(a)
{
cout<<"unmatched bracket error";
exit(-1);
}//catch over
}//']' case
break;//..........(d)
default:
break;
} //switch
} //while
if(stack1.display==0)//0 is displayed if stack is empty
cout<<"string is correct"<<endl;
else
cout<<"unequal number of brackets"<<endl;
system("pause"); //........(e)
return 0;
} //main
Now the problem is that when I compiled the code there were various errors:
syntax error before numeric constant.........in all (a)
case label '}' not within switch statement........(b)
case label ']' not within switch statement........(c)
syntax error before break.................(d)
ISO forbids declaration of 'system' with no type...........(e)
Please tell me how to plug these errors?
system is found in the header cstdio, but you did not #include that header.
Anyway, it's best not to use "tricks" like system("pause") to keep the console window after your program ends: if your console environment is not hanging around after your program has finished doing its meaningful work, then that's your console environment's fault/problem and you should configure it properly so that that does not happen. Pausing is not part of your program's job.
Catching looks like this:
catch (Type object-name) { code }
object-name is optional, but Type is not.
Therefore catch (6) is ill-formed. The other errors are a result of this one: parsing of your program goes all wonky when you write stuff that is not valid C++!
And your indentation is pretty horrendous.
C++ cannot catch a 5, but C++ can catch an int. Change all instances of catch(5) to catch(int) and that will solve that issue.
Also, on line 60, the ..... isn't commented, and in several places you compare string s to '{' chars instead of "{" strings.
Also, Tomalak Geret'kal noted that you have to #include <cstdlib> for the system and exit calls.

C++ - Quitting a program

In the C++ Without Fear: A Beginner's Guide That Makes You Feel Smart book in chapter(8), part of a code trying to display a text file is the following:
while(1)
{
for(int i=1; i <= 24 && !file_in.eof(); i++)
{
file_in.getline(input_line,80);
std::cout<<input_line<<std::endl;
}
if(file_in.eof())
{
break;
}
std::cout<<"More? (Press 'Q' and ENTER to quit.)";
std::cin.getline(input_line,80);
c=input_line[0]; // <<<<<<
if(c=='Q'||c=='q')
{
break;
}
}
The part I'm not getting here is:
c=input_line[0];
I think it is put to read 'Q' or 'q'. But, why using this form (Array)? And, isn't there a way to read 'Q' or 'q' directly?
I tried std::cin>>c; but seemed to be incorrect.
Any ideas?
Thanks.
Because input_line is string ( array from chars), so input_line[0] gets the first letter - this is in case, that the user write "quit" or "Quit", not just "Q"
std::cin >> c; would be correct, if you enter just one char and press Enter
I tried std::cin>>c; but seemed to be incorrect.
That's correct, if c is a char.
You're right; reading an entire line just to extract a single character is bizarre. I recommend a book from this list.
You are getting the first character from the "array" into which the input line has been written.
NON-STANDARD solution, but works on windows platforms.
you can use getch() function defined in conio.h
example:
#include <conio.h>
...
char c = getch();
bye