


                                                       Chapter 17
                                                       EXCEPTIONS


EXCEPTIONS ARE NOT NEW TO YOU
_________________________________________________________________

Assuming you have completed part 1 of this tutorial, you have seen
many references to exceptions, but we have said little about how
you can use them.  The purpose of this chapter is to instruct you
on the use of exceptions, and by the time you complete it, you will
have the ability to use exceptions to develop a program with its
own error handling ability.


WHY DO WE NEED EXCEPTIONS?
_________________________________________________________________

The original charter for the development of Ada included the
ability to operate in a real-time environment.  You already
understand, if you have much programming experience, that if it is
possible for an error to surface, it will eventually surface.  Many
programming languages simply terminate operation if a "fatal error"
is detected, but this could be a disaster if the program was
controlling a real-time system upon which human lives or safety
depended.  A 747 in final approach to the airport, or a system used
in a hospital operating room would be two such examples of systems
that simply could not be terminated abruptly because a bad data
point was somehow accumulated.  The careful application of Ada
exceptions will allow the software to gracefully recover from such
a situation rather than aborting operation completely.


OUR FIRST EXCEPTION
_________________________________________________________________

Examine the program named EXCEPT1.ADA for our     ===============
first example program with an exception handler.    EXCEPT1.ADA
Ignore lines 21 and 22 for the moment and you     ===============
will have a program that is not at all unusual,
and should pose no problem for you to
understand.  The program does have a carefully introduced error
however, because when we reach a value of 4 for Index in line 17,
we will be attempting to divide by zero.  Dividing by zero is not
allowed in any programming language, because the answer is infinite
and therefore undefined.  The Ada runtime system will, by
definition, cause the exception named Numeric_Error to be raised,
which is the Ada way of saying that a divide by zero was attempted.
This signals the system to do something about it.  (Actually, there
are many other ways to get the Numeric_Error exception raised but
we will worry about them later.)



                                                        Page 17-1

                                          Chapter 17 - Exceptions

The Ada system will search, in a very definite way, for any
instructions we have given about this error, and if it finds none,
will terminate operation of the program after issuing a message
concerning the error.  If we have given instructions about what to
do with the error, it will execute the instructions and continue
operation as we direct it to do.  The method of giving the system
these instructions is illustrated in lines 21 and 22.


HOW ARE EXCEPTIONS HANDLED?
_________________________________________________________________

When any exception is raised, the system immediately looks at the
end of the current block or subprogram for the reserved word
exception.  If it is found, and if the specific exception that was
raised is defined there, the instructions associated with that
exception are executed, and the subprogram or block is exited.

To define a handler for a specific exception, the reserved word
when is used, followed by the name of the exception, and finally
the sequence of statements to be executed following the =>
operator.  The sequence of statements can be of arbitrary
complexity, but should be kept simple due to the nature of
exception handling.  In this case, we output a message to the
monitor and do nothing else.  As many different exceptions as
desired can be handled at the end of any block or subprogram by
adding additional constructs of the form,

     when <exception_name> => instructions;

following the single instance of the reserved word exception.  We
will study examples of multiple exception handlers later in this
chapter.



WHAT HAPPENS FOLLOWING EXCEPTION HANDLING?
_________________________________________________________________

Following handling of the exception, the program executes an
otherwise normal return to the calling program, and the normal
sequence of instructions in the calling program is executed.  Note
that it is impossible to jump back into the subprogram or block in
which the exception was raised from the exception handling routine
at the end of that block.  In this case, because of the logic used,
the loop defined in line 13 is terminated early because we
essentially jumped out of the loop and the program is ended.  If
an exception handler or a group of exception handlers is included,
it must be the last thing in the block.  If normal execution of the
block reaches the end of the executable statements by coming to the
reserved word exception, the block is terminated normally.  You
cannot drop into the exception handler at the end of a block.  The
only way to get to the exception handling code is through raising
an exception.

                                                        Page 17-2

                                          Chapter 17 - Exceptions


In spite of the additional questions you have at this point,
compile and execute this program.  Observe the results, and if you
do not understand the output, reread the above text until you do,
because these fundamental points are essential to understanding the
entire topic of exceptions.



LET'S USE SEVERAL EXCEPTIONS
_________________________________________________________________

Examine the program named EXCEPT2.ADA for         ===============
additional examples of exceptions.  This program    EXCEPT2.ADA
will answer many of your questions about          ===============
exceptions.

The portion of the program defined in lines 46 through 50, along
with its associated procedure is logically identical to the last
example program except for the loop being moved to the calling
program.  When the divide by zero is detected by the system, which
raises the Numeric_Error exception, the exception is handled by the
exception handler defined in lines 20 and 21, and the return to the
calling program is effected.  In this case however, when control
returns to the calling program, we are still inside of the loop,
and the loop completes normally.  This should indicate to you that
by careful selection of where you handle exceptions, you can
control the overall result.  We will see more about this as we
continue our study of exceptions.

The logic of the second group of instructions, found in lines 52
through 67, is identical to the logic of the first group as studied
in the last paragraph.  The only difference is that the procedure
has been changed into a block and inserted into the code in an
inline fashion.  This has been done to illustrate the use of an
exception in a block of code, and to illustrate that the exception
handler for the block of code is put at the end of that block.
After the exception is raised and handled, execution begins at the
first statement following the block.  Because the block is
contained within the loop, the exception is handled within the loop
and the loop runs to completion.



MULTIPLE EXCEPTION HANDLERS
_________________________________________________________________

Finally, we come to the section of code in lines 69 through 72,
consisting of a simple loop calling the procedure New_Divide_Loop.
The procedure itself, defined in lines 24 through 43, contains an
example of a new operation, the ability to make up our own
exception, raise it ourself, and handle it with our own exception
handler.


                                                        Page 17-3

                                          Chapter 17 - Exceptions

Line 25 declares the identifier My_Own_Exception as being the name
of an exception, and is defined in much the same way that we would
declare a variable.  We cannot assign a value to it, but we can
raise it anywhere within its defined scope which is the same as the
scope of a variable declared at the same place.  The exception is
automatically initialized to the "not raised" condition by the
system.

Beginning in line 37, we define three different exception handlers,
which will cover any exceptions raised anywhere within this
procedure.  The first two are named exception handlers but the
third handler uses the reserved word others to indicate that it
will be used for any exceptions that are not handled by the two
named exception handlers.  The others clause is optional, but if
it is included, it must be last.



RAISING AN EXCEPTION
_________________________________________________________________

If we reach line 31 with a value of 4, which we eventually will
because of the logic of the calling program, we will detect the
divide by zero that would be attempted upon reaching line 34.
Instead of letting the system generate the exception named
Numeric_Error, we generate our own exception named
My_Own_Exception, using the reserved word raise followed by the
name of the exception.  As soon as we raise this exception, the
system jumps to the end of the block, looks for the reserved word
exception, which it finds, then looks for the exception handler
with the name that was raised.  Upon finding it, the statements are
executed, resulting in a message being output to the display, and
a return is effected to the calling program.

In this case, the system will not raise the exception
Numeric_Error, because we are detecting the error before it
actually happens.  You could raise it yourself by inserting the
statement "raise Numeric_Error;" somewhere in this procedure,
possibly when the value of Index is equal to 3.  It would be a good
exercise for you to insert that in the code to see that you can
raise one of the system exceptions as well as your own.

Be sure to compile and execute this program to verify proper
operation according to this description and to ascertain your
understanding of the same.

Note that if an exception occurs, formal parameters of mode out or
in out are not updated since a normal return is not accomplished.
Partial results will therefore not be returned to the calling
program.  This is not illustrated here, but is left for the student
to investigate if desired.




                                                        Page 17-4

                                          Chapter 17 - Exceptions

WHAT ARE THE PREDEFINED EXCEPTIONS?
_________________________________________________________________

There are five predefined exceptions which can be raised by the
system to indicate a very specific problem.  A brief definition
follows;

Constraint_Error - This will occur if something goes out of its
     assigned range.

Numeric_Error - This will occur if something goes wrong with
     arithmetic such as the attempt to divide by zero illustrated
     in this chapter.

Program_Error - This will occur if we attempt to violate an Ada
     control structure such as dropping through the bottom of a
     function without a return.

Storage_Error - This will occur if we run out of storage space
     through either recursive calls or storage allocation calls.

Tasking_Error - This will occur when attempting to use some form
     of tasking in violation of the rules.

All five of these exceptions are defined in detail in section 11.1
of the LRM.



WHAT ABOUT AN UNHANDLED EXCEPTION?
_________________________________________________________________

Examination of the program named EXCEPT3.ADA      ===============
will reveal what happens if an exception is         EXCEPT3.ADA
raised that is not handled by the program.  In    ===============
a word, the program will be terminated, but we
need to understand how termination occurs so we
can intelligently prevent it.

There is a loop in the main program which calls two procedures
successively, Divide_By_Zero and Raise_An_Error.  The first
procedure is identical to that in the first two example programs
and the only exception raised is Numeric_Error, which is handled
properly.

The second procedure has its own exception defined, named
My_Own_Error which it raises and handles itself in the manner
defined previously in this chapter.  It also has a divide by zero
problem in line 29 that will raise the exception Numeric_Error when
Count is equal to 6.  Of course, the logic is defined to make this
happen and illustrate the error.




                                                        Page 17-5

                                          Chapter 17 - Exceptions

PROPAGATION OF EXCEPTIONS
_________________________________________________________________

When the exception Numeric_Error is raised at line 29, the system
searches for the reserved word exception which it finds in line 34
at the end of the procedure.  It then searches for a sequence of
statements for Numeric_Error which it does not find.  Since an
exception handler is not found within the procedure, the exception
is propagated to the calling program in such a way that the
exception appears to have been raised by the calling statement.
In this case it will appear to the logic as if the exception
Numeric_Error was raised by the statement in line 42.  Once again,
the exception rules are applied, and the system searches for an
exception block at the end of the block or subprogram, in this case
being the main program.  Finding the reserved word exception in
line 46, the system looks for the desired exception handler, which
it finds and executes, then drops out of the bottom of the main
program which initiates a return to the operating system.

If there were no handler for the exception, the exception would be
propagated to the operating system, and it would issue some sort
of nasty message about an unhandled exception leading to program
termination.

It should be somewhat obvious to you that if you added another
level of subprogram nesting, you could report the error yourself,
and possibly recover operation of the program.  It is all a matter
of program definition.


CAN YOU EXECUTE AN EXCEPTION WITHOUT RAISING IT?
_________________________________________________________________

As mentioned before, the section of code at the end of the program,
following the reserved word exception, is never executed without
raising an exception.  It can never be executed by dropping into
it.

Be sure to compile and execute this program to observe the
operation of the exceptions.


EXCEPTIONS CAN OCCUR DURING DECLARATIONS
_________________________________________________________________

Examine the program named EXCEPT4.ADA for an      ===============
example of an exception that occurs during the      EXCEPT4.ADA
declaration part of the program.                  ===============

When a procedure is called, its declarations are
elaborated prior to the logic being executed, as we have stated
before.  If one of the declarations cannot be properly elaborated,
then an error occurs and an exception is raised.  Examining the
procedure Try_It will reveal an error in lines 8 through 10, where

                                                        Page 17-6

                                          Chapter 17 - Exceptions

the variable Funny is declared to be of type LIMIT_RANGE with
limits of 14 through 23, then it is initialized to the value 8.
Since this is out of the allowed range, the exception
Constraint_Error will be raised.  The executable part of the
procedure is not yet ready for use, so the exception handler
defined within it cannot be used, and the exception will be
propagated to the calling program where it will be handled just as
if it occurred in the calling statement, which is line 22 in this
case.  The exception will therefore be handled by lines 24 through
26 of the procedure Try_To_Fix_It.  Be sure to compile and run this
program, then study the results.


ADDITIONAL PREDEFINED EXCEPTIONS
_________________________________________________________________

You will find that there are actually additional exceptions
predefined by your compiler, but these are all defined in
additional packages supplied with your compiler.  Packages such as
Text_IO, Sequential_IO, or Calendar (to be discussed later with
tasking), have some number of exceptions defined as a part of their
interfaces, but there are only five exceptions predefined as a part
of Ada.  These were listed and discussed earlier.


A FEW MORE TOPICS CONCERNING EXCEPTIONS
_________________________________________________________________

The example program named EXCEPT5.ADA             ===============
illustrates a few additional topics about           EXCEPT5.ADA
exceptions and illustrates how they are used in   ===============
a package.  This is a very strange program with
lots of exception handling examples for your
study.  You will be left on your own to study the overall operation
of this program, but the unique exception handling techniques will
be pointed out to you.

The package body contains a section of initialization code in lines
37 through 48 which is composed of nothing but a null statement and
several exception handlers.  These are only used during
initialization of the package since they are not within the
executable portion of either of the subprograms.  You will notice
that the exception named Funny_Add_Error is declared in the package
specification so it is visible in the exception handler in line 44,
but the exception named Funny_Subtract_Error is not visible there
because it is declared within the function.  We will see soon
however, that even this exception can be propagated to the main
program.

When the program is executing, a call to the function Subtract_One
raises the exception Funny_Subtract_Error which is handled by the
exception handler at the end of the function in line 32.  A message
is displayed and the same exception is raised by the isolated raise
statement in line 34.  This statement simply raises the exception

                                                        Page 17-7

                                          Chapter 17 - Exceptions

that caused the jump to the exception handler in the first place.
The isolated raise statement can only be used within an exception
handler.  The exception is passed on to the calling program.

Because the exception named Funny_Subtract_Error is not visible to
the main program, it cannot handle it by name but even this
exception can be handled by an others clause as is done in line 66.
After printing a message, the same exception is once again raised
in line 68 where it is passed on to the operating system.  You will
see when you execute this program that the exception is known by
name to the operating system.  It will give you a nasty message
about an unhandled exception and terminate operation.


THE others CLAUSE IN AN EXCEPTION HANDLER
_________________________________________________________________

If the others clause is used, it must be the last exception handler
and it cannot be combined with any other exceptions such as
illustrated in line 62.  As in other Ada constructs, two or more
exceptions can be "or"ed and use the same exception handler.

Note line 57 where an exception is renamed to reduce the length of
its name.  Any exception can be renamed in a similar fashion.

Be sure to compile and execute this program and spend the time
necessary to understand the exception propagation illustrated here.



PROGRAMMING EXERCISES
_________________________________________________________________

1.   Change line 34 of EXCEPT2.ADA to cause a divide by zero when
     Index is equal to 2 and see that both exceptions will be
     handled correctly.

2.   Also in EXCEPT2.ADA, declare a new exception in line 25, and
     raise it in line 32 to see how the others clause handles it.














                                                        Page 17-8