Courses/CS 2124/Lab Manual/Exception Handling

From A-State Computer Science Wiki
Jump to: navigation, search

A portion of this lab is to be done during the scheduled lab time. The take-home programming assignment is to be turned in before the next lab; see the lab website. The in-lab portion is worth 40% of the lab credit; the programming assignment is worth the other 60%. See the website for details on how the programming assignment will be graded. You are not responsible for user errors in input unless specified in the assignment. Feedback will be provided explaining your grade on each assignment.

It is important that you complete each step before going on to the next, as the exercises build upon one another. You will find it helpful to diagram the action of each method or function as you go along. If you have difficulty in some step, DO NOT proceed before resolving it; seek assistance from your lab proctor. You will not be able to fully appreciate the remaining content of the lab and you are likely to compound the problem.

Introduction

This exercise examines how to implement synchronous exception handling in C++. A synchronous exception is the occurrence of a predictable event such as division by zero (when the denominator is zero) or memory overflow (when new is invoked repeatedly without balancing deletes). C++ allows a program to throw an instance of an object when an exception occurs from within a try block and to catch the instance in a subsequent catch block.

Topics Covered in this Lab:
  • handling of synchronous exceptions
  • static data and methods
  • function pointers
Questions Answered in this Lab:
  • How are try, catch, and throw used?
  • When and where are try, catch, and throw appropriate?
Demonstrable Skills Acquired in this Lab:
  • ability to utilize try, catch and throw for various synchronous exceptions
  • understanding of the utility of try, catch and throw


Createproject.pngCreate a project oop03 with the empty C++ header and source files listed below; upon completion of the in-lab portion of this assignment, submit all of these files in zip file oop03iL.zip.
Makefiledirects compiling and linking of others
main.cppcontains main function
DivisionException.h
DivisionException.cpp
SquareRootException.h(homework)
SquareRootException.cpp(homework)
LogarithmException.h(homework)
LogarithmException.cpp(homework)

An Example Exception

Implementation of exception handling begins by defining an object to be thrown; for example, a division exception is used here. Define the object in file DivisionException.h now.

Verbatimcode.pngCode Illustration
class DivisionException
{
   public:
             DivisionException 
                (double unluckyNumerator);
      void   printMessage
                (void);
   private:
      double numerator;
};

The constructor for a division exception object will store the dividend for which the division was attempted; the dividend will be displayed by the print method. Implement the methods in file DivisionException.cpp now.

Verbatimcode.pngCode Illustration
DivisionException::DivisionException (double unluckyNumerator)
{
   numerator = unluckyNumerator;
   return;
}  // end method

void DivisionException::printMessage (void)
{
   cout << "Exception occurred: attempt to divide " << numerator 
        << " by zero!\n";
   return;
}  // end method

Function main in file main.cpp can now be written to test the division exception:

Verbatimcode.pngCode Illustration
   double dividend;
   double divisor;
   cout << "Enter dividend and divisor (both 0 to quit): ";
   cin  >> dividend >> divisor;
   while (dividend != 0 || divisor != 0)
   {
      try
      {
         showQuotient(dividend, divisor);
      }  // end try block
      catch (DivisionException e)
      {
         e.printMessage ();
      }  // end catch block
      cout << "Enter dividend and divisor (both 0 to quit): ";
      cin  >> dividend >> divisor;
   }  // end while not both division operands zero

You will need to add the showQuotient function to the file main.cpp. It should produce output similar to the following (assuming dividend was 7 and divisor was 3):

The quotient was 2.

Write this function now.

Keyword try denotes a block in which exceptions can be thrown; execution of the throw statement only has effect within a try block. The operation of throw and catch is similar in some ways to a function call. The throw statement provides what amounts to an actual parameter list, while catch provides a corresponding formal parameter list. The operation is not identical to a function call, however; when throw transfers control to a catch, execution will proceed forward from there, never returning. A try block may be followed by multiple catches. Values of any type can be thrown, not just objects (although objects can provide much more control).


Labcheckpoint.png FOR IN-LAB CREDIT: Execute the program with several pairs of dividends and divisors to be sure that it is working correctly.


Static Class Members

An unrelated aspect of C++ that can be explored and used to advantage here is that of static class members. When a data item for a class is declared to be static, only one instance of it exists and all objects of the class have access to this same instance; further, the single instance exists from the moment the program starts to the moment it ends. Because of its lifetime, this data may be accessed even when no instance of the class exists; to do so, a static method is used.

One such static variable can be incorporated to maintain a count of the number of division exception objects instantiated. Make the following additions to the exception class defined in file DivisionException.h.

Verbatimcode.pngCode Illustration
   public:
      static
      int    getCount
                (void);
   private:
      static
      int    count;


As the declaration above for count is in a header file, no memory is allocated by it; this must be done in a cpp file. Add the following to the beginning of DivisionException.cpp, just after the #include.

Verbatimcode.pngCode Illustration
int DivisionException::count = 0;


Add count++; to the body of the constructor for DivisionException objects in file DivisionException.cpp; add the method implementation for access method getCount as well. Note that since getCount has been declared static, it can access only static data of the class (which follows from the fact that it may be called when no instance of the class exists).

Finally, add the following cout statement to the end of function main.

Verbatimcode.pngCode Illustration
   cout << "There were " << DivisionException::getCount() 
        << " division exceptions.\n";

Test the program with several pairs of values and be sure that the count for the number of divisions by zero attempted is correct.


Labcheckpoint.png FOR IN-LAB CREDIT: Demonstrate the count of the number of division exceptions for a series of divisions of the lab instructor's choosing.


Function Pointers

Another unrelated aspect of C++ that can be put to good use here is that of function pointers. Function pointers allow one function to be written so that it can work with other functions, instead of just data. Add the following function definition to file main.cpp, noting the use of throw to handle the division by zero exception.

Verbatimcode.pngCode Illustration
double f0 (double x)
{
   if (x == 0)
      throw DivisionException (x);
   return x*x + 3*x + 2/x;
}  // end function


Although it would not really have been treated as such prior to this point, identifier f0 is essentially a pointer to a block of instructions in memory in much the same way an array identifier is a pointer to a block of integers or doubles set aside for its data.

In general, a function pointer variable is defined as follows.

Examplecode.pngExample Code
   returnType (*functionPointer) (parameter list);


Note that the parentheses surrounding *functionPointer are necessary to prevent the compiler from associating the * with returnType. Given the function f0 above, the function pointer g could be defined as follows and assigned the location of the same instructions as function f0.

Verbatimcode.pngCode Illustration
   double (*g) (double) = f0;


The pointer g can then be used just like f0.

Verbatimcode.pngCode Illustration
   cout << "f0(5) = " << f0(5) << endl
        << "g(5)  = " << g(5)  << endl;


Consider the notion of integration, or computing the area under the curve defined by a function. The height of the curve is dictated by the particular function, but the algorithm for finding area is the same for all functions. A function to perform integration could be written as follows:

Verbatimcode.pngCode Illustration
double integrate (double (*function) (double), double low, double hi, double dx)
{
   double sum = 0;

   for (double x = low + dx/2; x < hi; x += dx)
   {
      sum += function(x) * dx;
   }  // end for

   return sum;
}  // end function


This function can be tested with calls for several different functions; add the following examples to file main.cpp.

Verbatimcode.pngCode Illustration
double f1 (double x)
{
// if (x < 0)
//    throw SquareRootException (x);
   return sqrt(x) + sin(x);
}  // end function

double f2 (double x)
{
// if (x <= 0)
//    throw LogarithmException (x);
   return log(x) + sin(x);
}  // end function


Note again the commented throw statements for handling exceptions; these exceptions are to be implemented in the homework.

Now add the following test code to the function main.


Verbatimcode.pngCode Illustration
   double a;
   double b;
   cout << "Enter low and high bounds for integration (low > high to quit): ";
   cin >> a >> b;
   while (a < b)
   {
      double dx;
      cout << "Enter interval size: ";
      cin >> dx;
      try
      {
         cout << "Definite integral for f0 by summation is " 
              << integrate (f0, a, b, dx) << endl; 
         cout << "Definite integral for f1 by summation is " 
              << integrate (f1, a, b, dx) << endl; 
         cout << "Definite integral for f2 by summation is " 
              << integrate (f2, a, b, dx) << endl;
      }  // end try
      catch (DivisionException e)
      {
         e.printMessage ();
      }  // end catch
      cout << "Enter more low & high bounds for integration (low > high to quit): ";
      cin >> a >> b;
   }  // end while a < b


Execute the program with several different sets of input to be sure that it is working correctly. Sketch graphs and compare the computed results with an approximation from the graphs.


Labcheckpoint.png FOR IN-LAB CREDIT: Integrate functions f0, f1, and f2 over a domain of the lab instructor's choosing.



Labsubmitsinglefile.png FOR IN-LAB CREDIT: Zip up these files: Makefile main.cpp DivisionException.h DivisionException.cpp
Name the file oop03iL.zip and upload to CSCADE.