HomeC&C++WainTutorialsSamplesTip & TrickTools


Every second introduction, tutorial or book on C++ has a program similar to this:
   std::cout << "Enter first number: ";
   int a;
   std::cin >> a;
   std::cout << "Enter second number: ";
   int b;
   std::cin >> b;
   std::cout << a << " +  " << b << " = " << a + b << std::endl;
This looks fine, and works fine, if the user do as told, but if he didn't read the instruction, he might think that he was asked to enter his name, and then we are in trouble. He might enter John to the first question, and then the program does not even bother to wait for him to enter the next number. The problem is that the text John got stuck in the stream, and there is no propper way to get it out.
To make the program robust to user errors we create a template function:
template <typename T>
bool Get(T &t, std::istream &is = std::cin)
{
   std::string s;
   std::getline(is, s);
   std::stringstream ss(s);
   return ss >> t;
}
First we read a std::string from the stream, which by deault is std::cin. Then a std::stringstream is created with a copy of the string. Then the string is converted to to the desired type, and the result of the conversion, true if the conversion was successfulll, false otherwise, is returned.
We use getline to read the string, as the >> operator stops scanning when the user enter a space, which could leave pending input in the input stream. The advantage of this method is that if the user enters a value which can not be converted, it will be ignored and not stick in the original stream.
To use it:
   std::cout << "Enter first number: ";
   int m;
   Get(m);
Or if you will let the user try again until he succeds:
   int n;
   do
   {
      std::cout << "Enter a number: ";
   }
   while(!Get(n));
You can use Get to read any type of number, short, int, long, unsigned, signed, float, double, etc.
You have to include sstream to use std::stringstream.

The template function has one flaw; if the user enters "123pop" when asked for a integer, the function will convert it to 123 and return true. This is fine for some purposes, buf might be fatal in other cases.
We can use the std::istream::eof function to see if there is more input at the end of the stream:
template <typename T>
bool Get(T &t, std::istream &is = std::cin)
{
   std::string s;
   std::getline(is, s);
   std::stringstream ss(s);
   if(ss >> t)
   {
      if(!ss.eof())
      {
         std::string Temp;
         std::getline(ss, Temp);
         std::cerr << "Pending input: " << Temp << std::endl;
         return false;
      }
      return true;
   }
   return false;
}
In this example the function will write to std::cout if there is text in the stream after the convertion, so if the user enters "123pop" when asked for input it will write "Pending input: pop", and return false to indicate that the entered string was not a valid number.
Normally one will not expect a function as this to write output like that, so we will change it a bit to make it hand the part of the input which could not be converted back to the caller:
template <typename T>
bool Get(T &t, std::istream &is = std::cin, std::string *Rest=0)
{
   std::string s;
   std::getline(is, s);
   std::stringstream ss(s);
   if(ss >> t)
   {
      if(ss.eof())
         return true;
      if(Rest)
      {
         std::getline(ss, *Rest);
      }
      return false;
   }
   return false;
}
The function will put the part of the input string which could not be converted into the optional third argument, if present.
You can use the function as:
   int n;
   Get(n);
Or, if you want to give detailed user feedback:
   bool Result;
   int m;
   std::cout << "Enter a number: ";
   do
   {
      std::string Rest;
      if((Result = Get(m, std::cin, &Rest)) != true)
      {
         std::cout << "Failed to convert: " << Rest << std::endl;
         std::cout << "Try again: ";
      }
   }
   while(!Result);
In any aspect this function is better than the >> operator, except for performance.

Does this not work for you? Read on.
The function might not work correct on all compileres, due to "differences" between compilers.
This small program demonstrates the problem:
#include <iostream>
#include <string>

int main()
{
   std::string S;
   std::getline(std::cin, S);
   std::cout << "You Wrote: " << S << std::endl;
   std::cout << "Size: " << S.size() << std::endl;
}
On VisualC++ 6.0 you have to hit the enter key twice, and one of these keys are appended to the string. This is a known problem.
If you enter 123 and hits enter on BorlandC++ it will claim that the size is 4, the last character i 0, it seems to count the 0-terminator as a part of the string.
One solution to this is to use basic_istream::get(...):
int main()
{
   char Line[128];
   std::cin.get(Line, sizeof(Line));
   if(!std::cin)
   {
      std::cin.clear();
   }
   std::cin.get();
   std::cout << "You Wrote: " << Line << std::endl;
   std::cout << strlen(Line) << std::endl;
}
The first get() will read the string, and stop when it meets a newline, the second get() reads this newline, to make the stream ready to be read again.
There is one more catch, if the user does not write anything but hits enter, get will set the failbit, for the stream (cin), if(!std::cin) check if the flag is set, if it is we will clear it.
The disadvantage of this solution is that we have to use a char array, which by definition has a fixed size.
Then a fixed Get function, which works on the four compilers I have tried it with:
template <typename T>
bool Get(T &t, std::istream &is = std::cin, std::string *Rest=0)
{
   char Line[128];
   is.get(Line, sizeof(Line));
   if(!is)
   {
      is.clear();
      is.get();
      return false;
   }
   is.get();
   std::stringstream ss(Line);
   if(ss >> t)
   {
      if(ss.eof())
         return true;
   }
   ss.clear();
   if(Rest)
   {
      std::getline(ss, *Rest);
   }
   return false;
}
If the user hits enter without typing anything, we clear the error flag and returns false.
Note that I have made a small improvement; if noting of what the user wrote could be converted it will put everything into Rest, if pressent.