Skip to content

Simple Input Bullet-Proofing In C++

September 13, 2011

Introduction

One of the first programs that most learner C++ programmers write looks something like this:

#include <iostream>
using namespace std;

int main() {
  int a, b;
  cout << "enter a: ";
  cin >> a;
  cout << "enter b: ";
  cin >> b;
  cout << "a * b = " << a * b << endl;
}

This reads two integers from standard input, multiplies them, and prints out the result. What could possibly go wrong?

Well, if the user does what they are told, not much – the dialog with the program looks like this:

enter a: 10
enter b: 3
a * b = 30

However, suppose instead of typing in the number 10, they enter the string "foo". Now the dialog looks like this:

enter a: foo
enter b: a * b = 0

The program didn’t report that "foo" was invalid, didn’t even try to read b, and then printed out a dubious result, zero in this particular case, but in fact the result is undefined as far as the C++ language is concerned. Things are not much better if you enter the correct number for a and a string for b, or if you type two integers on the same line.

The problem is that once you execute a statement like this:

cin >> a;

and provide an input that the C++ input library cannot convert into whatever type a is, such as "foo" when a is an int, then the input stream is flagged as being in a failed state, and all further operations on the stream will also fail, even if you provide good data. This article, which is aimed at beginning C++ programmers, looks at some ways to get around this problem.

Input Stream Flags

Each C++ input stream instance (of which cin is an example) maintains a number of status flags which indicate the state of the stream. The one you need to worry about is the fail flag. This is set to true when a read operation on the stream fails, usually because bad input data was provided.

The fail flag can be tested using the fail() member function:

if ( cin.fail() ) {
   // stream is in failed state
}

It can also be tested (along with certain other flags) at the point that the read operation takes place in this more idiomatic manner:

if ( cin >> a ) {
  // everything is fine
}
else {
  // stream is bad
}

Once a stream gets into a failed state, it stays in that state until the programmer explicitly clears the fail flag, which can be done with the clear() method. So your first attempt at writing bullet-proof input might look like this:

#include <iostream>
using namespace std;

int main() {
    int a, b;
    cout << "enter a: ";
    while( ! ( cin >> a ) ) {
        cout << "invalid input!\n";
        cin.clear();
    }
    cout << "enter b: ";
    cin >> b;
    cout << "a * b = " << a * b << endl;
}

 

Now the dialog with the user looks like this:

enter a: foo
invalid input!
invalid input!
invalid input!
invalid input!

Unfortunately, clearing the fail flag does not remove the thing that caused the failure from the input buffer, so the next time round the while-loop, the read operation tries to read "foo" again, and fails again. This continues ad infinitum. You need to remove the old input before retrying the read operation.

A lot of bizarre operations have been suggested to allow removal of existing input from an input stream. I would just like to point out one that is definitely illegal – flushing the stream. Flushing input streams is undefined by both the C and C++ Standards,, and you should never try to do it, even if it appears to "work" for your particular C or C++ installation.

In fact, the C++ Standard Library provides a function called ignore() which discards data currently in the input buffer. Unfortunately, this is not one of the best-designed functions in the C++ Library. It would be nice if you could just discard everything in the buffer like this:

cin.ignore();

but this only discards a single character. To discard more than one character, you have to tell ignore() a maximum number of characters and the character at which it should stop ignoring (which in this case is a newline). But of course, you don’t know how many characters are in the input stream….

The solution is to give ignore() a very big number of characters. Now, you could just use a number like 9999999, and that will probably work, but it takes just a little more effort to write something that will definitely work – it is a bit scary, and looks like this:

cin.ignore( numeric_limits<int>::max(), '\n' );

You will also have to #include the <limits> header file. The numeric_limits class is a template that is specialised for all the built-in types for your specific C++ implementation, and the max() function is specialised to return the biggest possible value. So this code gives us the biggest possible value for an int.

If you want to wimp out at this point, I couldn’t blame you – this operation  should be much easier to do than it is, but such is the world of library design – the designers rarely bother to provide convenience functions; they just make sure that the functionality is available.

So your new code looks, in full, like this:

#include <iostream>
#include <limits>
using namespace std;

int main() {
    int a, b;
    cout << "enter a: ";
    while( ! ( cin >> a ) ) {
        cout << "invalid input!\n";
        cin.clear();
        cin.ignore( numeric_limits<int>::max(), '\n' );
    }
    cout << "enter b: ";
    cin >> b;
    cout << "a * b = " << a * b << endl;
}

Now the dialog with the program looks so:

enter a: foo
invalid input!
10
enter b: 3
a * b = 30

Hurrah! Now you need to do the same thing for the input to the b variable.

Well, no you don’t. You would end up writing exactly the same code all over again, except that you would be inputting into b rather than a. Whenever you find yourself in the situation that you are about to duplicate some code; stop! – you need to write a function.

An Input Function

Before turning the loop you have  already written into a function, it’s a good idea to sit back and think a little. What should the function return? Could you make it prompt the user each time through the loop? I asked myself these questions and came up with this:

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

int ReadInt( const string & prompt  ) {
    int n;
    while( (cout << prompt) && ! (cin>> n) ) {
        cout << "invalid input!\n";
        cin.clear();
        cin.ignore( numeric_limits<int>::max(), '\n' );
    }
    return n;
}

int main() {
    int a, b;
    a = ReadInt( "enter a: " );
    b = ReadInt( "enter b: " );
    cout << "a * b = " << a * b << endl;
}

With this function in place, the interaction with the program looks like this:

enter a: foo
invalid input!
enter a: 10
enter b: 3
a * b = 30

Much nicer, and it seems like you are done. But consider this interaction:

enter a: 10 3
enter b: a * b = 30

I entered two numbers on the same line, and they were both read correctly. However, the interaction looks messy and confusing, and the user probably didn’t mean to enter the second number. For interactive use, it makes sense that once you have successfully read an input from a line, you discard the remaining input. Can you see how to do this – hint: you will need another call to ignore().

A Template Function

Suppose you decide that you want your program to perform real arithmetic instead of being limited to integers. You will have to use doubles or floats, and you will (it seems) have to write new ReadFloat and ReadDouble functions to read them correctly. However, if you consider what these functions will look like, they will all be exactly the same apart from the type of the variable n that you perform input into and return the value of.

When you find yourself in the situation where you have two functions who’s implementation is identical, differing only in the data types involved, you should think templates!  The template version of the function is very easy to write:

template <typename T>
T ReadType( const string & prompt  ) {
    T n;
    while( (cout << prompt) && ! (cin>> n) ) {
        cout << "invalid input!\n";
        cin.clear();
        cin.ignore( numeric_limits<int>::max(), '\n' );
    }
    return n;
}

You can see that it is almost identical to the original function, except that where there was an int, there is now a T, which is the template parameter specifying a type, such as int, float, double etc. I have also changed the function name to something more generic.)

In use:

int main() {
    int a, b;
    a = ReadType<int>( "enter a: " );
    b = ReadType<int>( "enter b: " );
    cout << "a * b = " << a * b << endl;
}

Or if you want to do double arithmetic:

int main() {
    double a, b;
    a = ReadType<double>( "enter a: " );
    b = ReadType<double>( "enter b: " );
    cout << "a * b = " << a * b << endl;
}

 

A Header File

The  function will probably be extremely useful in a number of programs, so it makes sense to make it a header file, that can be included whenever you need to use it. The full contents of the header, which should be called readtype.h, should look like this:

#ifndef INC_READTYPE_H
#define INC_READTYPE_H

#include <iostream>
#include <string>
#include <limits>

template <typename T>
T ReadType( const std::string & prompt  ) {
    T n;
    while( (std::cout << prompt) && ! (std::cin>> n) ) {
        std::cout << "invalid input!\n";
        std::cin.clear();
        std::cin.ignore( std::numeric_limits<int>::max(), '\n' );
    }
    return n;
}

#endif

 

There are a few things to notice here. Firstly, the code is wrapped in include guards, which prevent it being included more than once in any other source file. Secondly, all the headers it needs are explicitly included, so the user does not have to know about them. And thirdly, there is no using namespace std, instead all the Standard Library names are explicitly qualified with the std:: namespace – this avoids problems with namespace clashes and pollution.

Using the header is simple – you just include it:

#include <iostream>
#include "readtype.h"
using namespace std;

int main() {
    int a, b;
    a = ReadType<int>( "enter a: " );
    b = ReadType<int>( "enter b: " );
    cout << "a * b = " << a * b << endl;
}

Note that you should still include the iostream header, as you are not supposed to know that readtype.h includes it for you. Also, you need to use double-quotes around the file name, as the angle brackets are (notionally at least) reserved for including library headers.

Getting Functional

If you come from a functional background, you might be getting excited at this point!  Why not get rid of the variables a and b and simply write this:

cout << "a * b = " 
  <<  ReadType<int>( "enter a: " ) *  ReadType<int>( "enter b: " ) 
  << endl;

Well, this almost works. Unfortunately though, C++ when given an expression like X * Y gives no guarantee about whether X or Y is evaluated first. In the context of your program this means that the interaction with the program could be that you are prompted to input a first, then b, or to input b first, then a, depending on your C++ compiler implementation. It is thus best to input into named variables, in the explicit order you want them to be read.

Conclusion

This article has presented a simple function for reading numeric data into small, beginner-style C++ programs. It is not intended to be industrial-strength code, but simply to be a mechanism for slightly improving the quality of such programs, and as a means of teaching beginners a little about the iostream library, code decomposition and template functions.

Advertisements

From → c++, tutorial

7 Comments
  1. I once made myself this quite similar function: https://gist.github.com/1213875

    It’s a bit difficult for a newbie to use and looks ugly too, but it allows you to have customized stuff when invalid stuff is inputted. C++11 would make it even better, but that is without lambdas.

  2. S.Recker permalink

    When I typed up this example in Visual Studio 2008 Express the output window closed immediately, even with a cin.get(); statement right before the end of main.cpp. If I put two cin.get() statements the window would stay open. Is there still something (newline ? ) still in the input stream that is being passed to the first cin.get()?

  3. S.Recker permalink

    Follow up to my last post.

    I found if I added to the header file the following line:

    std::cin.ingnore();

    right before returning n, then a single cin.get() would keep my output window open. So it seems to me that there is still a newline in the input stream.

    Is this correct, because honestly I am not 100% sure what is going on.

  4. In VS, run the code using Start Without Debugging (Ctrl-F5)

  5. The first argument to ignore gets converted to a streamsize, which is only guaranteed to be one of the basic integer types and might not have the same numeric limits as int. So the call to ignore might not always empty the line.

  6. Jehjoa permalink

    In the template function you use numeric_limits::max(), shouldn’t that now be numeric_limits::max()?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: