tags:

views:

244

answers:

4

I have a Win32 console program that I wrote and it works fine. The program takes input from the user and performs some calculations and displays the output - standard stuff. For fun, I am trying to get the program to work on my Fedora box but I am running into an issue with clearing cin when the user inputs something that does not match my variable type. Here is the code in question:

void CParameter::setPrincipal() {
double principal = 0.0;

cout << endl << "Please enter the loan principal: ";
cin >> principal;

while(principal <= 0)
{
    if (cin.fail())
    {
          cin.clear();
          cin.ignore(INT_MAX, '\n');
    }
    else
    {
        cout << endl << "Plese enter a number greater than zero. Please try again." << endl;
        cin >> principal;
    }
}

m_Parameter = principal;

}

This code works in Windows. For example, if the user tries to enter a char data type (versus double) then the program informs the user of the error, resets cin, and allows the user another opportunity to enter a valid value.

When I move this code to Fedora, it compiles fine. When I run the program and enter an invalid data type, the while loop never breaks to allow the user to change the input.

My questions are; how do I clear cin when invalid data is inputted in the Fedora environment? Also, how should I write this code so it will work in both environments (Windows & Linux)?

Thanks in advance for your help!

+1  A: 

I think that cin.ignore sets the failed flag on cin, which makes it stay in the upmost if statement for ever. INT_MAX is a very large number - are you sure it is allowed to cin.ignore on all platforms?

Anders Abel
Good point, i think. `std::numeric_limits<std::streamsize>::max()` might be a better choice.
Johannes Schaub - litb
+1  A: 

I'd switch to using a getline to read the input and then parse with a stringstream:

double principal = 0;
string temp;
while (principal <= 0)
{
    getline(cin, temp);
    istringstream converter(temp);
    if (!(converter>>principal) ||
        !(converter>>std::ws).eof() ||
        principal <= 0)
    {
        cout << "Try again" << endl;
        principal = 0;
    }
}
tzaman
+1  A: 

I think it's a bad idea to use formatted input to read user responses. I'd use getline - something like this:

#include <iostream>
#include <string>
#include <sstream>
using namespace std;

template <typename T>
bool Read( T & t, istream & is ) {
    string s;
    if ( ! getline( is, s ) ) {
        return false;
    }
    else {
        istringstream ss( s );
        return ss >> t;
    }
}    

int main() {
    while(1) {
        double d;
        if ( ! Read( d, cin ) ) {
            cout << "invalid\n";
        }
        else {
            cout << "You entered " << d << "\n";
        }
    }
}

which works on Windows - I don't have my Linux box switched on at the moment.

anon
A: 

I agree with Anders Abel and Johannes Schaub; I think also that it may not be guaranteed that, in case of error, principal is left untouched, so you may consider adding principal=0.0; at the beginning of the cycle.

By the way, to perform that kind of work I usually use this template function:

template<typename InType> void AcquireInput(std::ostream & Os, std::istream & Is, const std::string & Prompt, const std::string & FailString, InType & Result)
{
    do
    {
        Os<<Prompt.c_str();
        if(Is.fail())
        {
            Is.clear();
            Is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        }
        Is>>Result;
        if(Is.fail())
            Os<<FailString.c_str();
    } while(Is.fail());
}

template<typename InType> InType AcquireInput(std::ostream & Os, std::istream & Is, const std::string & Prompt, const std::string & FailString)
{
    InType temp;
    AcquireInput(Os,Is,Prompt,FailString,temp);
    return temp;
}

The first overload may be preferred if you want to avoid copying, the second may be more convenient for builtin types. Usage examples:

//1st overload
double aDouble;
AcquireInput(cout,cin,"Please insert an integer: ","Invalid value.\n",aDouble);

//2nd overload (more convenient, in this case and in general with POD)
double aDouble=AcquireInput(cout,cin, "Please insert an integer: ","Invalid value.\n");

In your case you may change your code in this way:

double principal=0.0;
const char * errMsg="Plese enter a number greater than zero. Please try again.\n";
while(principal<=0.0)
{
    principal=0.0;
    principal=AcquireInput(cout,cin,"Please enter the loan principal: ",errMsg);
    if(principal<=0.0)
        cout<<errMsg;
}
Matteo Italia