Michael Francis
Michael Francis

Reputation: 8747

std::cin sometimes hangs, sometimes repeats forever after invalid input

In this case, when the line marked hang point receives invalid input, eg. abc, it repeats the inner do-while loop and hangs upon reaching that line again.

// figure 1
std::string s;
do {
    do {
        std::cout<<"s = ";
        std::getline(std::cin, s);
    } while (s == "");
    double d;
    std::cout<<"d = ";
    do {
        std::cin.clear();
        std::cin>>d; // hang point
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    } while (std::cin.fail());
} while (s != "exit");

In this case the program skips over that line and repeats the do-while loop forever.

// figure 2
double d;
do {
    std::cout<<"d = ";
    std::cin.clear();
    std::cin>>d; // skip point
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
} while (std::cin.fail());

Why?

Originally I was going to ask why figure 1 hangs, and in the process of trying to simplify the example I came up with figure 2, and found that it didn't hang, but repeated forever. I don't understand why either figure 1 or figure 2 do what they do.

Edit: I've been causing cin to fail by entering alphabetical data at the prompt that I wrote for myself. eg.

s = something
d = 3
s = something
d = a

Upvotes: 0

Views: 1226

Answers (2)

Jimmy
Jimmy

Reputation: 445

My impression is that you're resetting the flags, but the cin buffer is still full. So when it re-enters the loop, 'd' is automatically reassigned the letter value (say, the 'abc' you entered at the terminal) automatically. Thus the fail flag will then be reset, and the cycle repeats all over again.

Here's the fixed version (with a few stylistic changes, sorry about that):

#include <string>
#include <iostream>

int main(){
    std::string s="";
    std::string dummy="";
    while ( s!="exit"){
        s="";
        while (s==""){
            std::cout<<"s = ";
            std::getline(std::cin, s);
        }
        double d;
        std::cout<<"d = ";
        do {
            if (std::cin.fail()){
                std::cin.clear();
                std::getline(std::cin, dummy);
                std::cout << "d = ";
            }
            std::cin>>d;
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        } while (std::cin.fail());
    }
}

Upvotes: 1

TheOneWhoSighs
TheOneWhoSighs

Reputation: 154

Figure2: You have to call cin.clear() before cin.ignore(). Otherwise, cin.ignore won't do anything because cin is still in an error state.

double d;
do {
    if (std::cin.fail()) {
        std::cin.clear();
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }
    std::cout<<"d = ";
    std::cin>>d;
} while (std::cin.fail());

Upvotes: 2

Related Questions