PDA

View Full Version : Need help with C++ cin...




ArtOfWarfare
May 9, 2012, 03:05 PM
I'm making a simple command line tool in C++. It asks the user a yes or no question and needs a valid response before it can move on.

Here's my code:
#include <iostream>

using namespace std;

int main(int argc, char* argv[])
{
askComplex:
cout << "Will complex quoefficients be needed?" << endl << "Y/N > ";
char complex = 'U';
cin >> complex;
switch (complex)
{
case 'Y': case 'y':
cout << "Complex coefficients will be used." << endl;
cout << "Enter a1's real component > ";
break;
case 'N': case 'n':
cout << "Complex coefficients will not be used." << endl;
cout << "Enter a1 > ";
break;
default:
cout << complex;
cout << " isn't valid input. Enter Y for yes or N for no." << endl;
cout << endl;
goto askComplex;
break;
}
return 0;
}

Here's some sample input/output...

First off, several examples of it working properly:
Will complex quoefficients be needed?
Y/N > Y
Complex coefficients will be used.
Enter a1's real component >
Will complex quoefficients be needed?
Y/N > Yes
Complex coefficients will be used.
Enter a1's real component >
Will complex quoefficients be needed?
Y/N > No
Complex coefficients will not be used.
Enter a1 >
Will complex quoefficients be needed?
Y/N > U
U isn't valid input. Enter Y for yes or N for no.

Will complex quoefficients be needed?
Y/N > Y
Complex coefficients will be used.
Enter a1's real component >

Here's where I have an issue... if the user types more than one character, when it gets to cin, it'll just take the next character from what the user already entered! How can I make it forget about what the user previously entered?

Will complex quoefficients be needed?
Y/N > Shucks, beats me!
S isn't valid input. Enter Y for yes or N for no.

Will complex quoefficients be needed?
Y/N > h isn't valid input. Enter Y for yes or N for no.

Will complex quoefficients be needed?
Y/N > u isn't valid input. Enter Y for yes or N for no.

Will complex quoefficients be needed?
Y/N > c isn't valid input. Enter Y for yes or N for no.

Will complex quoefficients be needed?
Y/N > k isn't valid input. Enter Y for yes or N for no.

Will complex quoefficients be needed?
Y/N > s isn't valid input. Enter Y for yes or N for no.

Will complex quoefficients be needed?
Y/N > , isn't valid input. Enter Y for yes or N for no.

Will complex quoefficients be needed?
Y/N > b isn't valid input. Enter Y for yes or N for no.

Will complex quoefficients be needed?
Y/N > e isn't valid input. Enter Y for yes or N for no.

Will complex quoefficients be needed?
Y/N > a isn't valid input. Enter Y for yes or N for no.

Will complex quoefficients be needed?
Y/N > t isn't valid input. Enter Y for yes or N for no.

Will complex quoefficients be needed?
Y/N > s isn't valid input. Enter Y for yes or N for no.

Will complex quoefficients be needed?
Y/N > m isn't valid input. Enter Y for yes or N for no.

Will complex quoefficients be needed?
Y/N > e isn't valid input. Enter Y for yes or N for no.

Will complex quoefficients be needed?
Y/N > ! isn't valid input. Enter Y for yes or N for no.

Will complex quoefficients be needed?
Y/N > Maybe
M isn't valid input. Enter Y for yes or N for no.

Will complex quoefficients be needed?
Y/N > a isn't valid input. Enter Y for yes or N for no.

Will complex quoefficients be needed?
Y/N > Complex coefficients will be used.
Enter a1's real component >



ArtOfWarfare
May 9, 2012, 03:22 PM
Naturally, writing out a post asking for help ended up helping me think of what to google to find a good answer. Namely, "flush cin", which didn't turn up much, and then "ignore cin", which gave me a pretty good solution:

(The bolded line was the only one I needed to add.)
int main(int argc, char* argv[])
{
askComplex:
cout << "Will complex quoefficients be needed?" << endl << "Y/N > ";
char complex = 'U';
cin >> complex;
switch (complex)
{
case 'Y': case 'y':
cout << "Complex coefficients will be used." << endl;
cout << "Enter a1's real component > ";
break;
case 'N': case 'n':
cout << "Complex coefficients will not be used." << endl;
cout << "Enter a1 > ";
break;
default:
cout << complex;
cout << " isn't valid input. Enter Y for yes or N for no." << endl;
cout << endl;

std::cin.ignore(INT_MAX, '\n');

goto askComplex;
break;
}
return 0;
}

(Apologies to the mods for accidentally posting this in the iOS programming section at first. Given how much of my work is for Mac OS X recently, it seems like I'd bookmark the Mac OS X programming forums in addition to, if not instead of, the iOS programming forums...)

Mac_Max
May 9, 2012, 11:59 PM
<troll>Don't use goto! Especially when you have while loops....</troll>

Also when handling input I usually std::string or std::wstring as a buffer and then use the [] operator to access it. I've found that there are some funky edge cases you can run into trouble with when multiple characters are input and you insert into a char or char array.

http://www.cplusplus.com/reference/string/string/operator[]/

ArtOfWarfare
May 10, 2012, 11:03 PM
What's wrong with goto? I had a specific point I wanted the program to refer to and I figured goto and a label would be the easiest code to read and understand for anyone editing my code later on.

Also, I don't have a while loop... Not sure why you said "especially when you have while loops..."

Mac_Max
May 11, 2012, 03:06 AM
What's wrong with goto? I had a specific point I wanted the program to refer to and I figured goto and a label would be the easiest code to read and understand for anyone editing my code later on.

Also, I don't have a while loop... Not sure why you said "especially when you have while loops..."

I meant as in you have while loops available. What you've written is functionally the same as:


#include <iostream>

using namespace std;

int main (int argc, const char * argv[])
{
bool cont;
do{
cout << "Will complex quoefficients be needed?" << endl << "Y/N > ";
char complex = 'U';
cin >> complex;

cont = false;
switch (complex)
{
case 'Y': case 'y':
cout << "Complex coefficients will be used." << endl;
cout << "Enter a1's real component > ";
break;
case 'N': case 'n':
cout << "Complex coefficients will not be used." << endl;
cout << "Enter a1 > ";
break;
default:
cout << complex;
cout << " isn't valid input. Enter Y for yes or N for no." << endl;
cout << endl;
std::cin.ignore(INT_MAX, '\n');
cont = true;
break;
}

} while(cont);

return 0;
}


One big problem with using goto in C++ is that it essentially can allow you to evade an object's construction/destruction. If you add any object initialization within the switch statement and before the goto, XCode won't let you compile the code. It's really bad form because it can cause leaks or data corruption (or both... good times!). Older/different compilers/IDEs might let you do something like that. If they do, code like this becomes dangerous:


class test {
int* Data;

public:
test()
{
Data = new int(22);
}
~test()
{
std::cout << "In Destructor!";
delete Data;
}
void print()
{
std::cout << *Data << std::endl;
}
};

...
in some functional unit
...

int val = 1;

leakstart:

if (val == 1)
{
test ThisWillLeakOrCorrupt;
val = 2;
goto leakstart:
} else {
test ThisWillWorkFine;
}



When following the RAII idiom objects designed to be properly deallocated when a stack based instance variable goes out of scope. By using goto, the first test variable never has its destructor called and thats bad juju. If the object being wrapped by the RAII class is non-trivial, you're going to have a very bad day. Even if the resource being used is created dynamically and isn't a stack variable, goto can still be a cause of leaks, especially if you goto over your delete call. Even when memory isn't an issue, escaping scope can lead to odd edge cases and other problems. When used too much it turns your code into a mess thats difficult to follow.

Here's an example of where goto can be used... arguably correctly...

http://www.cprogramming.com/tutorial/goto.html

At the same time, I agree with the author's notion that there must be a better way to do this. I can think of a few: exceptions, using RAII objects, reorganizing code into functors or regular objects, inverting control using closures, or simply just using a cleanup function that you can call.


int big_function()
{
int ret_val = [success];
try
{
/* do some work */
}
catch(myExceptionType e1)
{
/* code to recover */
ret_val = e1.ErrorCode;
}
catch(exception e6)
{
/* code to recover */
ret_val = -1; //generic error
}

/* cleanup */

return ret_val;
}


Even that is considered less than optimal design by some if the exceptions being caught are thrown within the same function/method.

Here's a StackOverflow post with more details:

http://stackoverflow.com/questions/379172/to-use-goto-or-not

Goto might have worked for you when writing C, but C++ when used as a "Better C" is IMO, actually a "Worse C." Modern C++ is a very different language than C (or old C++ for that matter) and should be treated differently, just as you wouldn't want to write Objective C the same way you'd write C++.