View Single Post
Old 10-17-2004, 09:02 AM   #19 (permalink)
Valmont
[code][/code] enforcer
 
Valmont's Avatar
 
Join Date: Mar 2003
Location: Netherlands
Posts: 1,544
Valmont is on a distinguished road
After cin>>|anyNUMBER|; the EOF ('n') is left in the stream. So the next call with getline() eats the newline (newline character is by default the EOF for getline()!) and skips the input.

Solution:
Remove the newline before the getline() method is called again.

See my code for the basics. Then see comments after that.
Code:
#include <iostream>
#include <string>
#include <climits>

using namespace std;

//Optional functions: depends on IDE.
void wait_for_enter();

//Additional helper.
void reset_istream( );

//--
const unsigned RACERS = 3;

//The model. Our world.
struct Racer
{
   string sName;
   unsigned nMinutes;
};

int main(int argc, char *argv[])
{
   Racer theRacers[RACERS];
   for( unsigned i = 0; i < RACERS; ++i )
   {
      cout << "Enter the name of racer #" << i+1 << ": ";
      getline( cin, theRacers[i].sName, '\n' );
      cout << "Enter the finish time in minutes of racer #" << i+1 << ": ";
      cin >> theRacers[i].nMinutes;
      reset_istream();
   }
   
   for( unsigned i = 0; i < RACERS; ++i )
      cout<<theRacers[i].sName<<" : "<<theRacers[i].nMinutes<<endl;

   wait_for_enter();
   return 0;
}

//--------------------------------------------------

void reset_istream( )
{
   //Reset failstate just in case.
  cin.clear();
   cin.ignore(numeric_limits<streamsize>::max(), '\n');
}

//---------------------------------------------------

void wait_for_enter()
{
  cout << "press <enter> to continue...\n";
  // Reset failstate, just in case.
  cin.clear();
  string line;
  getline( cin, line);
}
Good.
Now we found out about the basics of emptying the buffer. Also notice that my reset_istream() is programmed differently then then I usually do (see other posts). This method is faster, and proven to be correct in more situations. Both work though. Just a nice heads up. Remember the way I clear the stream well. It is going to be handy in many occasions.

Next thing.
Our input method is basic. A bit too basic for two reasons. Here are the reasons in order of importance:
1) Correctness.
2) Expectations.

Ad 1:
With the std::string, not many things could go wrong. But with the unsgined integer, I could enter a letter or a word or even a sentence for it by accident. The result is undefined then. We have to solve this.

Ad 2:
Once we solved the integer issue, we would like to force the user to really enter a name, and not an empty string (just press enter without entering anything).

I leave one excersise for you though:
Make it also correct when this happens: I enter numbers instead of a words/letters for the name. Don't accept numbers or whatever for my name.

Correctness
The result of entering wrong input into a number variable results in an infinite loop wich is well obvservable in a console application. In that case, the stream enters in a fail state. So one of the things we need to do is resetting the stream state to its initial state: cin.clear();.
The second thing under the hood, is that with the erratic behaviour (infinite loop) an eof is generated. We need to get rid of that end of file character as well: cin.ignore(numeric_limits<streamsize>::max(), '\n');. You will need to include <climits> to make this happen though. What it does is removing everything including the terminating character (eof in our case). It eats up the stream up to the very end.

So here is the complete program where we solved that problem:
Code:
#include <iostream>
#include <string>
#include <climits>

using namespace std;

//Optional functions: depends on IDE.
void wait_for_enter();

//Additional helper.
void reset_istream( );

//The model. Our world.
struct Racer
{
   string sName;
   unsigned nMinutes;
};

//--
const unsigned RACERS = 3;
Racer theRacers[RACERS];

//Core functions.
void menu();
void console_output();

int main(int argc, char *argv[])
{
   menu();
   console_output();
   
   wait_for_enter();
   return 0;
}

//--------------------------------------------------

void menu()
{
   for( unsigned i = 0; i < RACERS; ++i )
   {
      cout << "Enter the name of racer #" << i+1 << ": ";
      getline( cin, theRacers[i].sName);
      cout << "Enter the finish time (minutes) of racer #" << i+1 << ": ";
      while( !(cin >> theRacers[i].nMinutes) )
      {
         cout<<"ERROR! Reason: could not find valid input.\n";
         cout<<"RETRY (finish time in minutes): ";
         reset_istream();
      }
      reset_istream();
   }
}

//--------------------------------------------------

void console_output()
{
   for( unsigned i = 0; i < RACERS; ++i )
   {
      cout<<theRacers[i].sName<<" : "<<theRacers[i].nMinutes<<endl;
   }
}

//--------------------------------------------------

void reset_istream( )
{
   if(cin.eof())
   {
      cin.clear();
   }
   else
   {
      cin.clear();
      cin.ignore(numeric_limits<streamsize>::max(), '\n');
   }
}

//---------------------------------------------------

void wait_for_enter()
{
  cout << "press <enter> to continue...\n";
  // Reset failstate, just in case.
  cin.clear();
  string line;
  getline( cin, line);
}
Expectations
*** to be edited by me in a moment. I want some cappucino first :) ***
__________________

Last edited by Valmont; 10-17-2004 at 10:54 AM.
Valmont is offline   Reply With Quote