6

Look at this code:

#include <iostream>
using namespace std;
int main()
{
    string s;
    int n;
    float x;
again:
    cout << "Please Type this: ABC456 7.8 9 XYZ\n";
    cin >> s >> n >> x >> s;
    cout << "\nDo you want to try Again(y/n)? ";
    char t;
    cin >> t;
    if (t=='y' || t=='Y')
        goto again;
    return 0;
}

Just try to type "ABC456 7.8 9 XYZ" and press enter, it will cause the program exit before prompting the user to try again. I know the inputs are wrong and they are not in their types, but why it causes the exit? and how to avoid such exit?

6
  • @CppLearner It is a label for the labeled statement again: cout << "Please Type this: ABC456 7.8 9 XYZ\n";
    – Mankarse
    Commented Apr 30, 2012 at 6:15
  • @CppLearner: Its a label indicated for goto statement to be used. see 8th line dear....
    – Addicted
    Commented Apr 30, 2012 at 6:17
  • Thanks. Well first, I hope <string> is included in the header. Let me execute... It's hard to read with code are on the same line. Thanks.
    – CppLearner
    Commented Apr 30, 2012 at 6:17
  • 2
    You ask for a 7.8 (float) as the second item, but try to read that into n, an int. This is probably not a recipe for happiness (because x gets 0.8, s gets 9; and the blank or 'X' of XYZ is read as t). Commented Apr 30, 2012 at 6:19
  • 1
    Because neither blank nor X is the same as either y or Y, so the character read into t doesn't satisfy the looping criterion. The first thing to do when puzzled about something like this is to print out the key controlling values, in this case t. If you printed the value in t (cout << "t = [" << t << "]" << endl;), you'd immediately see why the goto isn't executed. Or step through the code in a debugger and check the value that way. This technique is fundamental to debugging. Commented Apr 30, 2012 at 13:45

7 Answers 7

7

Change

cin >> s >> n >> x >> s;

to

cin >> s >> x >> n >> s;

As you are entering 7.8 as 2nd input but you are collecting it in a integer variable instead of a floating point variable. As a result of this when you enter:

ABC456 7.8 9 XYZ

s gets ABC456, n gets 7 (as it is of int type and the input buffer still has .8 9 XYZ\n in it). Next n gets .8 and finally s gets "9". Now the input buffer has XYZ\n in it. The next time when you read the input into t to get the user's choice, X gets read into t and since it is not y or Y, the loop exits.

5
  • 3
    This is not the answer. OP wants to know why the program exits. He swapped the types intentionally. Commented Apr 30, 2012 at 6:21
  • @shiplu.mokadd.im: Was updating the answer.
    – codaddict
    Commented Apr 30, 2012 at 6:26
  • @codaddict I just understand one things, if inputs are more than variables to store, then the program will exit entirely. But why it is like this?
    – Inside Man
    Commented Apr 30, 2012 at 6:30
  • 1
    @Stranger: You need to understand what the user enters and what gets read in the variables. After each variable gets read, you need to understand what is left in the input buffer.
    – codaddict
    Commented Apr 30, 2012 at 6:34
  • @codaddict so it seems I should erase left buffer after inputs. is there any way to do this?
    – Inside Man
    Commented Apr 30, 2012 at 6:38
3

Enter a debug line after the cin

cout << "s = " << s << "   n = " << n << "   x = " << x;  

And run

Please Type this: ABC456 7.8 9 XYZ
ABC456 7.8 9 XYZ
s = 9   n = 7   x = 0.8

Clearly the first ABC456 is read into the string s. The next was a integer therefore only the 7 is read into n and the 0.8 part was read into float x. Now the next input 9 was assigned to s again therefore the final value of s is the string "9". And now the first character of X gets fed into the next cin where it gets assigned to t. To confirm just insert another debug line cout << "\nt = " << t; after you input t. Therefore the if is false with the value of t assigned to 'X' therefore the program exits.

If you input ABC456 7.8 9 YZ instead the program will ask for input another time, as now t will have 'Y'.

2

Answer https://stackoverflow.com/a/10379322/924727 expalins what happens. About the why, we must go a bit into philosophy.

The C++ stream model is not thought for "human interaction": it is a generic converter of a virtually infinite sequence of characters into a list of space separated "values" to be converted into the supplied "variables".

There is no concept of "input and output interleaving to for a dialog". If you write your input into a text file like myinput.txt (unsing correct input)

ABC456 9 7.8 XYZ
Y
ABC456 5 6.7 XYZ
N

and ron your program from the command prompt like

   myprogram < myinput.txt

your program will run ... and no "pause" can be required to see the output, since no-one is sitting there to see it and answer it.

The program pauses to wait user input not because of cin >>, but because cin is not in fail state and the buffer is empty and the source the buffer remaps is the console. It is the console that waits for '\n' before returning, not cin.

When cin >> n is called...

  • the operator>> function is called, and it ...
  • gets the num_get facet from the stream locale and call its get function that...
  • call the stream buffer sbumpc repeatedly to get the digits and compute the number value.
  • If the buffer has content, it just return its characters one after the other. When no more character are present (or if it is empty) ...
  • The buffer ask the operating system to read from the low level file.
  • If the file is the console, the console internal line editor is invoked:
  • This makes the console to stuck letting the user press character and some controls (like the backspace, for example) until, when Enter is pressed
  • The console line editor returns the line to the operating system that will let the contents available to the input CON file ...
  • That is read by the buffer (after passing the read characters to the cvt locale facet, but this is a detail) that fills up itself.
  • Now do yourself the returns an unrolling.

All this mechanism makes the fact that - if you type more than required - the buffer content remains available to the next >> calls, independently it is or not another program line.

A proper "safer" parse requires, afer an input has been read, the stream state to be cleared and the following content to be ignored up to the next '\n'. This is typically done with

cin.clear(); 
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');

So that, whatever had been typed is discarded, and the next cin>> finds a buffer with no data (just the '\n', that is trimmed as "beginning space"), thus causing the console to go in line edit mode again.

6
  • -1: There is a concept of input and output interleaving interactively, and cin and cout are linked together in just such a way. See basic_istream::tie(). Besides being incorrect, this answer is rambling and unclear. Commented Apr 30, 2012 at 9:01
  • @Potatoswatter: When talking about philosophy everything is "rambling": The point is not to be correnct in Touring sense, but to be correct in the effects. cin and cout the way are used by the OP are not in sync each other. The OP clearly state he wants to know "why" not "what". And to explain "why" you must focus only on the OP selected aspects. I respect your opinion, but my opinion about it is that you have just a prejudice. Commented Apr 30, 2012 at 9:12
  • cin and cout are in sync with each other because any operation on cin automatically flushes cout. (This is missing from your sequence of steps.) You are supporting an incorrect assertion about what the library does not do with invented philosophy unconnected to the library's design intent. A proper philosophical argument would be supported by references, of which you have none. Commented Apr 30, 2012 at 9:28
  • "are in sync with each other because any operation on cin automatically flushes cout". No: this makes cout in sync with cin, but not viceversa: cin is not "flushed" (how??) by cout. Exceeded input remain in cin no matter of cout. That's the whole point. If you don't get it, that can be may fault, only after no-one else gets it. You tell me abut doing false assertion, but you do the same in telling me so. About the "library design intent ..." please, at least qualify yourself as a library designer! Commented Apr 30, 2012 at 9:39
  • 1
    "The answer to the question is that cin enters an error state" No, in this particular case cin is not errored. The data are distributed differently as expected, letting a "tail" available to the next input, that is not flushed by existence of an intemediate output, and that is considered a valid answer of value "No". I don't want to enter to the reason that made the design as such (anyway, your contribution in the implementation is not "design", the spec already existed), but that's the point. Commented Apr 30, 2012 at 11:56
2

When the stream extraction operator >> encounters invalid input, it puts the stream into a mode where no more input is extracted.

You can detect this mode using a Boolean test such as if ( cin ), and reset it using cin.clear(). After doing so, the invalid input remains in cin's input buffer, so you need to process it somehow, such as by ignore.

Better yet, the extraction operator returns cin so you can test while you extract:

if ( ! ( cin >> s >> n >> x >> s ) ) {
    cout << "You fail horribly!\n";
    cin.clear();
    cin.ignore( std::numeric_limits< std::streamsize >::max(), '\n' );
}

For more depth see Semantics of flags on basic_ios (and I'm sure there are a few questions on this site that are exact duplicates of this).

1

The program hits a problem or exception when it tries to but the wrong data types together. Perhaps you want to look at the documentation for the >> operator on cin, look for specifics on what it does when it hits wrong input for data types, and look over the cin>> line and your input for any places that this might be occurring so you can validate the input is handled correctly

1

when you call cin.clear(), you should call cin.sync() as the same time.

5
  • Why? sync has no standardized behavior on an input stream. Commented Apr 30, 2012 at 7:55
  • using cin.sync() before entering t will solve the problem, thanks.
    – Inside Man
    Commented Apr 30, 2012 at 7:56
  • 1
    Not in general. sync on an input stream has implementation defined behavior. It's expect it generally to be a no-op. Commented Apr 30, 2012 at 8:11
  • So I will wait for another answer, if no answer then this answer will be accepted. So What will be the general trick to avoid this issue?
    – Inside Man
    Commented Apr 30, 2012 at 8:14
  • 1
    sync will clear the input buffer, call this function will clear the wrong input to avoid read it again. Commented Apr 30, 2012 at 13:32
1

Once the stream detects an error, it is in an error state, and all further attempts to input will be no-ops. Accessing variables read by a stream without first testing whether the read succeeded is undefined behavior, so in theory at least, you're program could do anything. (In practice, if the uninitialized variable is of type char, all you risk is a random value.)

When reading line oriented input, the best solution is to use std::getline. then use the string that was input to construct an std::istringstream to parse the line. This leaves the input stream in a good state, and already synchronized for the next input. I'd use something like:

void
getInput()
{
    std::string line;
    std::cout << "Please type this: ABC456 7.8 9 XYZ" << std::endl;
    std::getline( std::cin, line );
    if ( ! std::cin ) {
        //  Very unexpected error... 
        throw SomethingSerious();
    }
    std::string s;
    int n;
    float f;
    std::istringstream toParse( line );
    toParse >> s >> f >> n >> s;
    if ( ! toParse ) {
        //  Input incorrect...
    }
}

bool
tryAgain()
{
    std::cout << "Do you want to try again (y/n)? ";
    std::string line;
    std::getline( std::cin, line );
    return line.size() == 1 && (line[0] == 'y' || line[0] == 'Y');
        //  But I'd be more tolerant with regards to spaces...
}

bool
oneInput()
{
    getInput();
    return tryAgain();
}

int
main()
{
    while ( oneInput() ) {
    }
    return 0;
}
2
  • Thanks For reply, but it is a long trick! BTW Thanks again, it can help too ;)
    – Inside Man
    Commented Apr 30, 2012 at 8:15
  • 1
    @Stranger Handling input correctly usually is fairly complex, since you have to be able to handle anything the user inputs. And users can be very unpredictable in what they enter. Commented Apr 30, 2012 at 8:32

Not the answer you're looking for? Browse other questions tagged or ask your own question.