2

This program prints a specified amount of numbers within a specified range. However, when I enter a character, it just endlessly loops whichever do-while loop I do it in. E.g: If I enter a character in "Enter maximum number" cin, it just spams "Enter maximum number" endlessly, it just skips the cin and loops the cout (same goes for the other 2 do-whiles. Anyone know why?

#include <iostream>
#include <cmath>
#include <cstdlib>
#include <ctime>

using namespace std;

int roll(int mini, int maxi)
{
        int v = maxi - mini;
        int x  = mini + (rand() % (v+1));
        return x;

}
void caller()
{
    int a;
    int b;
    int c;

    do {
    cout << "Enter minimum number" << endl;
    cin.clear();
    cin >> a;
    } while (cin.fail());

    do {
    cout << "Enter maximum number" << endl;
    cin.clear();
    cin >> b;
    } while (cin.fail() || a > b);

    do {
    cout << "How many rolls?" << endl;
    cin.clear();
    cin >> c;
    } while (cin.fail());

    for (int i = 0; i < c; i++)
    cout << roll(a, b) << endl;
}

int main()
{
    srand (time(NULL));
    caller();
    return 0;
}
Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
Seb
  • 1,966
  • 2
  • 17
  • 32
  • You never actually extract the character. It just sits there for next time you try to read something. – chris Feb 16 '13 at 07:21
  • I'm still a beginner, what do you mean by extract? And how can I fix this? – Seb Feb 16 '13 at 07:23
  • 1
    Since you didn't actually read it, it's still in the stream, so next time you try to read anything, it will be the first thing read, which causes the problem again. Use `ignore` or something to discard the unreadable (at least for an `int`) characters. – chris Feb 16 '13 at 07:28
  • Ignore only helps if I put a character once. If I do it a 2nd time, it just does the loop again. – Seb Feb 16 '13 at 07:35
  • 1
    Hint: Ignore more than one character. In fact, for keyboard input, just ignore all characters currently in the stream. – us2012 Feb 16 '13 at 07:38
  • How do I do that? I'm still a newbie – Seb Feb 16 '13 at 07:39

2 Answers2

2

I prefer not to use istream::fail() for loop control. See Why is iostream::eof inside a loop condition considered wrong? for a similar issue.

Instead, I rely upon the return value of istream::operator >>.

I also use the following function to reset the flags and clear the input on input stream:

void flush_stream(std::istream& stream)
{
    stream.clear();
    stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

See How do I flush the cin buffer? for more on this.

So I'd code your input checks thus:

int get_valid_number(const std::string& prompt)
{
    int number = 0;

    bool valid = false;
    while (!valid)
    {
        std::cout << prompt << std::endl;
        if (std::cin >> number)
        {
            valid = true;
        }
        flush_stream(std::cin);
    }

    return number;
}

Hopefully the benefits of extracting this into a function are obvious. See it run.

Community
  • 1
  • 1
johnsyweb
  • 136,902
  • 23
  • 188
  • 247
  • While I do prefer this way, `operator bool()` and `operator void *()` use `fail()` to determine the return value. – chris Feb 16 '13 at 16:41
2

You could try doing something like

int a;
string trash;

do {
  cout << "Enter minimum number" << endl;
  cin >> a;

  if(cin.fail()){
    cin.clear();
    cin >> trash;
  }

} while (cin.fail());

This will remove any bad input from the cin stream by throwing it into the trash string.

This link may help you understand these cin functions a bit better.

http://web.eecs.utk.edu/~cs102/lectures/cinLecture.html

Alex
  • 671
  • 3
  • 11
  • 25