Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Exceptions to the programming rules, Part 1

Learn how exception handling has evolved from C to Java

  • Print
  • Feedback

Page 3 of 7

Note
I used Borland C++ 4.52 to create mfc1.c. In fact, I used Borland C++ 4.52 to create all of this article's C/C++ programs. If you lack access to Borland C++ 4.52, you can still compile the source code to those programs with other C++ compilers, although you might need to make minor adjustments to the source code.


mfc1 nicely illustrates the three problems with error code testing:

  • mfc1 does not check to see if fopen() succeeds in creating the destination file. That oversight is bad: if fopen() fails, each call to fputc() -- to put a character to the file -- also fails. Fortunately, you can easily resolve that problem by introducing the following source code after fpDst = fopen (argv [argc-1], "wb");:

    if (fpDst == NULL)
    {
        printf ("Could not open %s for writing\n", argv [argc-1]);
        return;
    }
    
  • Despite the small size of mfc1's source code, the source code illustrates error code testing obscuring the program's natural execution flow. The presence of the if statements -- along with the statements they execute when their expressions evaluate to true -- within the for loop contributes to the obscurity. Unfortunately, we cannot eliminate this problem. The best we can do is minimize the amount of code each if statement executes.
  • mfc1's if statements reveal the third problem -- increased code size due to the duplication of fclose (fpDst); and fclose (fpSrc); function calls.


Let's try to reduce the duplication and deal with the other two problems. After some work, we might end up with Listing 2's mfc2.c source code:

Listing 2. mfc2.c

// ==============================
// mfc2.c
//
// Multiple File Copy #2
//
// Created with: Borland C++ 4.52
// ==============================
#include <stdio.h>
void main (int argc, char *argv [])
{
   FILE *fpSrc, *fpDst;
   int byte, i;
   if (argc < 3)
   {
       printf ("usage: mfc2 srcfile [ srcfile ...] dstfile");
       return;
   }
   fpDst = fopen (argv [argc-1], "wb");
   if (fpDst == NULL)
   {
       printf ("Could not open %s for writing\n", argv [argc-1]);
       return;
   }
   for (i = 1; i < argc-1; i++)
   {
        fpSrc = fopen (argv [i], "rb");
        if (fpSrc == NULL)
        {
            printf ("Could not open %s\n", argv [i]);
            goto exit;
        }
        do
        {
           byte = fgetc (fpSrc);
           if (byte == EOF)
               break;
           if (fputc (byte, fpDst) == EOF)
           {
               printf ("Could not write byte\n");
               goto exit;
           }
        }
        while (1);
        fclose (fpSrc);
   }
exit:
   fclose (fpSrc);
   fclose (fpDst);
}


The mfc2 program fixes the problem of ignoring error codes and also reduces the obscurity and duplication problems. For small programs, like Multiple File Copy, error code testing offers a simple solution for dealing with exceptions. But for larger programs, the three problems that error code testing introduces outweigh the benefits of testing error codes. For those programs, developers need a different technique for handling exceptions. Such a technique exists within C++.

Handling exceptions in C++

The error code testing problems in C led to the development of a new exception-handling technique. That technique originated in the paper "Exception Handling for C++," (revised) written by Andrew Koenig and Bjarne Stroustrup, the father of C++. In April 1990, they presented their paper at the Usenix C++ Conference, and the throw-object/catch-object exception handling technique was born. According to that technique, when a function encounters and cannot cope with an exception, the function creates an object describing an exception. The function then throws that object; hopefully, either the function's direct or indirect caller will catch that object. If caught, the object's contents describe the exception. Code that catches the object can use the object's contents to handle the exception.

  • Print
  • Feedback

Resources