HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Forcing user to enter an integer

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
forcingenterintegeruser

Problem

I am new to C++ and would like to know the most efficient way of forcing a user to enter an integer. Here is my function that I have created. Please show me the best way and explain why it's better than this.

int getInt(){
    int x;
    while (true){
        cout > x;
        if (!cin.fail()){
            return x;
        }
    }
}

Solution

I don't like while loops without a body. They are hard to read.

while(cin.get() != '\n');

// Even if the body is just a comment.
while(cin.get() != '\n')
{  /* Empty Body */ }


But what you are doing is basically ignoring the current line. It would be easier to do this with ignore()

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


But now that we have looked at it I don't really think you want to ignore the current line of input. Getting input screwed up (especially when working with interactive user input) is that you are mixing std::getline and operator>>. User input is line based so always read a line of input from the user then parse the input. Then you will not get screwed up by unread end of line markers.

Looping while(true) is dangerous when working with streams. Things have a tendency of becoming infinite. If your stream becomes bad (ie bad bit is set), then you will go into infinite loop. Clearing the bad bit and retrying is unlikely to make it better.

So I would re-write like this:

How about:

int getInt(std::istream& stream)
{
    // Only loop as long as the input is stream is usable.
    // Otherwise you enter an infinite loop.
    while (stream)
    {
        cout > value)) {
            continue;   // The input was not an integer.
        }               // So retry the loop.

        // You got a good value.
        // Do you care if there is stuff on the end of the line?
        // i.e.  If the user entered 56 f is that bad?
        //       If it is then you check to see if there is more valid input on the line.
        char  x;

        if (linestream >> x) {
            continue;    // There was good input (thus the input was bad).
        }
        return value;
    }
    // If you get here,
    // this means you ran out of input.
    // What do you do?
    // You could return a default value.
    // or throw an exception.
    return 0;

    // The trouble with either of these methods is that its hard to detect
    // the error situation. You need to explicitly check. That is why `operator>>`
    // works well. The stream goes bad when things get out of sync and the natural
    // operations cause the current operation to fail.
}


So an alternative to getInt() would be to extend operator>> for reading a line and getting an integer out of it.

struct GetInt
 {
     int&  val;
     GetInt(int& v): val(v) {}

     friend std::istream& operator>>(std::istream& str, GetInt& data)
     {
         data.val  = getInt(str);
         return str;
     }
 };


Now you can use your line getting in a normal stream like operation.

int val;
 if (std::cin >> GetInt(val))
 {
     // We correctly retrieved a value from the user.
 }

Code Snippets

while(cin.get() != '\n');

// Even if the body is just a comment.
while(cin.get() != '\n')
{  /* Empty Body */ }
std::cin.ignore(numeric_limits<streamsize>::max(), '\n');
int getInt(std::istream& stream)
{
    // Only loop as long as the input is stream is usable.
    // Otherwise you enter an infinite loop.
    while (stream)
    {
        cout << "Enter an int." << endl;

        // Read a line of user input.
        std::string line;
        std::getline(stream, line);

        // Now convert user input to value.
        std::stringstream linestream(line);
        int               value;

        if (!(linestream >> value)) {
            continue;   // The input was not an integer.
        }               // So retry the loop.

        // You got a good value.
        // Do you care if there is stuff on the end of the line?
        // i.e.  If the user entered 56 f<enter> is that bad?
        //       If it is then you check to see if there is more valid input on the line.
        char  x;

        if (linestream >> x) {
            continue;    // There was good input (thus the input was bad).
        }
        return value;
    }
    // If you get here,
    // this means you ran out of input.
    // What do you do?
    // You could return a default value.
    // or throw an exception.
    return 0;

    // The trouble with either of these methods is that its hard to detect
    // the error situation. You need to explicitly check. That is why `operator>>`
    // works well. The stream goes bad when things get out of sync and the natural
    // operations cause the current operation to fail.
}
struct GetInt
 {
     int&  val;
     GetInt(int& v): val(v) {}

     friend std::istream& operator>>(std::istream& str, GetInt& data)
     {
         data.val  = getInt(str);
         return str;
     }
 };
int val;
 if (std::cin >> GetInt(val))
 {
     // We correctly retrieved a value from the user.
 }

Context

StackExchange Code Review Q#39076, answer score: 3

Revisions (0)

No revisions yet.