Hello,

I have this on going project for my C++ class, dealing with a random number generator guessing game. I'm on the last step, finally. I gotta add functionality to output the array of guesses to a file after the answer has been guessed, and I need to include exception handling within my program. Is there a simple way to do these things? I am completely lost at this point, and just want to get it over with so i can start C++ from scratch and learn it at my own pace, rather than the rush job of online school.

Recommended Answers

All 7 Replies

Here is an example of how to tell the fstream to throw an exception if something wrong happened, and how to catch the exception. I guess that is what is required to do. The basic mechanism of exceptions in C++ is with the "throw" "try" and "catch" keywords. Their meaning is pretty clear from their names.

You use "throw" to initiate/create the exception (throw an exception). Now, you can throw anything you like, but it is most common to throw something derived from the std::exception class, and typically it is thrown by value and caught by reference. However, you can also throw primitive types and pointers to objects, but that is less usual.

You use the "try" statement (followed by { } ), to tell the compiler to be watching for exceptions to be thrown. If an exception is thrown, all the actions that were performed from the start of the { after the try keyword to the point where the exception was thrown will be sort-of "undone" (the stack is unwinded).

Following the try-clause (after the closing } ), you follow with a "catch" statement (or multiple ones). There, between parenthesis after "catch", you tell the compiler what kind(s) of exception you want to catch. When the exception is thrown, the stack unwinded up to the try-clause, the exception that was thrown is checked against the catch-statements for correspondance. If you have a catch statement for the kind of exception that was thrown, the code following that catch-statement will get executed.

If, after the exception was thrown, it does not find a matching catch-statement, it will keep unwinding the stack until it gets to the start of another try-clause. If it unwinds the stack all the way to the beginning of the program without ever finding an appropriate catch-statement, it will terminate the program and report the error through a standard error channel (std::cerr).

If you want to cook-up your own exception class and use it, here is an example:

#include <exception>

//create an exception class derived from std::exception:
class guess_out_of_range : public std::exception {
  public:
    guess_out_of_range() throw() : std::exception() { }; //trivial constructors.
    guess_out_of_range(const guess_out_of_range& e) throw() : std::exception(e) { };
    guess_out_of_range& operator=(const guess_out_of_range&) throw() { return *this; };
    virtual ~guess_out_of_range() throw() { };
    virtual const char* what() const throw() {
      return "Your guess was out-of-range!"; //report your error message.
    };
};

//say you have a function that gives you a guess:
int getGuess() {
  int userGuess;
  std::cout << "Please enter a guess: ";
  std::cin >> userGuess;
  if((userGuess < 0) || (userGuess > 100))
    throw guess_out_of_range(); //if out-of-range, throw your custom exception object.
  return userGuess;
};

//now, in main(), you can use the getGuess:
int main() {

  int userGuess = -1;
  while(userGuess < 0) {
    try {
      userGuess = getGuess();        //try to get a good guess from the user.
    } catch(const std::exception& e) {     //catch any exception that could have occurred.
      //now, an exception occurred and was caught, use "e" to refer to it:
      std::cout << "Your guess failed: " << e.what() << std::endl;
      std::cout << "Try again..." << std::endl;
      userGuess = -1;
    };
  };

  std::cout << "Your valid guess was: " << userGuess << std::endl;
  std::cout << "Thank you for playing." << std::endl;
  return 0;
};

OK Mike, is exception handling normally inserted in code, or is it only put in there when you are having problems with your code?

If you want to use exceptions, the code must generally be written with them in mind from the start. Tossing exceptions in as an afterthought has never ended well, in my experience.

>>OK Mike, is exception handling normally inserted in code, or is it only put in there when you are having problems with your code?

Exceptions in C++ are a mechanism to report certain "abnormal" events that can occur that generally break the normal execution of the program. But, if used, they are part of the normal code, it's not a thing you put if you have problems or something, that's debugging. They are a way of handling exceptional cases that are part of the program's possible behaviour (not a bug or problem with the code). For example, not being able to open a file because it doesn't exist, not being able to establish a network connection because the server or connection is down, not being able to proceed with some numerical calculation because of some divide-by-zero situation, etc. etc. These are things that CAN happen but deviate from normal execution, but they have to be dealt with.

Typically, exceptions are comparable to error-codes (which is more of a C-style way of handling exceptional cases). Say you want to open a file and read a fixed amount of data from it. The normal execution would involve opening the file and reading X amount of bytes from it. What if the file doesn't exist? What if it contains less data than you expected to find? These situation break the normal execution of the operation. There are two common ways to deal with this: error-codes and exceptions. With error codes, what you do is, you do one operation that could fail (like opening the file) then check the error code (either returned by the function or as a data member or global variable) to see if it was a success. If it was successful, you can proceed to the next step, otherwise, you report the error or deal with it in some other way. The problem with this approach is that it requires passing or storing the error-code, and most importantly, it breaks the continuity of the code because you always get this alternation of "do something" and "check if it failed and respond".

Exceptions in C++ are much nicer because they allow you to write the "normal" execution (as in, the "all goes according to plan" code) in a continuous sequence, and then have the "handle all the problems" part.

The IO streams library in C++ uses both mechanism (error-codes and exceptions) interchangeably because some programmers prefer one to the other.

For example, if you wanted to write a function that reads a bunch of parameters from a file, but there could be less parameters in the file (say if there are less parameters, you want to use default values for the rest). Then, using error-codes, you would have to do this:

int main() {
  int param1 = 20;
  int param2 = 30; //use some default values for the parameters.
  int param3 = 42; 
  
  ifstream inFile("my_params.txt"); //open the file
  
  if(inFile.is_open()) { //check if it was opened correctly.
    inFile >> param1; //read param1.
    if(!inFile.eof()) { //check if it worked.
      inFile >> param2; //read param2.
      if(!inFile.eof()) { //check if it worked.
        inFile >> param3; //read param3.
      };
    };
  };
  //...
  return 0;
};

You can see how terrible the code looks with this alternation of do-and-check. Now, with exceptions:

int main () {
  int param1 = 20;
  int param2 = 30; //use some default values for the parameters.
  int param3 = 42; 

  ifstream inFile; //turn on the exceptions:
  inFile.exceptions ( ifstream::failbit | ifstream::badbit | ifstream::eofbit );

  try {
    inFile.open ("test.txt");               //nice and simple sequence of steps.
    inFile >> param1 >> param2 >> param3;
  }
  catch (ifstream::failure e) { //handle errors in another part of the code.
    //do nothing, it will just use parameters as default if not read from the file.
  };

  inFile.close();
  return 0;
};

PS: When you have problems with the code and you have a hard time finding it, this is called debugging and can be dealt with by using a debugger that lets you point break-points in the code such that you can stop the program before the error occurs, than step through the lines of code and find exactly where the problem is. This has absolutely nothing to do with exceptions.

That makes sense. It sounds similar to when a computer runs the POST test when it is first started. If the POST test fails, a beep error code sounds to let the user know what the problem is.

OK, so lets say I'm starting with the code you redid last week. I need to add an array of guesses to be stored to a file. Then I need to add exception handling to the program. So I need to add the header for the exception library right?
#include <exception> ??

The part you wrote in the exception class is like reading a foreign language for me. Can you break that down to terms i may understand?

#include <iostream>  // Used to produce input and output information.
#include <cstdlib>  // This header is used to produce the random number generator function.
#include <ctime>  // Header used to help with the production of random numbers.
#include <string> // Header used to allow string usage.

using namespace std;  // Allows the use of cout and cin without having to type std in front of them everytime.

class Guess
{
  public:
    int guess;
    Guess(int userGuess) : guess(userGuess) { };
    virtual void DisplayGuessResult() = 0;
};

class High : public Guess
{
  public:
    High(int userGuess) : Guess(userGuess) { };
    virtual void DisplayGuessResult()
    {
      cout << "Your guess of " << guess << " is too high, guess lower" << endl << endl;
    };
};

class Low : public Guess
{
  public:
    Low(int userGuess) : Guess(userGuess) { };
    virtual void DisplayGuessResult()
    {
      cout << "Your guess of " << guess << " is too low, guess higher" << endl << endl;
    };
};

class Correct : public Guess
{
  public:
    Correct(int userGuess) : Guess(userGuess) { };

    virtual void DisplayGuessResult()
    {
      cout << "Congratulations, you are CORRECT with your guess of " << guess << " !" << endl << endl;
    };
};

class GuessingGame 
{
  public:

    GuessingGame(int seed)
    {
      srand(seed);
      regenerate();
    };
	
    int storedRandomNumber() {
      return m_variable;	
    };

    void regenerate() {
      m_variable = rand() % (maxRandomNumber - minRandomNumber + 1) + minRandomNumber;
    };
    static const int maxRandomNumber = 100;
    static const int minRandomNumber = 1;
    static const int MaxNumberOfGuesses = 10;
    int m_variable;

    void execute() {
      bool playAgain = true;
      while(playAgain)
      {
        regenerate();

        int index = 0; //index of the current guess being taken.
        Guess* UserResponse[MaxNumberOfGuesses]; //record of all guesses so far.

        while(index < MaxNumberOfGuesses) { // Loops for a fixed number of guesses (or a breaking condition).
          int userGuess = 0;
          cout << "Please enter your guess between 1 and 100: " ; // output
          cin >> userGuess;  // Input - User to enter a guess between 1 and 100
          cout << "\n";		

          if(userGuess > maxRandomNumber || userGuess < minRandomNumber) {
            cout << "Please stay within the parameters of guesses between 1 and 100!" << endl;
            continue;
          };

          if(userGuess > m_variable)
            UserResponse[index] = new High(userGuess);
          else if(userGuess < m_variable)
            UserResponse[index] = new Low(userGuess);
          else if(userGuess == m_variable)
            UserResponse[index] = new Correct(userGuess);

          UserResponse[index]->DisplayGuessResult();

          ++index;

          if(userGuess == m_variable)
            break;
        };

        //it is important to delete anything that you allocate with "new":
        for(int i=0;i<index;++i)
          delete UserResponse[i];

        //at this point, the game is finished (one way or another) and ask to play again:
        char again;
        cout << "Do you want to play again?  Enter y for yes or n for no: " ;
        cin >> again;
        cout << endl;

        if(again != 'y') {
          playAgain = false;
          cout << endl << "Thank you for playing My Number Generator Game! " << endl << endl;
        };
      };
    };
};


int main ()
{
  // Create random number

  cout << "Welcome to My Number Generator Game" << endl; // output
  cout << "The object of the game is to guess a number between 1 and 100" << endl << endl; // output

  GuessingGame newGame(time(NULL));
  		
  newGame.execute();

  string yourName;
  cout << "Please type your first name and press enter. " ; // output
  cin >> yourName; // Input - User to enter in their first name
		
  return 0;
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.