%*******************************************************************************
%* START OF AUTOMATICALLY GENERATED TEX FILE *
%*******************************************************************************
%* *
%* This TeX file was automatically generated by the FunnelWeb preprocessor. *
%* You can typeset this file to produce printed documentation by running it *
%* through the TeX typesetter using a command such as: *
%* tex thisfilename *
%* The resultant file thisfilename.dvi can be printed using a command such as: *
%* lpr -Pcslw -d thisfilename.dvi *
%* *
%* FunnelWeb is a preprocessor that allows programmers to weave programs and *
%* their documentation together in a single document. The FunnelWeb program *
%* analyses such documents producing both program files and typeset *
%* documentation such as this TeX file. *
%* FunnelWeb was created by Ross Williams. *
%* *
%* For more information on FunnelWeb look in the following FTP archive: *
%* Machine : sirius.itd.adelaide.edu.au [IP=129.127.40.3]. *
%* Directory: ~pub/funnelweb/ *
%* (or some other appropriately named directory). *
%* or email Ross Williams at ross@spam.adelaide.edu.au *
%* *
%*******************************************************************************
%===================== Start of FunnelWeb TeX Definitions ======================
% Version
% -------
% This is FunnelWeb TeX Macro Library Version 1.0.
% Copyright
% ---------
% This set of FunnelWeb TeX definitions was written by Ross Williams and was
% originally Copyright (C) 1992 Ross N. Williams. However, I, Ross Williams,
% hereby forego any claim to Copyright in this set of FunnelWeb TeX definitions
% and hereby authorize that the set of TeX definitions pass into the public
% domain. -- Ross N. Williams, 3:41pm 07-May-1992, Adelaide, Australia.
% Modification
% ------------
% Please record all modifications to these TeX definitions here. Unless
% otherwise specified, all modified definitions fall in the public domain too.
%
% Programmers:
% RNW Ross N. Williams ross@spam.adelaide.edu.au
%
% Changes:
% 07-May-1992 RNW Prepared this work for public domain release.
%
% General Comments
% ----------------
% This set of TeX definitions exists for two reasons:
%
% 1. To shorten and neaten the FunnelWeb TeX output.
% 2. To allow users to fiddle with the output format in their input files
% (by inserting redefining "\def"s) without having to resort to
% modifying the FunnelWeb code.
%
% The user is warned that these definitions may be changed from time to time
% (but probably not much). The user should not be too sneaky. In particular,
% users wishing to redefine some of these macros should do so in an explicitly
% defined section at the top of their input file. This will mean that in the
% event of problems, that section can simply be deleted or commented out to
% allow the document to at least be typeset in the default format. Users should
% limit themselves to redefining these macros in such a section and should
% refrain from using the macros throughout their documents.
% Environment Parameters
% ----------------------
% \tolerance tells TeX how tolerant it should be about making bad line and
% page breaks. Here we set it to it's maximum, as
% 1) Computer programs are likely to cause lots of bad breaks.
% 2) In most cases the user would probably rather get the TeX file through
% TeX without any errors than fiddle with spacings for perfection.
\tolerance=10000
% I don't like indentation as it makes the page look more busy. Instead,
% paragraphs are separated by a little space (see next).
\parindent=0pt
% In many cases, users will produce documents with long runs of paragraphs.
% In order to space out these paragraphs, it is convenient to maintain a
% prevailing non-zero \parskip (end-of-paragaph skip). The only trouble is
% that the skip becomes a problem in macro definitions which require no skip
% and so we have to turn the skip on and off. The following two macros
% simplify this process.
\def\fwparskipon{\parskip=\medskipamount}
\def\fwparskipoff{\parskip=0pt}
\fwparskipon
% Setting raggedbottom allows TeX to leave a bit of space at the bottom of the
% page in order to better vertically align the rest of the page (e.g. skips
% won't stretch as much). It also means that headings are less likely to be
% isolated at the bottom of the page without any following text.
\raggedbottom
% Fonts
% -----
% Most of the typeset output is set in 10pt roman and 10pt tt font.
% The major extra font needs spring from titles and headings.
% For portability's sake we use only the following fonts:
% cmr10
% cmbx10
% cmtt10
% and some enlargements of them. These fonts are all "standard" fonts
% in Plain TeX. See The TeXbook p.350.
\font\fwfontnote=cmr7
\font\fwfontnorm=cmr10
\font\fwfontnorma=cmr10 scaled \magstep1
\font\fwfontnormb=cmr10 scaled \magstep2
\font\fwfontbold=cmbx10
\font\fwfontbolda=cmbx10 scaled \magstep1
\font\fwfontboldb=cmbx10 scaled \magstep2
\font\fwfontboldc=cmbx10 scaled \magstep3
\font\fwfontboldd=cmbx10 scaled \magstep4
% Macros for Stylistic Details
% ----------------------------
% This section contains all the fiddly little macros for setting the details
% of each macro definition.
% Macro definitions are sandwiched by calls to these macros which can be used
% to sort out the spacing before and after the macro definition.
\def\fwbeginmacro{\fwparskipoff\bigskip}
\def\fwendmacro{\fwparskipon\par}
% These macros deal with the macro name and definition line.
\def\fwmacroname#1#2{{\sl #1\/}$\lbrack$#2$\rbrack$}
\def\fwfilename#1#2{{\bf #1}$\lbrack$#2$\rbrack$}
\def\fwzero#1{{\bf Z}}
\def\fwmany#1{{\bf M}}
\def\fwequals{ $\equiv$}
\def\fwplusequals{ $+\equiv$}
% Now for the actual body of the definition. It looks nice to have the tt
% code indented a little. Again, we use macros instead of writing direct TeX,
% so as to allow the user to fiddle this stuff to taste without having to
% modify the FunnelWeb C code.
\def\fwodef{\parindent=15pt\vskip0pt$\lbrace$\parindent=20pt}
\def\fwcdef{$\rbrace$\vskip0pt\parindent=0pt}
\def\fwoquote{`}
\def\fwcquote{'}
\def\fwoparen{$($}
\def\fwcomma{$,$}
\def\fwcparen{$)$}
\def\fwparam#1{$\diamond #1$}
\def\fwparams#1{$(\diamond #1)$}
% These macros deal with the notes that are appended at the end of each
% macro definition. Note that even though \fwisafile,\fwusedin, and \fwseealso
% have the same definition, they are given different names so as to allow the
% user to redefine these macros to typeset each kind of information differently
% if desired.
\def\fwbeginmacronotes{\begingroup\baselineskip=9pt\smallskip}
\def\fwnote#1{{\fwfontnote #1}\par}
\def\fwisafile#1{\fwnote{#1}}
\def\fwusedin#1{\fwnote{#1}}
\def\fwseealso#1{\fwnote{#1}}
\def\fwendmacronotes{\endgroup}
% Macros to Typeset Program Code Verbatim
% ---------------------------------------
% This is by far the hairiest and most difficult part of the typesetting task
% because we have to turn off most of TeX's natural instincts in order to
% typeset the program text exactly as it appears in the input file.
% Two macros are defined to pull this off: \fwbtx and \fwverbatimgobble.
% Their code was inspired by the following sections of "The TeXbook":
% Appendix D: Dirty Tricks, 3.Verbatim listing, p.380-382.
% Appendix E: Example Formats, p.421.
% The \fwbtx[ (for "FunnelWeb Begin TeXt") macro does most of the hard work.
% The liberal use of "%" is because I don't understand TeX well enough to
% understand when an end of line will cause trouble, and I am playing it safe.
% Before defining the main \fwbtx macro, we have to stash away some definitions
% in the hidden part of TeX's environment. Let's hope that these "hidden"
% definitions don't affect anything except what is desired to be affected.
% The tt font in which we wish to set the text has two Latin lurking ligatures!
% These are ?` and !`. To disable them, we define the left quote when ACTIVE
% to be defined in such a way as to prevent ligatures. The main TeX text will
% normally not be exposed to this definition because normally the leftquote
% character is not active. The \fwbtx macro temporarily makes the left quote
% character active thus activating the deactivation of left quote ligatures.
% See The TeXbook p.381.
{\catcode`\`=\active \gdef`{\relax\lq}}
% TeX is fairly carefree about spaces and so we have to make it more serious.
% To do so we pull the same trick as above, setting up a definition for active
% space, but only making space active during the span of the verbatim text.
% In Plain TeX the active space is defined to be simply a space, but here we
% define it to be a control space. This ensures that the space cannot
% be gobbled up by one of TeX's mysterious mechanisms when activated.
% See The TeXbook, p.381 and p.352.
{\obeyspaces\global\let =\ }
% Here is the main \fwbtx verbatim text macro.
% Note: The order in which all these pieces of business have to be done is
% still a partial mystery to me. Don't fiddle with this stuff unless you
% think you know what you are doing.
\def\fwbtx[{%
%
% The funnies involved in getting verbatim output are safely housed inside
% this \begingroup, and the \endgroup in \fwverbatimgobble. Groups are used
% instead of curly braces because we have to be able to signal the end of
% this macro with a curly brace.
\begingroup%
%
% \pars at the end of empty lines in the verbatim text won't come out normally
% because TeX is in vertical mode and they get gobbled up. To prevent this,
% we force \par to exit vertical mode first. See The TeXbook p.381.
\def\par{\leavevmode\endgraf}%
%
% Activate the leftquote character so as to avoid ligatures (see above).
\catcode`\`=\active%
%
% The \obeylines macro simply defines end of line (^M) to be \par. This ensures
% that TeX will treat each verbatim line as a new paragraph.
\obeylines%
%
% To get verbatim output, we have to desex all the special characters. This
% is explained in detail in The TeXbook p.380.
\def\do##1{\catcode`##1=12 }\dospecials%
%
% Activate the space character so as to make TeX treat blanks seriously.
% This activation invokes an eralier definition (see above).
\obeyspaces
%
% Interparagraph skips do not help the cause.
% Note: We have to preserve the indentation though, as the code is actually
% indented in the final output. See \fwodef in an earlier section.
\parskip=0pt%
%
% We typeset the verbatim text in tt font (courier on the Macintosh) for a
% number of reasons:
% - tt font has the same horizontal spacing for each character.
% - tt font covers the ASCII character set.
% - tt font doesn't have many surprises (e.g. ligatures).
% - tt font looks much what you might see on a computer terminal screen.
\tt%
%
% Having set up an environment for verbatim, we are ready to use it.
% By invoking \fwverbatimgobble, this \fwbtx macro gobbles up text verbatim (as
% part of the parameter of \fwverbatimgobble) until it sees the termination
% string "]fwetx=" (the "=" was thrown in to add obscurity as this sequence
% must never occur in the verbatim text).
\fwverbatimgobble}
% The \fwverbatimgobble macro exists to allow \fwbtx to bracket verbatim text.
\def\fwverbatimgobble#1]fwetx={#1\endgroup}
% Table of Contents
% -----------------
% The five levels of table of contents that FunnelWeb supports are identified
% by the five letters [A..E]. These are used throughout the following macros.
% The following macros are utilities to the TOC macros to follow.
\def\fwrule{\medskip\hrule\medskip}
\def\fwqh{\hskip1.5em\relax}
\def\fwbeforesec{\penalty-200\bigskip\medskip\par}
% The following macros are used to typeset the table of contents.
\def\fwtocstart#1{\fwrule\leftline{\fwfontbolda Table of Contents}\fwrule}
\def\fwtoca#1#2{\leftline{{\bf #1 #2}}}
\def\fwtocb#1#2{\leftline{\fwqh #1 #2}}
\def\fwtocc#1#2{\leftline{\fwqh\fwqh #1 #2}}
\def\fwtocd#1#2{\leftline{\fwqh\fwqh\fwqh #1 #2}}
\def\fwtoce#1#2{\leftline{\fwqh\fwqh\fwqh\fwqh #1 #2}}
\def\fwtocfinish#1{\fwrule}
% The following "library" macros define five different strengths of headings
% which can be used later in the section macros.
\def\fwliba#1#2{\vfill\eject{\fwfontboldc #1 #2}\penalty200\smallskip}
\def\fwlibb#1#2{\fwbeforesec{\fwfontboldb #1 #2}\penalty200\smallskip}
\def\fwlibc#1#2{\fwbeforesec{\fwfontnormb #1 #2}\penalty200\smallskip}
\def\fwlibd#1#2{\fwbeforesec{\bf #1 #2}\penalty200}
\def\fwlibe#1#2{\fwbeforesec{\bf #1 #2}}
% Here are the macros that actually typeset the section headings throughout
% the document. The fwlib system has been employed so as to easily allow the
% user to redefine the strengths of headings to taste. For example, the
% user could insert in the input document a similar set of definitions to these
% but with the b..e headings set to \fwlibc. This would tone down the output.
\def\fwseca#1#2{\fwliba{#1}{#2}}
\def\fwsecb#1#2{\fwlibb{#1}{#2}}
\def\fwsecc#1#2{\fwlibc{#1}{#2}}
\def\fwsecd#1#2{\fwlibd{#1}{#2}}
\def\fwsece#1#2{\fwlibe{#1}{#2}}
% Support for Explicit Typesetting
% --------------------------------
% FunnelWeb supports pragmas and other constructs that allow
% typesetter-independent typesetting commands to be given. The
% following macros support these features.
% The in-text literal @{sloth@} and emphasise @[walrus@] features.
\def\fwlit#1{{\tt #1}}
\def\fwemp#1{{\it #1}}
% The "@p new_page" pragma.
\def\fwnewpage{\vfill\eject}
% The "@p vskip Nmm" pragma.
\def\fwvskip#1{\null\vskip #1mm}
% The "@p title " pragma.
\def\fwfontnormal#1{{\fwfontnorm {#1}}}
\def\fwfonttitle#1{{\fwfontboldd {#1}}}
\def\fwfontsmalltitle#1{{\fwfontboldb {#1}}}
\def\fwleftline#1{\leftline{#1}}
\def\fwcenterline#1{\centerline{#1}}
\def\fwrightline#1{\rightline{#1}}
% Support for Old FunnelWeb
% -------------------------
% The following macros were used extensively in the first version of
% FunnelWeb and are retained so that these older input files will still
% typeset cleanly.
\def\p#1{{\tt #1}} % P for Program text.
\def\flagpage#1#2{
\null
\vfill
\centerline{\fwfontboldd #1}
\vskip 1cm
\centerline{\fwfontboldd #2}
\vfill
\null
\vfill
}
%====================== End of FunnelWeb TeX Definitions =======================
\fwvskip{50}
\fwcenterline{\fwfonttitle{Exceptions}}
\fwcenterline{\fwfonttitle{Package}}
\fwvskip{10}
\fwcenterline{\fwfontsmalltitle{by Ross Williams}}
\fwcenterline{\fwfontsmalltitle{April 1993}}
\fwnewpage
\fwvskip{20}
\fwcenterline{\fwfonttitle{Contents}}
\fwvskip{10}
\fwtocstart{}
\fwtoca{1}{Overview}
\fwtocb{1.1}{Configuration Header Notice}
\fwtocb{1.2}{Abstract}
\fwtocb{1.3}{Motivation}
\fwtocb{1.4}{What is an Exception?}
\fwtocb{1.5}{Exceptions as Active Statuses}
\fwtocb{1.6}{How to Use This Package}
\fwtoca{2}{Static Parameters of this Package}
\fwtocb{2.1}{Inline Switch Parameter}
\fwtocb{2.2}{Threading Parameter}
\fwtocb{2.3}{Macros for Global Variables}
\fwtoca{3}{Specification}
\fwtocb{3.1}{Specification Includes}
\fwtocb{3.2}{Exception Type}
\fwtocb{3.3}{Macro EX\_RAISE}
\fwtocb{3.4}{Exception Block Construct}
\fwtocb{3.5}{Definition of Semantics}
\fwtocb{3.6}{Exception Information}
\fwtocb{3.7}{Function ex\_str}
\fwtocb{3.8}{A Warning About Names}
\fwtocb{3.9}{Glossary}
\fwtoca{4}{Implementation}
\fwtocb{4.1}{Overview and Data Structure Description}
\fwtocc{4.1.1}{Global Variables}
\fwtocc{4.1.2}{Magic Numbers}
\fwtocb{4.2}{Exception type}
\fwtocb{4.3}{Macros for Declaring and Defining Exceptions}
\fwtocb{4.4}{Macro for Raising Exceptions}
\fwtocb{4.5}{Exception Block Macros (Safe)}
\fwtocb{4.6}{Fast, Inline Versions of the Exception Block Macros}
\fwtocb{4.7}{Function ex\_str}
\fwtocb{4.8}{Support Functions}
\fwtocc{4.8.1}{Function \_exrai}
\fwtocc{4.8.2}{Function ex\_bomb}
\fwtocc{4.8.3}{Function \_expop}
\fwtoca{5}{Test Program}
\fwtocb{5.1}{Function sc01}
\fwtocb{5.2}{Function sc02}
\fwtocb{5.3}{Function sc03}
\fwtocb{5.4}{Function sc04}
\fwtocb{5.5}{Function sc05}
\fwtocb{5.6}{Function sc06}
\fwtocb{5.7}{Function sc07}
\fwtocb{5.8}{Function sc08}
\fwtocb{5.9}{Function sc09}
\fwtocb{5.10}{Function fa01}
\fwtocb{5.11}{Function fa02}
\fwtocb{5.12}{Function fa03}
\fwtocb{5.13}{Function fa04}
\fwtocb{5.14}{Function fa05}
\fwtocb{5.15}{Function fa06}
\fwtocb{5.16}{Function fa07}
\fwtocb{5.17}{Function fa08}
\fwtocb{5.18}{Function fa09}
\fwtoca{6}{Design Notes}
\fwtocb{6.1}{Architectural Discussion}
\fwtocb{6.2}{Exception Identity}
\fwtocb{6.3}{Identifier Names}
\fwtocb{6.4}{Catering for Multi-Threading}
\fwtocb{6.5}{Checking}
\fwtocb{6.6}{Future Improvements}
\fwtocfinish{}
\fwnewpage
\fwseca{1}{Overview}
This overview section provides general information about this package.
Processing this file using FunnelWeb will result in the production of the
specification and implementation files defined below. In addition a test
program for testing this package will be generated. This FunnelWeb source
file and the files that it generates are Copyright (C) Ross Williams 1993.
See the configuration header notice below.
\fwbeginmacro
\fwfilename{except.h}{1}\fwequals \fwodef \fwmacroname{Specification file}{18}\fwbtx[
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwisafile{This macro is attached to an output file.}
\fwendmacronotes
\fwendmacro
\fwbeginmacro
\fwfilename{except.c}{2}\fwequals \fwodef \fwmacroname{Implementation file}{31}\fwbtx[
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwisafile{This macro is attached to an output file.}
\fwendmacronotes
\fwendmacro
\fwbeginmacro
\fwfilename{ex\_test.c}{3}\fwequals \fwodef \fwmacroname{Test program file}{74}\fwbtx[
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwisafile{This macro is attached to an output file.}
\fwendmacronotes
\fwendmacro
The remainder of this overview section contains only descriptions. It does
not contain any code.
\fwsecb{1.1}{Configuration Header Notice}
The following notice applies to this FunnelWeb source file as well as to
the FunnelWeb output files to which it is written.
\fwbeginmacro
\fwmacroname{Configuration header notice}{4}\fwmany{}\fwequals \fwodef \fwbtx[
/*************************************************************************/
/* Package name : Except. */
/* Version : 1.0 */
/* Completed : April 1993. */
/* Released : 29 September 1993. */
/* First created : This package was first created in April 1993. */
/* Summary : This package provides Ada-like exceptions for C. */
/* Components : except.fw - FunnelWeb source file. */
/* except.h - Exported header file. */
/* except.c - Implementation file. */
/* ex_test.c - C test program. */
/* Requires : style.h, as.h, as.c. */
/* Author : Ross N. Williams (ross@guest.adelaide.edu.au) */
/* Rocksoft^tm Pty Ltd */
/* 16 Lerwick Avenue, Hazelwood Park 5066, Australia. */
/* FTP Archive : This file can be found in */
/* "ftp.adelaide.edu.au/pub/funnelweb/examples/" */
/* Disclaimer : This program is distributed WITHOUT ANY WARRANTY; */
/* without even the implied warranty of MERCHANTABILITY */
/* or FITNESS FOR A PARTICULAR PURPOSE. */
/* Copyright : Copyright (C) Ross Williams 1993. */
/* However, permission is granted for anyone to copy, */
/* modify, and distribute this work for any purpose, */
/* commercial or non-commercial, so long as this notice */
/* is included verbatim, and so long as all */
/* modifications are recorded in the change log below. */
/* Changes : Please log any changes to this software either in the */
/* originating FunnelWeb source file, or, if you must, */
/* in the C source files produced from the FunnelWeb */
/* file. */
/* ---- */
/* ??-Apr-93: RNW: Created this package. */
/* 29-Sep-93: RNW: Released this package. */
/* ---- */
/*************************************************************************/
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definitions 18, 31, and 74.}
\fwendmacronotes
\fwendmacro
\fwsecb{1.2}{Abstract}
This package provides an \fwemp{exceptions} facility for C that is very similar to
the exceptions facility in the Ada programming language. An exception is a
control-flow event that causes control to be transferred up the stack
(with the stack unwinding as it goes) until a \fwemp{handler} for the particular
exception invoked is found. The exception facility is totally under the
client's control. The client defines a set of exceptions, and \fwemp{raises} an
exception event by calling \fwlit{EX\_RAISE}. Other functions defined by the client
can catch the exceptions by name. The exception facility is useful for
error handling and package interface definition.
\fwsecb{1.3}{Motivation}
The motivation for creating this package was to provide a simpler, safer, and
more sophisticated non-local jump facility than is provided by the standard
ANSI \fwlit{setjmp} (ANSI 7.6.1.1) and \fwlit{longjmp} (7.6.2.1) primitives. In
particular, the biggest problem with the ANSI functions is that they
require that the thrower and catcher of the control event be sufficiently
coordinated to share a single global variable containing the catcher's
context. The requirement for this link is unacceptable because it precludes
the use of non-local jumps as an element of the interface of packages
having an abstract client (unless \fwlit{jmpbuf}s are passed as parameters - yuk!).
This lack of abstraction results in the following disadvantages:
* A global variable of type \fwlit{jmpbuf} must be agreed upon between thrower
and catcher for each potential throw. The result is a proliferation of
global context variables, whose validity at any particular instant is often
unclear.
* If the programmer wishes to set up a nest of handlers for a particular
condition, the global \fwlit{jmpbuf} buffer for the condition must be explicitly
and carefully saved and restored by each function in the nest.
* The interface to \fwlit{setjmp} and \fwlit{longjmp} is a bit raw, requiring
fiddling with integer values and embedding calls in conditional statements.
A better solution to the problem of non-local jumps is a system of
exceptions.
\fwsecb{1.4}{What is an Exception?}
An exception is essentially a non-local \fwlit{goto} to the nearest dynamically
enclosing target label in the stack of suspended functions. To execute an
exception \fwlit{goto}, one \fwemp{raises} the exception, for example with a \fwlit{raise}
statement \fwlit{raise sloth\_exception;}. Control is then immediately transferred
to the nearest dynamically enclosing handler (label) for \fwlit{sloth\_exception}.
In fact handlers are not simply labels, but block-structured constructs
that define an execution zone within which the set of exceptions that they
handle will be caught. For example, the following exception handling
anonymous block might appear in a program written in the Ada programming
language:
\fwbeginmacro
\fwmacroname{Example of an Ada exception handling block}{5}\fwzero{}\fwequals \fwodef \fwbtx[
declare
n : integer := 2*x+y;
begin
sloth_action(n);
walrus_action(n*n);
exception
when sloth_exception => PUT("The sloth exception went off.");
when aardvark_exception => PUT("The aardvark exception went off.");
end
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is NEVER invoked.}
\fwendmacronotes
\fwendmacro
In the above example, the code between \fwlit{declare} and \fwlit{exception} is
the code within which exceptions will be caught. The code between
\fwlit{exception} and \fwlit{end} contains two exception handlers that will catch
the exceptions \fwlit{sloth\_exception} and \fwlit{aardvark\_exception} should they
be raised by the execution of the declarations or \fwlit{sloth\_action} or
\fwlit{walrus\_action}. Any other exception that is raised within the controlled
zone will simply propagate further up the call chain to the nearest handler
that can handle it.
A most important aspect of the exceptions provided by Ada and this package
is that an exception propagates to the nearest \fwemp{dynamically} enclosing
exception handler. This means that, apart from the shared knowledge of the
definition of the exception, there is not necessarily any static relationship
between the function that raises an exception and the function that catches
it. This makes exceptions both dangerous and powerful. Dangerous because a
programmer can code a \fwlit{raise} statement with no knowledge of the function
catching the exception. Powerful, because this very abstraction allows
packages to raise exceptions that can be caught by abstract clients.
\fwsecb{1.5}{Exceptions as Active Statuses}
Over the years, exceptions have received a lot of bad press and have
generally been considered to be dangerous things. For example, Tony Hoare,
in his Turing Award lecture of 1980, when covering the programming
language Ada, remarked:
\fwemp{Gradually these objectives have been sacrificed in favour of power,
supposedly achieved by a plethora of features and notational conventions,
many of them unncessary and some of them, like exception handling,
even dangerous.} --- Charles Antony Richard Hoare, Turing Award Speech, 1980.
Certainly exceptions can be dangerous things. However, I believe that, on
balance, exceptions enhance reliability rather than deteriorating it. The
reason for this belief is that exceptions \fwemp{actively} indicate problems,
whereas their alternative in this capacity, statuses, \fwemp{passively} indicate
problems.
To see this, consider the simple example case of a function that is supposed
to write its argument character to its argument device. This is a
straightforward operation, unless the device fails in some way, in which case
this fact must be indicated to the caller. The traditional way of defining such
a function is to have it return a status indicating whether the call succeeded.
\fwbeginmacro
\fwmacroname{Example of a function returning a status}{6}\fwzero{}\fwequals \fwodef \fwbtx[
EXPORT bool writdev P_((dev_t,char));
/* Returns TRUE upon success, FALSE if the write failed. */
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is NEVER invoked.}
\fwendmacronotes
\fwendmacro
This results in calling code that should look something like this:
\fwbeginmacro
\fwmacroname{Example of call to function returning a status}{7}\fwzero{}\fwequals \fwodef \fwbtx[
if (!writdev(slothdev,'x'))
bomb("Sloth device failed - aborting.\n");
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is NEVER invoked.}
\fwendmacronotes
\fwendmacro
but often looks like this:
\fwbeginmacro
\fwmacroname{Example of call without checking to a function returning a status}{8}\fwzero{}\fwequals \fwodef \fwbtx[
writdev(slothdev,'x');
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is NEVER invoked.}
\fwendmacronotes
\fwendmacro
Code such as this is extremely dangerous because a program written in this
way might operate correctly for years, but then suddenly start behaving in
odd ways when undetected errors occur. Unfortunately, it
is extremely easy to write code such as this, as is easy to forget that
a particular function even returns a status! Code such as this is quite
common.
Now consider how \fwlit{writdev} looks if it is defined using an exception to
take care of the error case:
\fwbeginmacro
\fwmacroname{Example of a function that uses an exception}{9}\fwzero{}\fwequals \fwodef \fwbtx[
EXPORT void writdev P_((dev_t,char));
/* Raises writedev_ex exception if the write fails. */
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is NEVER invoked.}
\fwendmacronotes
\fwendmacro
By casting the error condition in terms of an exception, the function
\fwemp{by default} ensures that something will happen if an error arises.
In fact, one of two things must happen if the exception is ever raised: either
the programmer codes a catcher for the exception and deals with it (in which
case, it will probably be dealt with properly) or the exception propagates
up out of \fwlit{main()} where it will cause the exceptions package to
initiate a controlled program bomb describing the unhandled exception.
Both of these alternatives ensure that the condition is explicitly handled
which is the important thing.
By using exceptions, the programmer can ensure that \fwemp{by default} any
error condition that arises will cause a program bomb. This is such a
strong safety net that it actually \fwemp{legitimizes} the no-error checking
style of programming so criticized earlier. If you are in a hurry to write
a program and do not wish to bother with error checking, the use of
functions that raise exceptions upon errors ensures that if the code
terminates normally, then it has terminated error-free --- even though
the programmer did not code any checking of return conditions.
Another advantage of exceptions is that they allow whole classes of errors
that might arise in a span of code to be handled at one point. For example:
\fwbeginmacro
\fwmacroname{Example of single-point error handling}{10}\fwzero{}\fwequals \fwodef \fwbtx[
EX_BEGIN
f_file_t file1, file2;
char line[1000];
f_open(file1,read,"sloth.dat");
f_open(file2,write,"walrus.dat");
while (!f_eof(file1))
{
f_readline (file1,line,1000);
f_writeline(file2,line,1000);
}
fclose(file1);
fclose(file2);
EX_FORGET
EX_WHEN(f_open_ex ) goto f_handle;
EX_WHEN(f_read_ex ) goto f_handle;
EX_WHEN(f_write_ex) goto f_handle;
EX_WHEN(f_close_ex)
f_handle:
f_delete(file2);
printf("Error performing copy.");
EX_END]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is NEVER invoked.}
\fwendmacronotes
\fwendmacro
In the above code, a single exception handler handles all the error conditions
that might arise during all the file operations. The resultant code is far
cleaner than the code that would be necessary if the status of each call
had to be checked independently. Using exceptions, the normal and the
exceptional cases can each be expressed clearly.
\fwemp{Summary:} There is no doubt that, when misused, exceptions can be
an extremely dangerous language construct. However, they also have the
outstanding quality of enabling functions to be written that \fwemp{actively}
indicate errors, thus ensuring either that the client code does \fwemp{something}
about the error, or that the program will bomb with an unhandled exception
error. In this capacity exceptions act as \fwemp{active statuses}. In addition,
exceptions allow error handling to be separated from ordinary execution.
These two properties of exceptions mean that, in my opinion, they improve
reliability on balance, and are a worthy programming tool.
\fwsecb{1.6}{How to Use This Package}
Using this package is easy - just follow the following steps!
\fwemp{STEP 1: Define the exception.} Before raising or catching an exception,
you have to define it and make it visible to every entity that needs to
refer to the exception. To define it, you need to think of an identifier that
will be used to represent the exception, and you need to think of a short
(one-line) description for the exception. Having done this, if you want
the exception to be visible only within the current program file, insert
a call to the macro \fwlit{EX\_LOCAL} in the variable section. For example:
\fwbeginmacro
\fwmacroname{Example exception definition}{11}\fwzero{}\fwequals \fwodef \fwbtx[
EX_LOCAL(buf_full,"buf_full: Buffer is full exception.");
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is NEVER invoked.}
\fwendmacronotes
\fwendmacro
The first argument is the \fwemp{exception identifier} and the second argument is
the \fwemp{exception description}. The \fwlit{EX\_LOCAL} macro translates into a
string constant declaration having the name \fwlit{buf\_full} and the value
\fwlit{\char`\"buf\_full: Buff...\char`\"}. It is important that the exception identifer be
unique within its scope as it is implemented by an ordinary constant. It
is also very important that the description of the exception be unique
within the set of exceptions in the program. If each exception has a unique
description, then unhandled exceptions (see later) can be easily identified.
If you want the exception to be visible within all clients of the module
declaring it, replace \fwlit{EX\_LOCAL} above with \fwlit{EX\_EXPORT} and insert a
call to \fwlit{EX\_EXCEPT} in the header file for the module. \fwlit{EX\_EXCEPT}
requires just the variable name.
\fwbeginmacro
\fwmacroname{Example exception declaration}{12}\fwzero{}\fwequals \fwodef \fwbtx[
EX_EXCEPT(buf_full);
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is NEVER invoked.}
\fwendmacronotes
\fwendmacro
\fwemp{STEP 2: Raise the exception.} Having completed step 1, you can now raise the
exception in any module that can see the exception (i.e. the \fwlit{.c} file
with the \fwlit{EX\_LOCAL} or \fwlit{EX\_EXPORT}, and any \fwlit{.c} file that includes
the header file containing the \fwlit{EX\_EXCEPT}) simply be calling \fwlit{EX\_RAISE}.
Note: \fwlit{EX\_RAISE} is syntactically equivalent to a compound statement.
\fwbeginmacro
\fwmacroname{Example of raising an exception}{13}\fwzero{}\fwequals \fwodef \fwbtx[
/* If the buffer is full, raise the full buffer exception. */
if (bufsiz > BUF_MAX)
EX_RAISE(buf_ful);
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is NEVER invoked.}
\fwendmacronotes
\fwendmacro
The effect of a call to \fwlit{EX\_RAISE} is that control will leave the current
function, and the stack will unwind until a function is encountered whose
context contains an active exception handler construct that specifies a
catcher for the exception that has been raised (in this case \fwlit{buf\_ful}.
If no such handler exists, the exception package will bomb the program,
printing out an error message stating that an exception has not been
handled, and giving the description of the exception.
\fwemp{STEP 3: Catch the exception.} Catching exceptions is a little tricker than
raising them. To catch an exception, you need to specify so in advance
by enclosing the code that might raise the exception within an \fwemp{exception
block} construct provided by the \fwlit{EX\_} macros \fwlit{EX\_BEGIN}, \fwlit{EX\_FORGET},
\fwlit{EX\_WHEN} and \fwlit{EX\_END}. Here is an abstract example:
\fwbeginmacro
\fwmacroname{Exception block syntax}{14}\fwzero{}\fwequals \fwodef \fwbtx[
EX_BEGIN
EX_FORGET
EX_WHEN()
EX_WHEN()
EX_WHEN()
EX_END
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is NEVER invoked.}
\fwendmacronotes
\fwendmacro
The entire context (from \fwlit{EX\_BEGIN} to \fwlit{EX\_END}) is syntactically
equivalent to a compound statement (\fwlit{\char`\{\char`\}}). Its semantics are that the
code that might raise an exception is executed, and, if no exceptions are
raised, terminates. If an exception is raised, the code aborts to
the corresponding handler, or is propagated upwards if no matching handler
is present. For a more detailed description of the semantics, see the
specification section.
\fwseca{2}{Static Parameters of this Package}
This package has a few static (compile-time) parameters which can be used
to modify its behavior.
\fwsecb{2.1}{Inline Switch Parameter}
Exceptions are tricky constructs, particularly in a programming language
like C, and it makes sense to incorporate lots of checking into a package
such as this one. At the same time, exceptions are a fundamental language
construct that are likely to be used extensively, and it should be possible
to configure them to be as efficient as possible.
To satisfy these twin needs, this package provides a boolean parameter which
can be used to select two different kinds of implementation. Set \fwlit{\_EX\_FAST}
to \fwlit{FALSE} if you want the exported macros to generate function calls and to
perform lots of checks. Set \fwlit{\_EX\_FAST} to \fwlit{TRUE} if you want the exported
macros to generate inline code that runs as fast as possible with no checking
at all. It is strongly recommended that this symbol be set to \fwlit{FALSE}
unless performance requirements absolutely demand that it be set to \fwlit{TRUE}.
\fwbeginmacro
\fwmacroname{Symbol \_EX\_FAST}{15}\fwequals \fwodef \fwbtx[
#define _EX_FAST FALSE
/* Set _EX_FAST to TRUE to turn on inlining and turn off checking. */
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 32.}
\fwendmacronotes
\fwendmacro
Note: Some other packages (such as the \fwlit{link} package) allow parameters
like this to be set by the client package, thus enabling different client
modules to specify different safety/efficiency trade-offs. However, this level
of parameterization granularity could not be achieved in this package as
some of the checking is magic number checking which must be employed globally
or not at all. The problem is that a module with checks on might complain
about incorrect magic numbers that were not set by another module with checks
off.
\fwsecb{2.2}{Threading Parameter}
The parameter \fwlit{\_EX\_THRD} tells the package whether it is operating within
a multi-threaded environment (\fwlit{TRUE}) (thus prohibiting single-instance
global variables). If you set this to \fwlit{TRUE}, you will also need to
modify the macros for global variables in the next section.
\fwbeginmacro
\fwmacroname{Symbol \_EX\_THRD}{16}\fwequals \fwodef \fwbtx[
#define _EX_THRD FALSE
/* To configure this package to work under a multi-threaded environment, */
/* set _EX_THRD to TRUE and modify the k_var calls later on in the file. */
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 32.}
\fwendmacronotes
\fwendmacro
\fwsecb{2.3}{Macros for Global Variables}
This package uses three global variables --- \fwlit{\_ex\_curr}, \fwlit{\_ex\_id},
and \fwlit{\_ex\_info} --- which root the exception context stack, and store
information about the most recently raised exception. Unfortunately,
global variables such as these cause problems on multi-threaded
systems which require that separate instances of all global variables be
maintained for each thread.
The solution to this problem is to define the global variables as usual,
but to refer to them only through a set of lvalue-yielding macros. These
macros can then be configured to point either directly to a single set of
global variables (non-threaded system), or to thread-instance variables
(threaded system). This is the approach that has been taken in this package.
Accordingly the macros have been defined in this package static
parameter section. Both unthreaded and threaded versions appear below.
\fwbeginmacro
\fwmacroname{Macros for global variables}{17}\fwequals \fwodef \fwbtx[
#if !_EX_THRD
/* Non-threaded system macros simply point to variables. */
#define _EX_CURR _ex_curr
#define EX_ID ((p_ex_t) _ex_id)
#define _EX_ID _ex_id
#define EX_INFO _ex_info
#else
/* Threaded system macros translate to kernel calls. */
/* To make this package work in a multi-threaded environment, */
/* set _EX_THRD to TRUE and modify these definitions. */
/* Note: The kernel should initialize _EX_CURR to NULL. */
#define _EX_CURR (* ((_ex_cx_t *) k_var(51)))
#define EX_ID ((p_ex_t) (* ((p_ex_t *) k_var(52))))
#define _EX_ID (* ((p_ex_t *) k_var(52)))
#define EX_INFO (* ((ptrint *) k_var(53)))
#endif
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 32.}
\fwendmacronotes
\fwendmacro
(The 51, 52 and so on are just example variable numbers. By the way, each of
the three global variables will fit into a \fwlit{ptrint}.).
The global variable \fwlit{\_ex\_curr} is not supposed to be used by the client
so its symbol starts with \fwlit{\_}. \fwlit{EX\_ID} and \fwlit{EX\_INFO} are
intended to be visible to the client. However, the client is not allowed
to write to \fwlit{EX\_ID} and so two forms have been provided, one hidden
read/write form (\fwlit{\_EX\_ID}) and one read-only form (\fwlit{EX\_ID}). The type cast
in \fwlit{EX\_ID} is there solely to convert the value from an lvalue to an rvalue
(ANSI 6.3.4 (footnote 44)) so that the client can't write to it.
If these macros are changed to point to thread instance variables, be
sure to initialize the \fwlit{\_EX\_CURR} variable to \fwlit{NULL} (before performing
any exception actions) if you want unhandled exceptions to be correctly
detected. The other two variables don't need to be initialized.
Because these macros can translate to function calls, an
attempt has been made in the rest of the package to minimize references to
them (e.g. assign their values to temporary variables and manipulate them
rather than using the macro itself multiple times).
\fwseca{3}{Specification}
Because this package exports lots of macros, its specification (\fwlit{.h}) file
contains lots of stuff that should really be in the implementation file out
of sight of the client. Luckily, FunnelWeb allows most of these implementation
details to be located in the implementation section of this FunnelWeb file,
leaving this section to describe, as cleanly as possible, only those components
in the specification file intended to be presented to the client.
In a nutshell, the package exports an exception type, and macros to raise
and catch exceptions.
\fwbeginmacro
\fwmacroname{Specification file}{18}\fwequals \fwodef \fwbtx[
]fwetx=%
\fwmacroname{Configuration header notice}{4}\fwbtx[
#ifndef _EX_DONE
#define _EX_DONE 1
]fwetx=%
\fwmacroname{Specification includes}{19}\fwbtx[
]fwetx=%
\fwmacroname{Exception type}{20}\fwbtx[
]fwetx=%
\fwmacroname{Hidden definitions}{32}\fwbtx[
]fwetx=%
\fwmacroname{Macro EX\_RAISE}{22}\fwbtx[
]fwetx=%
\fwmacroname{Macros for constructing exception blocks}{23}\fwbtx[
]fwetx=%
\fwmacroname{Function spec ex\_str}{30}\fwbtx[
]fwetx=%
\fwmacroname{Hidden function specs}{33}\fwbtx[
#endif
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 1.}
\fwendmacronotes
\fwendmacro
\fwsecb{3.1}{Specification Includes}
The specification file requires just the following includes. The \fwlit{style.h}
include file contains basic C style stuff. The ANSI standard \fwlit{setjmp.h}
include file (ANSI Standard Section 7.6) is required because the \fwlit{setjmp}
and \fwlit{longjmp} functions/macros are used to implement the package.
\fwbeginmacro
\fwmacroname{Specification includes}{19}\fwequals \fwodef \fwbtx[
#include "style.h"
#include
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 18.}
\fwendmacronotes
\fwendmacro
\fwsecb{3.2}{Exception Type}
Under this package, each exception is represented by an explicit object that
resides in memory. However, the package does not export this object type.
Like many C packages that export abstract types, this package exports only
a pointer-to-object type, the intent being that the client should only ever
manipulate the abstract objects through pointers. Thus, this package exports
a pointer-to-exception type \fwlit{p\_ex\_t}, but does not export a corresponding
exception type \fwlit{ex\_t}.
Most packages that export abstract objects by pointer type also export
functions to dynamically create and destroy the abstract objects. However,
in the case of exceptions there is no need for this. In fact there is a
positive need to create exceptions at compile time and bind them statically
(Dynamic creation would mean that an exception would have to be created before
being raised, which would be awkward to coordinate at run time).
So, instead of exporting a function to create an exception, this package
exports macros that allow the client to statically create (i.e. at compile
time) a new (hidden) exception object and, simultaneously, define a constant
pointer (of type \fwlit{p\_ex\_t}) that points to the new exception object, and
to which an identifier of the client programmer's choosing can be bound.
Here are the macros that do this.
\fwbeginmacro
\fwmacroname{Exception type}{20}\fwequals \fwodef \fwbtx[
]fwetx=%
\fwmacroname{Type p\_ex\_t}{40}\fwbtx[
#define EX_EXCEPT(NAME) ]fwetx=%
\fwmacroname{Implementation of EX\_EXCEPT}{41}\fwbtx[
#define EX_EXPORT(NAME,DESC) ]fwetx=%
\fwmacroname{Implementation of EX\_EXPORT}{42}\fwbtx[
#define EX_LOCAL(NAME,DESC) ]fwetx=%
\fwmacroname{Implementation of EX\_LOCAL}{43}\fwbtx[
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 18.}
\fwendmacronotes
\fwendmacro
The macros \fwlit{EX\_EXPORT} and \fwlit{EX\_LOCAL} each create a brand new exception
object. They can be called only at places in a module where variables can be
declared. The argument \fwlit{DESC} must be a one-line constant string that
describes the exception; it will be written out if the exception is involved
in some kind of error (e.g. if it propagates out of \fwlit{main()} and has to be
described to the user). The \fwlit{NAME} argument must be a program identifier
which will be the name of the pointer constant that will point to the
new exception. Macros \fwlit{EX\_EXPORT} and \fwlit{EX\_LOCAL} are identical except
that \fwlit{EX\_EXPORT} makes the constant pointer globally visible in the
program (i.e. it is present in the symbol table at link time), whereas
\fwlit{EX\_LOCAL} does not. If \fwlit{EX\_EXPORT} has been used, the constant pointer
to the exception can be made visible in other modules by adding an instance
of the \fwlit{EX\_EXCEPT} macro in the header file of the module containing the
exception definition.
Here is an example of a global exception declaration:
\fwbeginmacro
\fwmacroname{Example exception declarations}{21}\fwzero{}\fwequals \fwodef \fwbtx[
In sloth.h:
EX_EXCEPT(sloth_exception);
In sloth.c:
EX_EXPORT(sloth_exception,"Sloth exception");
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is NEVER invoked.}
\fwendmacronotes
\fwendmacro
\fwsecb{3.3}{Macro EX\_RAISE}
The macro \fwlit{EX\_RAISE} raises its argument exception. The argument exception
must be an expression evaluating to a pointer to the exception to be raised.
See a later section for the precise semantics of an exception raise.
This macro is syntactically equivalent to a compound statement. It is
guaranteed that its argument will be evaluated exactly once.
\fwbeginmacro
\fwmacroname{Macro EX\_RAISE}{22}\fwequals \fwodef \fwbtx[
#if _EX_FAST
#define EX_RAISE(P_EX) ]fwetx=%
\fwmacroname{Fast implementation of EX\_RAISE(P\_EX)}{45}\fwbtx[
#else
#define EX_RAISE(P_EX) ]fwetx=%
\fwmacroname{Safe implementation of EX\_RAISE(P\_EX)}{44}\fwbtx[
#endif
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 18.}
\fwendmacronotes
\fwendmacro
\fwsecb{3.4}{Exception Block Construct}
The only way to catch one or more exceptions is to declare a construct
that encloses the code that could raise the exceptions, along with the handler
code. This construct is called an exception block and is implemented using
a small set of preprocessor macros. Here they are:
\fwbeginmacro
\fwmacroname{Macros for constructing exception blocks}{23}\fwequals \fwodef \fwbtx[
#if _EX_FAST
#define EX_BEGIN \
]fwetx=%
\fwmacroname{Fast implementation of EX\_BEGIN}{52}\fwbtx[
#define EX_FORGET ]fwetx=%
\fwmacroname{Fast implementation of EX\_FORGET}{53}\fwbtx[
#define EX_WHEN(P_EX) ]fwetx=%
\fwmacroname{Fast implementation of EX\_WHEN(P\_EX)}{54}\fwbtx[
#define EX_OTHERS ]fwetx=%
\fwmacroname{Fast implementation of EX\_OTHERS}{55}\fwbtx[
#define EX_END ]fwetx=%
\fwmacroname{Fast implementation of EX\_END}{56}\fwbtx[
#define EX_POP ]fwetx=%
\fwmacroname{Fast implementation of EX\_POP}{57}\fwbtx[
#else
#define EX_BEGIN \
]fwetx=%
\fwmacroname{Safe implementation of EX\_BEGIN}{46}\fwbtx[
#define EX_FORGET ]fwetx=%
\fwmacroname{Safe implementation of EX\_FORGET}{47}\fwbtx[
#define EX_WHEN(P_EX) ]fwetx=%
\fwmacroname{Safe implementation of EX\_WHEN(P\_EX)}{48}\fwbtx[
#define EX_OTHERS ]fwetx=%
\fwmacroname{Safe implementation of EX\_OTHERS}{49}\fwbtx[
#define EX_END ]fwetx=%
\fwmacroname{Safe implementation of EX\_END}{50}\fwbtx[
#define EX_POP ]fwetx=%
\fwmacroname{Safe implementation of EX\_POP}{51}\fwbtx[
#endif
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 18.}
\fwendmacronotes
\fwendmacro
These macros must be used in accordance with the following syntax:
\fwbeginmacro
\fwmacroname{Syntax of exception block}{24}\fwzero{}\fwequals \fwodef \fwbtx[
compound_statement = other_forms_of_compound_statement | exception_block
| EX_POP
exception_block = "EX_BEGIN" c_code "EX_FORGET" { handler } "EX_END"
handler = "EX_WHEN(" expression ")" c_code
handler = "EX_OTHERS"
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is NEVER invoked.}
\fwendmacronotes
\fwendmacro
The argument to each instantiation of \fwlit{EX\_WHEN} must be an rvalue of type
\fwlit{p\_ex\_t}. It is guaranteed that this argument will be evaluated \fwemp{at most}
once.
The macro \fwlit{EX\_POP} should be invoked only from within the normal code of
an exception block.
\fwsecb{3.5}{Definition of Semantics}
This section contains a detailed English description of the semantics of the
exception constructs provided by this package. Normally this sort of
information would be laced throughout the package specification. However, in
this package, there are so many special cases that it was thought best to
collect all the tricky stuff in one place. The reader is advised to study
this section thoroughly, as a lack of knowledge about the precise semantics
of the exception construct can be very dangerous.
\fwemp{Basic semantics:} When control hits an exception block,
the code between \fwlit{EX\_BEGIN} and \fwlit{EX\_FORGET} (the
\fwemp{normal code}) is executed. If, during the execution of the normal code, no
exceptions propagate up to this construct then, upon completion of the normal
code, execution of the entire construct terminates. If, during the execution
of the normal code, an exception is raised (in the code or any function it
calls) that propagates up to this construct, control exits the normal code at
that point and is transferred to the code associated with the handler for
the exception raised (the \fwemp{handler code}). If there is no handler for the
exception raised, the exception is propagated to the next dynamically
enclosing handler in the dynamic chain. If there is a handler for the target
exception, the code associated with that handler is executed and the
construct terminates. If an exception propagates out of \fwlit{main()}, the
exception package bombs the program with an error message explaining what
has happened and printing out the description of the offending exception.
\fwemp{State of the function:} You may be wondering why the middle keyword
in the exception construct is \fwlit{EX\_FORGET}. The reason is that the ANSI
standard (ANSI 7.6.2.1) states: \fwemp{All accessible objects have values as of
the time longjmp was called, except that the values of objects of automatic
storage duration that are local to the function containing the invocation
of the corresponding setjmp macro that do not have volatile-qualified type
and have been changed between the setjmp invocation and longjmp call
are indeterminate.} These are strong words! Because this exception package
is implemented using \fwlit{setjmp} and \fwlit{longjmp}, these words mean that
following the raising of an exception, you cannot rely on the value of any
\fwlit{auto}matic variable that was modified within the normal code of an
exception construct before the exception was raised. The \fwlit{EX\_FORGET} syntax
has been designed to act as a continual reminder of this danger.
\fwemp{Direct exits from normal code:} Because the exception package
maintains an explicit stack of exception handling contexts, there are some
restrictions about how control can leave such constructs. ``Acceptable'' ways
of leaving the main code of an exception block are 1) reaching the end of the
block, and 2) raising an exception (handled or unhandled). However, control
can also exit the construct in four other ``unacceptable'' ways. These are
1) by executing a \fwlit{goto} out of the block,
2) by executing a \fwlit{return},
3) by executing an explicit \fwlit{longjmp},
4) by executing \fwlit{exit} or some other program terminating function
(actually this case does not matter as much as the program ends anyway).
To ensure that the exception package correctly maintains its stack of
live exception blocks, each instance of any of the four unacceptable ways
of exiting a block should be prededed by a call to \fwlit{EX\_POP}. Here is
an example of a correct explicit exception block termination.
\fwbeginmacro
\fwmacroname{Example of a correct direct exit from an exception block}{25}\fwzero{}\fwequals \fwodef \fwbtx[
EX_BEGIN
add_char(buffer,'x');
if (is_full(buffer)) {EX_POP; return TRUE;}
EX_FORGET
EX_WHEN(buf_ful)
return TRUE;
EX_END
return FALSE;
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is NEVER invoked.}
\fwendmacronotes
\fwendmacro
\fwlit{EX\_POP} can only be called from within the normal code of an exception
block.
\fwemp{No restrictions on exiting handler code:} By the time control reaches any
exception handler code, the current exception context has been popped, so
there are no constraints at all on exits from handler code. In the example
above, the \fwlit{return TRUE} from the normal code must be preceeded by a call
to \fwlit{EX\_POP}, but no such call is required for the \fwlit{return TRUE} in the
handler code. In fact, such a call would be erroneous. As soon
as control hits handler code it is guaranteed that the current context
has been popped.
\fwemp{Exception handler code can jump to other exception handlers:} Just place a
label inside one handler and a goto in another. This is useful where the code
in one handler is suitable for many exceptions. For example:
\fwbeginmacro
\fwmacroname{Example of goto in exception handler}{26}\fwzero{}\fwequals \fwodef \fwbtx[
EX_BEGIN
add_char(buffer,'x');
EX_FORGET
EX_WHEN(misc_1) goto doit;
EX_WHEN(misc_2) goto doit;
EX_WHEN(misc_3) goto doit;
EX_WHEN(misc_4) goto doit;
EX_WHEN(misc_5)
doit:
printf("Miscellaneous exception happened.\n");
return FALSE;
EX_END
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is NEVER invoked.}
\fwendmacronotes
\fwendmacro
Because there are no restrictions on how handlers can exit, the \fwlit{gotos}
could have pointed outside the construct too.
\fwemp{Local variables:} Each normal code and handler code segment is actually
framed as a compound statement. This means that you can declare local
variables if desired. For example:
\fwbeginmacro
\fwmacroname{Example of local variables in an exception block}{27}\fwzero{}\fwequals \fwodef \fwbtx[
EX_BEGIN
int result;
result=add_char(buffer,'x');
if (result > 10) {EX_POP; return TRUE;}
EX_FORGET
EX_WHEN(buf_ful)
string walrus = "An example string.\n";
printf(walrus);
return TRUE;
EX_END
return FALSE;
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is NEVER invoked.}
\fwendmacronotes
\fwendmacro
\fwemp{Exception blocks can be nested:} Functions can contain many exception
blocks. Exception blocks can even be nested. However, be very sure to obey
the other rules. Here is an example:
\fwbeginmacro
\fwmacroname{Example of nested exception handlers}{28}\fwzero{}\fwequals \fwodef \fwbtx[
EX_BEGIN
add_char(buffer,'x');
EX_BEGIN
big_func();
EX_FORGET
EX_WHEN(buf_ful) printf("big_func raised buf_ful.\n");
EX_END
EX_FORGET
EX_WHEN(buf_ful) printf("add_char raised buf_ful.\n");
EX_WHEN(sloth) printf("either add_char or big_func raised sloth.\n");
EX_END
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is NEVER invoked.}
\fwendmacronotes
\fwendmacro
\fwemp{OTHERS handler:} The \fwlit{EX\_OTHERS} handler catches all exceptions.
\fwemp{Handler precedence:} The first handler that matches an exception
catches the exception. If more than one handler matches the same exception,
the first handler will be activated.
\fwemp{Re-raising an exception:} To re-raise an exception, simply
code the statement \fwlit{EX\_RAISE(EX\_ID);}.
\fwsecb{3.6}{Exception Information}
When an exception is raised, two symbols provide information about the
exception being raised. These symbols are macros and do not necessarily
translate to variable names. However, it is guaranteed that \fwlit{EX\_ID} will be
an rvalue (ANSI 6.2.2.1 (footnote 31)) of type \fwlit{p\_ex\_t}, and that \fwlit{EX\_INFO}
will behave like an modifiable lvalue (ANSI 6.2.2.1) of type \fwlit{ptrint} (see
the header file \fwlit{style.h} for this type). The declarations of these macros
appear in the static parameters section of this document.
Whenever an exception is raised, \fwlit{EX\_ID} contains the ID of the exception
being raised. This value persists until the next exception is raised.
This variable is only marginally useful in the current version of
this package, as each exception handler will always statically ``know'' the
exception that it has caught. In long and complex handlers though, the
capacity for self-reference that \fwlit{EX\_ID} provides enables such handlers
to be coded without hard-wiring the exception in question whenever it is
re-raised. This allows exceptions to be more easily renamed. The macro is
also useful because it allows exceptions to be dynamically re-raised from
code to which many handlers have been pointed to with \fwlit{goto}s. For example:
\fwbeginmacro
\fwmacroname{Example of use of EX\_ID}{29}\fwzero{}\fwequals \fwodef \fwbtx[
EX_BEGIN
add_char(buffer,'x');
EX_FORGET
EX_WHEN(misc_1) goto doit;
EX_WHEN(misc_2) goto doit;
EX_WHEN(misc_3) goto doit;
EX_WHEN(misc_4) goto doit;
EX_WHEN(misc_5)
doit:
printf("Miscellaneous exception happened.\n");
EX_RAISE(EX_ID);
EX_END
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is NEVER invoked.}
\fwendmacronotes
\fwendmacro
\fwlit{EX\_ID} has been configured as an rvalue so that it cannot be written.
Thus, it is guaranteed always to contain the ID of the exception most recently
raised.
The variable \fwlit{EX\_INFO} is more under client control. It exists simply to
act as an abstract channel for code raising exceptions to communicate with
code catching exceptions. There are no rules for this value --- it can be
written to, or read from any piece of code at any time; make up your own
rules. The type of \fwlit{EX\_INFO} has been set to \fwlit{ptrint} so as to allow
any pointer or integer to be conveyed through the variable. This effectively
allows any amount of information to be passed.
\fwsecb{3.7}{Function ex\_str}
The function \fwlit{ex\_str} accepts a pointer to an exception and returns a
pointer to a constant static string being the one line description of the
exception that was provided when the exception was defined with \fwlit{EX\_LOCAL}
or \fwlit{EX\_EXPORT}.
\fwbeginmacro
\fwmacroname{Function spec ex\_str}{30}\fwequals \fwodef \fwbtx[EXPORT string ex_str P_((p_ex_t));]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 18.}
\fwendmacronotes
\fwendmacro
\fwsecb{3.8}{A Warning About Names}
This package exports a number of compile-time and link-time symbols that
pollute the symbol table and link table. This pollution is necessary because
this package relies significantly on exported macros that reference exported
variables. To reduce naming conflicts, nearly all exported names that are
supposed to be visible to the client commence with \fwlit{ex\_} and all exported
names that are supposed to be invisible to the client commence with \fwlit{\_ex}.
Please be careful to avoid names with these prefixes.
\fwsecb{3.9}{Glossary}
\fwlit{Abstract client} --- A client of this package about which no information
is known.
\fwlit{Catch} --- When an exception is raised, it propagates up the run-time
context stack until it encounters a corresponding handler at which point it
is said to have been caught.
\fwlit{Client} --- Any code that uses this package.
\fwlit{Exception} --- A dynamic non-local control-flow event.
\fwlit{Exception block} --- A special kind of anonymous block (called a compound
statement in C) containing exception handlers.
\fwlit{Exception description} --- A single-line string briefly describing the
exception. This string is printed out whenever an exception has to be
identified to the user.
\fwlit{Exception identifier} --- A program identifier that is bound to a constant
whose value is a pointer pointing to an exception object.
\fwlit{Exception handler} --- An exception/code binding that appears at the
end of an exception block and catches the specified exception should it
propagate to that level.
\fwlit{Direct exit} --- The termination of an exception block in a way that
bypasses the exception package (for example, by \fwlit{return} or explicit
\fwlit{longjmp}). The macro \fwlit{EX\_POP} should be called before all such exits.
\fwlit{Raise} --- An exception is raised by an \fwlit{EX\_RAISE} statement. Once
raised, an exception unwinds the stack until it encounters a handler for
the specific exception.
\fwseca{4}{Implementation}
This section describes the implementation of this package. The implementation
is short, but is rather messy because of the fast/safe alternatives, and
because of the fact that much of the functionality of the package is provided
in the \fwlit{.h} file. Much of that functionality has been deferred using
FunnelWeb macros to this section.
The implementation (\fwlit{.c}) file itself is fairly simply, consisting only
of some global variables and a few function definitions.
\fwbeginmacro
\fwmacroname{Implementation file}{31}\fwequals \fwodef \fwbtx[
]fwetx=%
\fwmacroname{Configuration header notice}{4}\fwbtx[
#include "except.h"
#include "as.h"
]fwetx=%
\fwmacroname{Global variables (.c)}{35}\fwbtx[
]fwetx=%
\fwmacroname{Function ex\_str}{58}\fwbtx[
]fwetx=%
\fwmacroname{Function ex\_bomb}{67}\fwbtx[
]fwetx=%
\fwmacroname{Function \_exrai}{60}\fwbtx[
]fwetx=%
\fwmacroname{Function \_expop}{69}\fwbtx[
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 2.}
\fwendmacronotes
\fwendmacro
All the real action happens in the definitions in the specification (\fwlit{.h})
file that are hidden from the client.
\fwbeginmacro
\fwmacroname{Hidden definitions}{32}\fwequals \fwodef \fwbtx[
]fwetx=%
\fwmacroname{Symbol \_EX\_FAST}{15}\fwbtx[
]fwetx=%
\fwmacroname{Symbol \_EX\_THRD}{16}\fwbtx[
]fwetx=%
\fwmacroname{Exception block context type}{34}\fwbtx[
]fwetx=%
\fwmacroname{Global variables (.h)}{36}\fwbtx[
]fwetx=%
\fwmacroname{Macros for global variables}{17}\fwbtx[
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 18.}
\fwendmacronotes
\fwendmacro
\fwbeginmacro
\fwmacroname{Hidden function specs}{33}\fwequals \fwodef \fwbtx[
]fwetx=%
\fwmacroname{Function spec \_exrai}{59}\fwbtx[
]fwetx=%
\fwmacroname{Function spec \_expop}{68}\fwbtx[
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 18.}
\fwendmacronotes
\fwendmacro
\fwsecb{4.1}{Overview and Data Structure Description}
This section gives an overview of the implementation of this package.
This package operates by maintaining an explicit stack of currently active
exception blocks. Whenever an exception block is entered (\fwlit{EX\_BEGIN}),
a \fwlit{setjmp} is executed and the resultant \fwlit{jmp\_buf} is inserted into a
stack node and pushed onto the stack. Whenever an exception block is exited
normally (\fwlit{EX\_END}), the stack node on the top of the stack is popped. When
an exception is raised, the top of the stack is popped off and a \fwlit{longjmp}
is executed to its context. The context then searches its handlers for a
handler for the exception raised. If it finds one, it executes it. If it
doesn't, it pops and \fwlit{longjmp}s the top of stack again. This goes on
until the exception is caught, or the stack is empty.
The stack is composed of a singly-linked list of records of the following
type:
\fwbeginmacro
\fwmacroname{Exception block context type}{34}\fwequals \fwodef \fwbtx[
typedef struct _ex_cxt_
{
]fwetx=%
\fwmacroname{Header magic number field}{37}\fwbtx[
jmp_buf _ex_jmbf; /* Exception block context. */
struct _ex_cxt_ *_ex_prev; /* Pointer to previous stack record. */
]fwetx=%
\fwmacroname{Trailer magic number field}{38}\fwbtx[
} _ex_cx_t;
]fwetx=%
\fwmacroname{Magic number symbols}{39}\fwbtx[
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 32.}
\fwendmacronotes
\fwendmacro
The sneaky part is that instead of storing this stack explicitly on the
heap, it is stored on the run-time stack itself. This is done by having the
\fwlit{EX\_BEGIN} macro create a compound statement containing a local
automatic variable of type \fwlit{\_ex\_cx\_t} which forms a node of the stack.
These nodes are threaded into a linked list that is rooted by the global
variable \fwlit{\_ex\_curr} which points to the node at the top of the stack,
or \fwlit{NULL} if the stack is empty. Two other hidden global variables
\fwlit{\_ex\_id} and \fwlit{\_ex\_info} store the exception id and the exception
information.
\fwsecc{4.1.1}{Global Variables}
This package requires three global variables to keep track of everything.
The variable \fwlit{\_ex\_curr} points to the context record on the top of the
stack, or \fwlit{NULL} if the stack is empty. The variable \fwlit{\_ex\_id} contains
a pointer to the exception most recently raised. The variable \fwlit{\_ex\_info}
is provided for the use of the client.
To enable this package (and its client code) to be converted over to a
multi-threaded system at a later date, these global variables are referred
to only through a set of macros. The macros are described in the static
parameters section of this document. They are called \fwlit{\_EX\_CURR},
\fwlit{EX\_ID}, \fwlit{\_EX\_ID}, \fwlit{EX\_INFO}.
\fwbeginmacro
\fwmacroname{Global variables (.c)}{35}\fwequals \fwodef \fwbtx[
#if !_EX_THRD
GLOVAR _ex_cx_t *_ex_curr = NULL;
GLOVAR p_ex_t _ex_id;
GLOVAR ptrint _ex_info;
#endif
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 31.}
\fwendmacronotes
\fwendmacro
Here is the version of these variables that appears in the header file.
\fwbeginmacro
\fwmacroname{Global variables (.h)}{36}\fwequals \fwodef \fwbtx[
#if !_EX_THRD
extern _ex_cx_t *_ex_curr;
extern p_ex_t _ex_id;
extern ptrint _ex_info;
#endif
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 32.}
\fwendmacronotes
\fwendmacro
\fwsecc{4.1.2}{Magic Numbers}
In order to detect corruptions in exception context records, two magic
number fields have been placed at the start and end of the record. In C, there
are hundreds of ways for corruptions to occur, but in particular, in this
package, if a client exits an exception context without first popping it,
it is quite likely that the context will be ``run over'' by stack frames of
functions called later. If the context is then invoked, the program will
crash. The following magic number fields greatly improve the chances of
detecting such corruptions.
\fwbeginmacro
\fwmacroname{Header magic number field}{37}\fwequals \fwodef \fwbtx[#if !_EX_FAST
ulong _ex_mag1; /* Header magic number. */
#endif]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 34.}
\fwendmacronotes
\fwendmacro
\fwbeginmacro
\fwmacroname{Trailer magic number field}{38}\fwequals \fwodef \fwbtx[#if !_EX_FAST
ulong _ex_mag2; /* Trailer magic number. */
#endif]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 34.}
\fwendmacronotes
\fwendmacro
\fwbeginmacro
\fwmacroname{Magic number symbols}{39}\fwequals \fwodef \fwbtx[
#define _EX_MAG1 0xFB8A5D30
#define _EX_MAG2 0x09F2E7A2]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 34.}
\fwendmacronotes
\fwendmacro
\fwsecb{4.2}{Exception type}
Here is the definition of the pointer-to-exception type. Normally, a hidden
exception type would be declared to which the pointer type would point, but as
exceptions have only a single attribute, their description string, it makes
sense simply to declare the pointer type to be \fwlit{string} type (pointer to
char).
\fwbeginmacro
\fwmacroname{Type p\_ex\_t}{40}\fwequals \fwodef \fwbtx[typedef string p_ex_t;]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 20.}
\fwendmacronotes
\fwendmacro
\fwsecb{4.3}{Macros for Declaring and Defining Exceptions}
The three exception declaration macros make use of the fact that \fwlit{p\_ex\_t}
is really just a string pointer. There is a slight danger that a too-clever
C compiler might overlap two strings for which one is a non-strict suffix
of the other. However, this is considered unlikely.
\fwbeginmacro
\fwmacroname{Implementation of EX\_EXCEPT}{41}\fwequals \fwodef \fwbtx[extern const p_ex_t NAME]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 20.}
\fwendmacronotes
\fwendmacro
\fwbeginmacro
\fwmacroname{Implementation of EX\_EXPORT}{42}\fwequals \fwodef \fwbtx[ const p_ex_t NAME = DESC]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 20.}
\fwendmacronotes
\fwendmacro
\fwbeginmacro
\fwmacroname{Implementation of EX\_LOCAL}{43}\fwequals \fwodef \fwbtx[static const p_ex_t NAME = DESC]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 20.}
\fwendmacronotes
\fwendmacro
\fwsecb{4.4}{Macro for Raising Exceptions}
Two forms of the \fwlit{EX\_RAISE} macro are given. The first simply calls
\fwlit{\_exrai} which performs lots of checks before raising the argument exception.
The fast form does not perform any checking --- it just performs the
essential operations of setting the ID and executing the \fwlit{longjmp}.
\fwbeginmacro
\fwmacroname{Safe implementation of EX\_RAISE(P\_EX)}{44}\fwequals \fwodef \fwbtx[{_exrai(P_EX);}]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 22.}
\fwendmacronotes
\fwendmacro
\fwbeginmacro
\fwmacroname{Fast implementation of EX\_RAISE(P\_EX)}{45}\fwequals \fwodef \fwbtx[{_EX_ID = (P_EX); longjmp(_EX_CURR->_ex_jmbf,NON_ZERO);}]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 22.}
\fwendmacronotes
\fwendmacro
\fwsecb{4.5}{Exception Block Macros (Safe)}
The exception block macros are messy and deserve some explanation.
These macros operate as a coherent whole, so it is probably best to read
them through a few times to see how they interact, rather than attempting
to understand each one independently. Two versions of the exception block
macros are given --- the safe version and the fast version. The safe version
is described first.
The \fwlit{EX\_BEGIN} macro sets the stage for the entire exception block by
starting a compound statement, declaring a local variable for the stack
node (\fwlit{\_ex\_cxlc}), initializing its magic checking numbers, pushing it onto
the stack, and finally saving the current execution context in the \fwlit{\_ex\_jmbf}
field of the newly created stack node. This work has to be done in this macro
as most of the operations require direct access to the local context. Not much
checking can be done here either as we have no way of cross checking the value
of \fwlit{\_EX\_CURR} upon entry. The open \fwlit{if} statement encloses the entire
normal code of the exception block.
\fwbeginmacro
\fwmacroname{Safe implementation of EX\_BEGIN}{46}\fwequals \fwodef \fwbtx[{_ex_cx_t _ex_cxlc; \
_ex_cxlc._ex_mag1 = _EX_MAG1; \
_ex_cxlc._ex_mag2 = _EX_MAG2; \
_ex_cxlc._ex_prev = _EX_CURR; \
_EX_CURR = &_ex_cxlc; \
if (!setjmp(_ex_cxlc._ex_jmbf)) { ]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 23.}
\fwendmacronotes
\fwendmacro
At the point of forgetting, we need to terminate the compound statement
for the normal code, pop the current exception context and then test the
currently live exception against the candidate handlers. This is most
simply performed with a cascaded \fwlit{if} statement. The \fwlit{if} statement
has to start with a \fwlit{FALSE} branch so as to establish the syntactic
context for the first \fwlit{EX\_WHEN} macro.
An earlier version of this package that used integers to represent exceptions
used a \fwlit{switch} statement instead of an \fwlit{if} chain. A \fwlit{switch} statement
is actually preferable to an \fwlit{if} chain because \fwlit{switch}es prohibit
duplicate labels. However, \fwlit{switch}es had to be abandoned because when the
package was changed over so that it used pointers as exception ids, it was
noticed that K\&R78 section 9.7 says that the argument to \fwlit{switch} must be of
type \fwlit{int}. K\&R88 (section A9.4) and ANSI (section 6.6.4.2) weaken this to
include all integral types, but still preclude pointers. So an \fwlit{if} chain
had to be used.
\fwbeginmacro
\fwmacroname{Safe implementation of EX\_FORGET}{47}\fwequals \fwodef \fwbtx[;EX_POP} else {EX_POP; if (FALSE) {]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 23.}
\fwendmacronotes
\fwendmacro
The start of each handler in the exception block is marked by a call to the
\fwlit{EX\_WHEN} macro. All it does is add an extra clause onto the \fwlit{if} chain.
\fwbeginmacro
\fwmacroname{Safe implementation of EX\_WHEN(P\_EX)}{48}\fwequals \fwodef \fwbtx[} else if ((P_EX) == (EX_ID)) {]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 23.}
\fwendmacronotes
\fwendmacro
The \fwlit{EX\_OTHERS} macro is also easily implemented (but, like \fwlit{EX\_FORGET},
could conceivably provoke some inconvenient warnings from \fwlit{lint}.
\fwbeginmacro
\fwmacroname{Safe implementation of EX\_OTHERS}{49}\fwequals \fwodef \fwbtx[} else if (TRUE) {]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 23.}
\fwendmacronotes
\fwendmacro
To end the construct, we terminate the current handler, re-raise the
currently live exception if it has not already been caught, and terminate
the \fwlit{else} of \fwlit{EX\_FORGET} and the outer braces of the entire construct.
\fwbeginmacro
\fwmacroname{Safe implementation of EX\_END}{50}\fwequals \fwodef \fwbtx[} else _exrai(EX_ID); }}]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 23.}
\fwendmacronotes
\fwendmacro
Popping the current exception context is easily achieved with a function call.
Note: The reference to \fwlit{\_ex\_cxlc} will simply not compile if there
isn't a current context. This correctly precludes the use of this macro
outside an exception block. It also guarantees that the argument to \fwlit{\_expop}
will be a pointer to the context record of the innermost statically-enclosing
exception block.
\fwbeginmacro
\fwmacroname{Safe implementation of EX\_POP}{51}\fwequals \fwodef \fwbtx[{_expop(&_ex_cxlc);}]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 23.}
\fwendmacronotes
\fwendmacro
\fwsecb{4.6}{Fast, Inline Versions of the Exception Block Macros}
Here are the fast, inline versions of the exported macros. This version
of the macros operates in exactly the same way as the other version except
that no checking is performed whatsoever. This version of the macros
performs everything on the spot, without calling any support functions at all.
\fwbeginmacro
\fwmacroname{Fast implementation of EX\_BEGIN}{52}\fwequals \fwodef \fwbtx[{_ex_cx_t _ex_cxlc; \
_ex_cxlc._ex_prev = _EX_CURR; \
_EX_CURR = &_ex_cxlc; \
if (!setjmp(_ex_cxlc._ex_jmbf)) { ]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 23.}
\fwendmacronotes
\fwendmacro
\fwbeginmacro
\fwmacroname{Fast implementation of EX\_FORGET}{53}\fwequals \fwodef \fwbtx[;EX_POP} else {EX_POP; if (FALSE) {]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 23.}
\fwendmacronotes
\fwendmacro
\fwbeginmacro
\fwmacroname{Fast implementation of EX\_WHEN(P\_EX)}{54}\fwequals \fwodef \fwbtx[} else if ((P_EX) == (EX_ID)) {]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 23.}
\fwendmacronotes
\fwendmacro
\fwbeginmacro
\fwmacroname{Fast implementation of EX\_OTHERS}{55}\fwequals \fwodef \fwbtx[} else if (TRUE) {]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 23.}
\fwendmacronotes
\fwendmacro
\fwbeginmacro
\fwmacroname{Fast implementation of EX\_END}{56}\fwequals \fwodef \fwbtx[} else longjmp(_EX_CURR->_ex_jmbf,NON_ZERO); }}]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 23.}
\fwendmacronotes
\fwendmacro
\fwbeginmacro
\fwmacroname{Fast implementation of EX\_POP}{57}\fwequals \fwodef \fwbtx[{_EX_CURR = _EX_CURR->_ex_prev;}]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 23.}
\fwendmacronotes
\fwendmacro
\fwsecb{4.7}{Function ex\_str}
The function \fwlit{ex\_str} is easily implemented. Because exceptions are just
pointers to strings, all it has to do is return its argument!
\fwbeginmacro
\fwmacroname{Function ex\_str}{58}\fwequals \fwodef \fwbtx[
EXPORT string ex_str (p_ex) p_ex_t p_ex; {return p_ex;}]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 31.}
\fwendmacronotes
\fwendmacro
\fwsecb{4.8}{Support Functions}
Although most of what this package exports is macros, some of the macros
translate into calls to the support functions described in this section.
Functions have been used for operations that are particularly long-winded.
\fwsecc{4.8.1}{Function \_exrai}
The function \fwlit{\_exrai} raises an exception. This function performs
all the grunt work for the \fwlit{EX\_RAISE} macro, which merely translates to a
call of this function. The function specification appears in the header file
but is not supposed to be seen by the client.
\fwbeginmacro
\fwmacroname{Function spec \_exrai}{59}\fwequals \fwodef \fwbtx[EXPORT void _exrai P_((p_ex_t));]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 33.}
\fwendmacronotes
\fwendmacro
\fwbeginmacro
\fwmacroname{Function \_exrai}{60}\fwequals \fwodef \fwbtx[
EXPORT void _exrai (p_ex)
p_ex_t p_ex;
{
#if _EX_FAST
as_bomb("_exrai: This function should not be called with _EX_FAST==TRUE.");
#else
]fwetx=%
\fwmacroname{Take a copy of \_EX\_CURR in p\_curr}{61}\fwbtx[
]fwetx=%
\fwmacroname{Set the exception ID}{62}\fwbtx[
]fwetx=%
\fwmacroname{Bomb if the stack is empty}{63}\fwbtx[
]fwetx=%
\fwmacroname{Bomb if the target context is not on the stack}{64}\fwbtx[
]fwetx=%
\fwmacroname{Bomb if the target context is corrupted}{65}\fwbtx[
]fwetx=%
\fwmacroname{Raise the exception}{66}\fwbtx[
#endif
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 31.}
\fwendmacronotes
\fwendmacro
This function refers to the value of \fwlit{\_EX\_CURR} lots of times. However,
\fwlit{\_EX\_CURR} could be a function call. To avoid excessive use of this
variable, we take a copy of its value in \fwlit{p\_curr}.
\fwbeginmacro
\fwmacroname{Take a copy of \_EX\_CURR in p\_curr}{61}\fwmany{}\fwequals \fwodef \fwbtx[_ex_cx_t * p_curr = _EX_CURR;]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definitions 60 and 69.}
\fwendmacronotes
\fwendmacro
We set the global exception ID variable \fwlit{\_EX\_ID} \fwemp{before} performing
all the sanity checks so that if one of them fails, the current exception
can be printed out by \fwlit{ex\_bomb}.
\fwbeginmacro
\fwmacroname{Set the exception ID}{62}\fwequals \fwodef \fwbtx[_EX_ID = p_ex;]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 60.}
\fwendmacronotes
\fwendmacro
If the context stack is empty then there is no handler to catch the exception
and we have to bomb the program.
\fwbeginmacro
\fwmacroname{Bomb if the stack is empty}{63}\fwequals \fwodef \fwbtx[
if (p_curr == NULL) {as_wl("_exrai: Unhandled exception."); ex_bomb();}]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 60.}
\fwendmacronotes
\fwendmacro
If the target context is lower down in memory than the stack frame of \fwemp{this}
function (\fwlit{\_exrai}), then because stacks nearly always grow downwards, it
is almost certain that a context was not popped when it should have been.
Strictly speaking, this test is non-portable. However, in
practice, stacks grow downward in the address space on nearly all machines,
and it's such a powerful check, it seems worth the potential portability
problems.
\fwbeginmacro
\fwmacroname{Bomb if the target context is not on the stack}{64}\fwequals \fwodef \fwbtx[
if (UWIDE(&p_curr) > UWIDE(&p_curr->_ex_jmbf))
{
as_wl("_exrai: Target exception context is no longer legitimate.");
as_wl(" Exception context resides beneath the top of stack.");
as_wl(" This means that earlier on, control must have left an");
as_wl(" exception context without first popping its handler.");
as_wl(" Look for jumps out of exception contexts that are not");
as_wl(" immediately preceded by calls to EX_POP.");
ex_bomb();
}]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 60.}
\fwendmacronotes
\fwendmacro
Check the target context for corruptions. There is no point doing a \fwlit{longjmp}
to a corrupted context!
\fwbeginmacro
\fwmacroname{Bomb if the target context is corrupted}{65}\fwequals \fwodef \fwbtx[
if ((p_curr->_ex_mag1 != _EX_MAG1) || (p_curr->_ex_mag2 != _EX_MAG2))
{
as_wl("_exrai: Target exception context has been corrupted. This could");
as_wl(" be because an exception context wasn't popped, or it");
as_wl(" could be just a common garden-variety C corruption :-)");
ex_bomb();
}]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 60.}
\fwendmacronotes
\fwendmacro
Actually raising the exception is the easy part! All that is required is a
\fwlit{longjmp}.
\fwbeginmacro
\fwmacroname{Raise the exception}{66}\fwequals \fwodef \fwbtx[longjmp(p_curr->_ex_jmbf,NON_ZERO);]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 60.}
\fwendmacronotes
\fwendmacro
\fwsecc{4.8.2}{Function ex\_bomb}
The function \fwlit{ex\_bomb} writes out a description of the most recently
raised exception and then bombs the program. This function is used by
\fwlit{\_exrai}.
\fwbeginmacro
\fwmacroname{Function ex\_bomb}{67}\fwequals \fwodef \fwbtx[
LOCAL void ex_bomb P_((void));
LOCAL void ex_bomb ()
{
char s[100];
as_wr(" Exception desc is : \""); as_wr(EX_ID); as_wl("\".");
as_wr(" Exception id is : ");
sprintf(s,"%lu (= %lX)",ULONG(EX_ID),ULONG(EX_ID)); as_wl(s);
as_wr(" Exception info is : ");
sprintf(s,"%lu (= %lX)",ULONG(EX_INFO),ULONG(EX_INFO)); as_wl(s);
as_bomb("Aborting program after exception error.");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 31.}
\fwendmacronotes
\fwendmacro
\fwsecc{4.8.3}{Function \_expop}
The function \fwlit{\_expop} pops the current exception block from the context
stack. Before doing this, it performs a few checks. To assist it with its
checks, the caller must pass a pointer to the current context record
in the first parameter. This can always be done by the caller directly
(without referencing \fwlit{\_EX\_CURR}) because popping is only supposed to take
place within the static context of an enclosing exception block.
\fwbeginmacro
\fwmacroname{Function spec \_expop}{68}\fwequals \fwodef \fwbtx[EXPORT void _expop P_((_ex_cx_t *));]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 33.}
\fwendmacronotes
\fwendmacro
\fwbeginmacro
\fwmacroname{Function \_expop}{69}\fwequals \fwodef \fwbtx[
EXPORT void _expop (p_check)
_ex_cx_t *p_check;
{
#if _EX_FAST
as_bomb("_exrai: This function should not be called with _EX_FAST==TRUE.");
#else
]fwetx=%
\fwmacroname{Take a copy of \_EX\_CURR in p\_curr}{61}\fwbtx[
]fwetx=%
\fwmacroname{Bomb if the current context is not the top of stack context}{70}\fwbtx[
]fwetx=%
\fwmacroname{Bomb if current context is corrupted}{71}\fwbtx[
]fwetx=%
\fwmacroname{Zap the current context}{72}\fwbtx[
]fwetx=%
\fwmacroname{Pop the context}{73}\fwbtx[
#endif
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 31.}
\fwendmacronotes
\fwendmacro
The first check test is to make sure that the context about to be popped is
the current context --- the one statically enclosing the pop call.
\fwbeginmacro
\fwmacroname{Bomb if the current context is not the top of stack context}{70}\fwequals \fwodef \fwbtx[
if (p_curr != p_check)
{
if (p_curr == NULL)
as_bomb("_expop: Context stack is empty.");
else
as_bomb("_expop: Top of context stack is not the current context.");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 69.}
\fwendmacronotes
\fwendmacro
The second check tests the current context (the one about to be popped) to
make sure that it isn't corrupted. If it is, then the program should be
bombed.
\fwbeginmacro
\fwmacroname{Bomb if current context is corrupted}{71}\fwequals \fwodef \fwbtx[
if ((p_curr->_ex_mag1 != _EX_MAG1) || (p_curr->_ex_mag2 != _EX_MAG2))
{
as_wl("_ex_rai: Target exception context has been corrupted. This could");
as_wl(" be because an exception context wasn't popped, or it");
as_wl(" could be just a common garden-variety C corruption :-)");
as_bomb("Aborting program after exception error.");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 69.}
\fwendmacronotes
\fwendmacro
Before popping the current context, we need to zap its magic numbers so that
they don't float around in stack memory where they might cause some future
magic number test to succeed when it should have failed.
\fwbeginmacro
\fwmacroname{Zap the current context}{72}\fwequals \fwodef \fwbtx[
p_curr->_ex_mag1 = ~_EX_MAG1;
p_curr->_ex_mag2 = ~_EX_MAG2;
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 69.}
\fwendmacronotes
\fwendmacro
Having checked and zapped the context, popping it is easy!
\fwbeginmacro
\fwmacroname{Pop the context}{73}\fwequals \fwodef \fwbtx[_EX_CURR = p_curr->_ex_prev;]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 69.}
\fwendmacronotes
\fwendmacro
\fwseca{5}{Test Program}
This section provides a test program which can be used to test the exceptions
package. The program provides nine success tests and nine failure tests.
The success tests do not cause the program to terminate, and so can all
be performed in a single run. However, each failure test causes the
program to bomb with an assertion error, and so the failure tests must
be performed separately as nine separate invocations of the test
program. At the end of this testing, the user can have a high degree of
confidence that the package is working.
Each of the eighteen tests is embodied in a separate function.
The global boolean variable \fwlit{flag} is a miscellaneous temporary variable
which is usually used to tell if an exception has fired.
\fwbeginmacro
\fwmacroname{Test program file}{74}\fwequals \fwodef \fwbtx[
]fwetx=%
\fwmacroname{Configuration header notice}{4}\fwbtx[
#include "style.h"
#include "except.h"
#include "as.h"
EX_LOCAL(sloth_ex,"Sloth exception");
EX_LOCAL(walrus_ex,"Walrus exception");
GLOVAR bool flag;
]fwetx=%
\fwmacroname{Function sc01}{75}\fwbtx[
]fwetx=%
\fwmacroname{Function sc02}{76}\fwbtx[
]fwetx=%
\fwmacroname{Function sc03}{77}\fwbtx[
]fwetx=%
\fwmacroname{Function sc04}{78}\fwbtx[
]fwetx=%
\fwmacroname{Function sc05}{79}\fwbtx[
]fwetx=%
\fwmacroname{Function sc06}{80}\fwbtx[
]fwetx=%
\fwmacroname{Function sc07}{81}\fwbtx[
]fwetx=%
\fwmacroname{Function sc08}{82}\fwbtx[
]fwetx=%
\fwmacroname{Function sc09}{83}\fwbtx[
]fwetx=%
\fwmacroname{Function fa01}{84}\fwbtx[
]fwetx=%
\fwmacroname{Function fa02}{85}\fwbtx[
]fwetx=%
\fwmacroname{Function fa03}{86}\fwbtx[
]fwetx=%
\fwmacroname{Function fa04}{87}\fwbtx[
]fwetx=%
\fwmacroname{Function fa05}{88}\fwbtx[
]fwetx=%
\fwmacroname{Function fa06}{89}\fwbtx[
]fwetx=%
\fwmacroname{Function fa07}{90}\fwbtx[
]fwetx=%
\fwmacroname{Function fa08}{91}\fwbtx[
]fwetx=%
\fwmacroname{Function fa09}{92}\fwbtx[
main()
{
char ch;
printf("Test Program for the EXCEPT Package\n");
printf("===================================\n");
printf("This test program provides a number of different tests. Because\n");
printf("some tests provoke the package to bomb on purpose, this test\n");
printf("program must be run a number of times, once for each test.\n");
printf("The 0 test performs a group of tests that are not supposed to\n");
printf("fail. The 1..9 tests each perform a test that is supposed to\n");
printf("bomb the program if the test succeeds. The 0 test should be run\n");
printf("with the package compiled with both _EX_FAST==FALSE and\n");
printf("_EX_FAST==TRUE. The 1..9 tests should use only _EX_FAST==FALSE.\n");
printf("\n");
if (_EX_FAST)
printf("Current value of EX_FAST == TRUE.\n");
else
printf("Current value of EX_FAST == FALSE.\n");
printf("\n");
printf("Enter 0 for success tests, or 1..9 for one of nine fail tests>");
ch=getchar(); printf("\n");
if (ch == '0')
{
printf("Success Tests\n");
printf("-------------\n");
printf("The following tests (sc01..sc09) test the normal features\n");
printf("the exceptions package. You should see nine success lines\n");
printf("appear on the screen. If the package bombs during this test,\n");
printf("then something is wrong and should be fixed.\n");
printf("\n");
sc01();
sc02();
sc03();
sc04();
sc05();
sc06();
sc07();
sc08();
sc09();
printf("\n");
printf("All of the success tests SUCCEEDED.\n");
}
else
{
if (_EX_FAST)
{
as_wl("Error in test configuration. An attempt was made to perform");
as_wl("a 1..9 test with _EX_FAST==TRUE. This does not make sense as");
as_wl("these tests test the error checking capability of the package");
as_wl("and _EX_FAST=TRUE has all error checking turned off!");
as_bomb("Please recompile with _EX_FAST==FALSE and try again.");
}
switch (ch)
{
case '1': fa01();
case '2': fa02();
case '3': fa03();
case '4': fa04();
case '5': fa05();
case '6': fa06();
case '7': fa07();
case '8': fa08();
case '9': fa09();
default:
printf("\nCharacter entered was not 0..9. Aborting...\n");
}
}
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 3.}
\fwendmacronotes
\fwendmacro
\fwsecb{5.1}{Function sc01} This test checks to make sure that exception blocks operate correctly
when no exceptions are raised.
\fwbeginmacro
\fwmacroname{Function sc01}{75}\fwequals \fwodef \fwbtx[
LOCAL void sc01 P_((void));
LOCAL void sc01 ()
{
printf("Test SC01: Exception block with no exceptions raised.\n");
/* The NULL exception block should be OK. */
EX_BEGIN
EX_FORGET
EX_END
/* Now put some code in the normal section. */
flag=FALSE;
EX_BEGIN
flag=TRUE;
EX_FORGET
EX_END
as_cold(flag,"sc01: First flag block test failed.");
/* Make sure the EX_OTHERS branch only applies to exceptions. */
flag=FALSE;
EX_BEGIN
flag=TRUE;
EX_FORGET
EX_OTHERS
as_bomb("sc01: Raise failed.");
EX_END
as_cold(flag,"sc01: Second flag block test failed.");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 74.}
\fwendmacronotes
\fwendmacro
\fwsecb{5.2}{Function sc02} This test tests to make sure that an exception raised within an exception
block can be caught by that block.
\fwbeginmacro
\fwmacroname{Function sc02}{76}\fwequals \fwodef \fwbtx[
LOCAL void sc02 P_((void));
LOCAL void sc02 ()
{
printf("Test SC02: Exception caught within immediate block.\n");
flag = FALSE;
EX_BEGIN
EX_RAISE(sloth_ex);
as_bomb("sc02: Raise failed.");
EX_FORGET
EX_WHEN(sloth_ex) flag=TRUE;
EX_END
as_cold(flag,"sc02: Exception was not caught by local block.");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 74.}
\fwendmacronotes
\fwendmacro
\fwsecb{5.3}{Function sc03} This test tests the \fwlit{EX\_OTHERS} feature.
\fwbeginmacro
\fwmacroname{Function sc03}{77}\fwequals \fwodef \fwbtx[
LOCAL void sc03 P_((void));
LOCAL void sc03 ()
{
printf("Test SC03: Exception is caught by OTHERS clause.\n");
flag = FALSE;
EX_BEGIN
EX_RAISE(sloth_ex);
as_bomb("sc03: Raise failed.");
EX_FORGET
EX_OTHERS flag=TRUE;
EX_END
as_cold(flag,"sc03: Exception was not caught by EX_OTHERS clause.");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 74.}
\fwendmacronotes
\fwendmacro
\fwsecb{5.4}{Function sc04} This test tests nested exception blocks.
\fwbeginmacro
\fwmacroname{Function sc04}{78}\fwequals \fwodef \fwbtx[
LOCAL void sc04 P_((void));
LOCAL void sc04 ()
{
printf("Test SC04: Exception is caught by nested exception block.\n");
/* Try a simple nested handler. */
flag=FALSE;
EX_BEGIN
EX_BEGIN
EX_RAISE(sloth_ex);
as_bomb("sc04: Raise failed (1).");
EX_FORGET
EX_WHEN(walrus_ex)
as_bomb("sc04: Walrus exception caught (1).");
EX_END
EX_FORGET
EX_WHEN(sloth_ex) flag=TRUE;
EX_WHEN(walrus_ex) as_bomb("sc04: Walrus exception caught (x).");
EX_END
as_cold(flag,"sc04: First nested test failed.");
/* Test a re-raise. */
flag=FALSE;
EX_BEGIN
EX_BEGIN
EX_RAISE(sloth_ex);
as_bomb("sc04: Raise failed (2).");
EX_FORGET
EX_WHEN(sloth_ex)
EX_RAISE(walrus_ex)
as_bomb("sc04: Raise failed (3).");
EX_WHEN(walrus_ex)
as_bomb("sc04: Walrus exception caught (2).");
EX_END
EX_FORGET
EX_WHEN(sloth_ex)
as_bomb("sc04: Sloth exception escaped.");
EX_WHEN(walrus_ex)
flag=TRUE;
EX_END
as_cold(flag,"sc04: Second nested test failed.");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 74.}
\fwendmacronotes
\fwendmacro
\fwsecb{5.5}{Function sc05} This test tests whether \fwlit{EX\_OTHERS} can successfully re-raise an
exception.
\fwbeginmacro
\fwmacroname{Function sc05}{79}\fwequals \fwodef \fwbtx[
LOCAL void sc05 P_((void));
LOCAL void sc05 ()
{
STAVAR bool flag2;
printf("Test SC05: Single OTHERS handler reraises current exception.\n");
flag=FALSE;
flag2=FALSE;
EX_BEGIN
EX_BEGIN
EX_RAISE(sloth_ex);
as_bomb("sc05: Raise failed (2).");
EX_FORGET
EX_OTHERS
flag=TRUE;
EX_RAISE(EX_ID);
as_bomb("sc05: Raise failed (3).");
EX_END
EX_FORGET
EX_WHEN(sloth_ex)
flag2=TRUE;
EX_OTHERS
as_bomb("sc05: Exception escaped.");
EX_END
as_cold(flag ,"sc05: OTHERS test failed (1).");
as_cold(flag2,"sc05: OTHERS test failed (2).");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 74.}
\fwendmacronotes
\fwendmacro
\fwsecb{5.6}{Function sc06}
\fwbeginmacro
\fwmacroname{Function sc06}{80}\fwequals \fwodef \fwbtx[
LOCAL void sc06 P_((void));
LOCAL void sc06 ()
{
printf("Test SC06: Handlers all GOTO same code.\n");
flag=FALSE;
EX_BEGIN
EX_RAISE(sloth_ex);
as_bomb("sc06: Raise failed.");
EX_FORGET
EX_WHEN(sloth_ex) goto handle;
EX_WHEN(walrus_ex)
as_bomb("sc06: Walrus exception went off.");
handle:
flag=TRUE;
EX_OTHERS
as_bomb("sc06: Exception escaped to OTHERS.");
EX_END
as_cold(flag,"sc06: GOTO test failed.");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 74.}
\fwendmacronotes
\fwendmacro
\fwsecb{5.7}{Function sc07} This test simply checks that EX\_INFO can be written to and read from.
\fwbeginmacro
\fwmacroname{Function sc07}{81}\fwequals \fwodef \fwbtx[
LOCAL void sc07 P_((void));
LOCAL void sc07 ()
{
printf("Test SC07: Exercise EX_INFO.\n");
EX_INFO = 3;
as_cold(EX_INFO == 3,"sc07: EX_INFO test failed.");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 74.}
\fwendmacronotes
\fwendmacro
\fwsecb{5.8}{Function sc08} This test simply checks that the \fwlit{ex\_str} function is working.
\fwbeginmacro
\fwmacroname{Function sc08}{82}\fwequals \fwodef \fwbtx[
LOCAL void sc08 P_((void));
LOCAL void sc08 ()
{
printf("Test SC08: Exercise ex_str.\n");
as_cold(ex_str(sloth_ex) == sloth_ex,"sc08: Failed.");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 74.}
\fwendmacronotes
\fwendmacro
\fwsecb{5.9}{Function sc09} Test the legitimate use of \fwlit{EX\_POP}.
\fwbeginmacro
\fwmacroname{Function sc09}{83}\fwequals \fwodef \fwbtx[
LOCAL void sc09 P_((void));
LOCAL void sc09 ()
{
printf("Test SC09: Test EX_POP.\n");
flag=FALSE;
EX_BEGIN
EX_BEGIN
EX_POP; goto finish;
EX_FORGET
EX_WHEN(sloth_ex) as_bomb("sc09: Inner catcher caught the sloth.");
EX_END
finish: EX_RAISE(sloth_ex);
EX_FORGET
EX_WHEN(sloth_ex) flag=TRUE;
EX_END
as_cold(flag,"sc09: Sloth exception failed to work.");
as_cold(_EX_CURR == NULL,"sc09: EX_POP failed to pop correctly.");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 74.}
\fwendmacronotes
\fwendmacro
\fwsecb{5.10}{Function fa01} This test tests that an unhandled exception will be properly caught.
\fwbeginmacro
\fwmacroname{Function fa01}{84}\fwequals \fwodef \fwbtx[
LOCAL void fa01 P_((void));
LOCAL void fa01 ()
{
printf("Test FA01: Unhandled Exception\n");
printf("------------------------------\n");
printf("This test raises the sloth exception but doesn't handle it.\n");
printf("The result should be an \"unhandled exception\" bomb specifying\n");
printf("the sloth exception. Here we go...\n");
printf("\n");
EX_RAISE(sloth_ex);
as_wl ("FA01 FAILED: the exception package did not catch the\n");
as_bomb("unhandled sloth exception.\n");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 74.}
\fwendmacronotes
\fwendmacro
\fwsecb{5.11}{Function fa02} This test tests two calls to \fwlit{EX\_POP}.
\fwbeginmacro
\fwmacroname{Function fa02}{85}\fwequals \fwodef \fwbtx[
LOCAL void fa02 P_((void));
LOCAL void fa02 ()
{
printf("Test FA02: Double EX_POP\n");
printf("------------------------\n");
printf("This test executes two calls to EX_POP within the normal code of\n");
printf("an exception block. The second one should cause a\n");
printf("\"context stack is empty\" bomb. Here we go...\n");
printf("\n");
EX_BEGIN
EX_POP;
EX_POP;
as_bomb("FA02 FAILED: Second EX_POP did not cause bomb (1).");
EX_FORGET
EX_END
as_bomb("FA02 FAILED: Second EX_POP did not cause bomb (2).");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 74.}
\fwendmacronotes
\fwendmacro
\fwsecb{5.12}{Function fa03} This test tests a single call to \fwlit{EX\_POP} followed by a normal
termination of the normal code.
\fwbeginmacro
\fwmacroname{Function fa03}{86}\fwequals \fwodef \fwbtx[
LOCAL void fa03 P_((void));
LOCAL void fa03 ()
{
printf("Test FA03: EX_POP Followed by Normal Termination\n");
printf("------------------------------------------------\n");
printf("This test executes a call to EX_POP then falls through to the\n");
printf("end of the normal code of the exception block. The result should\n");
printf("be a \"context stack is empty\" bomb. Here we go...\n");
printf("\n");
EX_BEGIN
EX_POP;
EX_FORGET
EX_END
as_bomb("FA03 FAILED: Fallthough after EX_POP did not cause bomb.");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 74.}
\fwendmacronotes
\fwendmacro
\fwsecb{5.13}{Function fa04} This test checks to make sure that a context can detect when it is popping
the wrong context.
\fwbeginmacro
\fwmacroname{Function fa04}{87}\fwequals \fwodef \fwbtx[
LOCAL void fa04 P_((void));
LOCAL void fa04 ()
{
printf("Test FA04: Exit a block without EX_POPing\n");
printf("-----------------------------------------\n");
printf("This test exits an exception block using a goto without first\n");
printf("EX_POPing. The exception block is nested within another, and this\n");
printf("test is to make sure that the outer block detects that it is\n");
printf("popping the wrong context. The result should be a\n");
printf("\"not current context\" bomb. Here we go...\n");
printf("\n");
EX_BEGIN
EX_BEGIN
goto there;
EX_FORGET
EX_END
there:
EX_FORGET
EX_END
as_bomb("FA04 FAILED: Outer block FAILED to detect stack misalignment.");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 74.}
\fwendmacronotes
\fwendmacro
\fwsecb{5.14}{Function fa05} This test tests for the detection of an unpopped invalid stack context.
\fwbeginmacro
\fwmacroname{Function fa05}{88}\fwequals \fwodef \fwbtx[
LOCAL void fa05_x P_((void));
LOCAL void fa05_x ()
{
EX_BEGIN
return;
EX_FORGET
EX_END
}
LOCAL void fa05 P_((void));
LOCAL void fa05 ()
{
printf("Test FA05: Target Context Does Not Exist\n");
printf("----------------------------------------\n");
printf("This test exits an exception block from within a function using\n");
printf("a return statement. Thus, the context remains stacked even though\n");
printf("the enclosing C block has terminated. This test checks to see\n");
printf("whether this problem is detected next time an exception is raised.\n");
printf("The result should be an illegitimate context error. Here we go...\n");
printf("\n");
EX_BEGIN
fa05_x();
EX_RAISE(sloth_ex);
as_bomb("FA05 FAILED: Failed to raise sloth.");
EX_FORGET
EX_WHEN(sloth_ex)
as_bomb("FA05 FAILED: Sloth exception was raised.");
EX_END
as_bomb("FA05 FAILED: Costruct terminated normally - it shouldn't have.");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 74.}
\fwendmacronotes
\fwendmacro
\fwsecb{5.15}{Function fa06} This test is identical to the previous one except that the test is made
at the point of exit of the outer block, not at the point of raising an
exception.
\fwbeginmacro
\fwmacroname{Function fa06}{89}\fwequals \fwodef \fwbtx[
LOCAL void fa06 P_((void));
LOCAL void fa06 ()
{
printf("Test FA06: Target Context Does Not Exist (Fallthrough)\n");
printf("------------------------------------------------------\n");
printf("This test is identical to test FA05 except that it does not\n");
printf("raise an exception in the outer block. The result should be a\n");
printf("\"not current context\" error. Here we go...\n");
printf("\n");
EX_BEGIN
fa05_x();
EX_FORGET
EX_END
as_bomb("FA06 FAILED: Costruct terminated normally - it shouldn't have.");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 74.}
\fwendmacronotes
\fwendmacro
\fwsecb{5.16}{Function fa07}
\fwbeginmacro
\fwmacroname{Function fa07}{90}\fwequals \fwodef \fwbtx[
LOCAL void fa07 P_((void));
LOCAL void fa07 ()
{
as_bomb("Test FA07: Test FA06 is the last test.");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 74.}
\fwendmacronotes
\fwendmacro
\fwsecb{5.17}{Function fa08}
\fwbeginmacro
\fwmacroname{Function fa08}{91}\fwequals \fwodef \fwbtx[
LOCAL void fa08 P_((void));
LOCAL void fa08 ()
{
as_bomb("Test FA08: Test FA06 is the last test.");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 74.}
\fwendmacronotes
\fwendmacro
\fwsecb{5.18}{Function fa09}
\fwbeginmacro
\fwmacroname{Function fa09}{92}\fwequals \fwodef \fwbtx[
LOCAL void fa09 P_((void));
LOCAL void fa09 ()
{
as_bomb("Test FA09: Test FA06 is the last test.");
}
]fwetx=%
\fwcdef
\fwbeginmacronotes
\fwusedin{This macro is invoked in definition 74.}
\fwendmacronotes
\fwendmacro
\fwseca{6}{Design Notes}
This section contains a detailed design discussion of particular aspects of
this package. Inserting these notes into the implementation section would have
cluttered it up too much.
\fwsecb{6.1}{Architectural Discussion}
The basic engine for exceptions is provided by the ANSI standard \fwlit{setjmp}
and \fwlit{longjmp} primitives which provide simple non-local jumps. The challenge
is organizing them into an exception facility. To see the problems involved
in doing this, we need only focus on an executing program at the time that
an exception is raised. At this time, the exceptions package must locate
the closest handler for the particular exception. From this, two things
become immediately clear: 1) that the exceptions package is going to have to
perform ``work'' whenever an exception block is encountered as well as whenever
an exception is raised, 2) some sort of stacking of \fwlit{jmp\_buf} contexts
is required.
This reasoning suggests two organizations:
1) Maintain a stack for each exception. Whenever an exception block is
entered, push all the handlers for that block on their respective stacks.
Pop them when exiting the handler. If an exception is raised, simply look
on the top of its stack.
2) Maintain a stack of exception blocks. If an exception is raised, search
each block in turn for a matching handler.
Of these two organizations, the second is by far the better. Exceptions are
almost by definition rare events, and so in assessing the two organizations,
the emphasis should be placed on the efficiency of the ordinary case which
is the pushing and popping. The first alternative may require multiple pushes
and pops per exception block, whereas the second requires just one. There
are also a number of more practical reasons why the second alternative is
better.
Having decided that a stack of exception blocks should be maintained, the
next thing to decide is where it should be stored. The first thing that
pops to mind is to organize for the exceptions package to maintain a stack
in a linked list growing out of a global variable. While completely feasible,
this solution is distasteful as it requires a memory allocation and
deallocation per exception block instance. A far better scheme is simply
to store the stack on the run-time stack! This can be done by storing
each node of the stack in an automatic variable within the stack frame
of the function containing the exception block. These automatic variables
can then be threaded together with points to form the originally desired
explicit stack.
\fwsecb{6.2}{Exception Identity}
One interesting aspect of the design is the identity of exceptions.
When the client raises an exception by calling \fwlit{ex\_raise}, an argument
must be provided that in some way identifies the exception being raised.
A number of possible schemes spring to mind:
\fwemp{Integer/preprocessor constant:} Each exception could be mapped
onto a unique integer which would represent the exception. These integers
could be managed by defining preprocessor symbols for them. To raise an
exception, the client would simply pass the preprocessor symbol. This solution
is simple and provides compile-time checking (if the client programmer
mistypes a number, the problem won't be discovered until the exception is
raised and not caught, but if the client mistypes a preprocessor symbol, the
compiler will issue an ``unknown symbol'' error), but it does not provide
any way to ensure that two different exceptions in two different modules
are not using the same number to represent the two different exceptions.
There are organizational solutions to this problem such as having the
programmer maintain a global list of exceptions, but these are generally
unacceptable in the long run. One other solution is to make exception
integers wide (e.g. 32-bits) and have the programmer choose the integer
for each exception by tossing a coin 32 times. The probability of a collision
would be tiny. To be sure that two exceptions were not using the same
number, exceptions could be registered with the exceptions package which
would check the uniqueness of each registered exception integer. Registration
would also allow the exception package to bind a string name to each
exception number.
\fwemp{Integer/variable:} The first solution is clean because it uses
preprocessor symbols. A slightly different solution is to use global variables
instead. Under this scheme, a module exporting an exception exports a global
integer variable that represents the exception. When the package starts
up, it registers the exception with the exception package. Registration in
this case is active, not passive; the exceptions package allocates an
exception number which is assigned to the variable. This solution has the
advantage of guaranteeing that exception numbers are unique. However,
exceptions often happen under strange conditions and it is probably a
disadvantage to have an exception represented by a writeable variable.
\fwemp{Pointer to record:} An alternative to integers is to represent
exceptions by pointers to records describing the exceptions. Each record
could be declared statically (thus ensuring that each record has a unique
pointer) and could contain a description of the exception and possibly
other useful information (e.g. a count of how often the exception has fired).
The pointers to the records could be constants.
\fwemp{Pointer to string:} Pointers to records are OK, but are rather messy
to declare. As it seems that the only information that really needs to be
bound to an exception is a string describing the exception, a far better
more direct scheme is simply to represent exceptions as pointers to strings.
Each exception can be statically declared as a \fwlit{const} pointer to a string.
This solves all the problems: 1) the exception identity is read-only,
2) the exception identity is unique (because each string is stored at
a different address), 3) the exception identity is easy to declare,
4) the exceptions package doesn't have to manage a table of exceptions,
5) the exception variable names are subject to compile-time checking.
The only disadvantages are that 1) the compiler might overlap two constant
strings if one is a non-strict suffix of the other, 2) the exception is
represented by an object that is basically a variable which could be
accidentally written-to if the program goes haywire before the exception is
raised. The \fwlit{const} attribute helps, but may be ignored by many compilers
which will place the variable in writeable memory. These disadvantages are
considered minor compared to the advantages.
Of the solutions given above, the simplest and cleanest is the pointer to
string solution, and this is the scheme that is used in this package. This
is a particularly satisfactory solution because it solves the problem of
exception identity and exception description in one go.
\fwsecb{6.3}{Identifier Names}
Because it contains macros that refer to global variables, this package
exposes the major proportion of its names to the client. As such, particular
care must be taken with names.
To assist portability to brain-dead compilers, all names have generally been
restricted to six characters for external identifiers and eight for internal
identifiers. Upper case has been used for preprocessor symbols and lower case
for other identifiers. Although the use of upper case in the main exception
syntax is a little loud, it is also a continuous reminder that this is a
dangerous construct.
The menemonic for this package is \fwlit{ex} and this appears at the beginning of
every exported symbol. This will assist to reduce symbol collisions.
In addition, any symbol that is not intended to be seen by the client has
been prepended with an underscore. This is a little bit naughty as it
contravenes the ANSI standard's rule 7.13 on identifiers reserved for
libraries (``\fwemp{All identifiers that begin with an underscore are always
reserved for use as identifiers with file scrope in both the ordinary
identifier and tag name spaces}''), but as this package is supposed to be
a library package too, it seems only a minor breach, and completely in keeping
with the style of the ANSI library.
The names of all the exported constructs were chosen very carefully.
Because most of the exported items were macros, by universal convention,
their identifiers had to be in upper case. This requirement was not a
disadvantage though as exception handlers are so important that they ought
to be syntactically ``loud''. Prefixing each symbol with \fwlit{EX\_} is a little
verbose, but was worthwhile because it immediately binds the symbol so
tightly to this package that generic suffixes such as \fwlit{BEGIN} can be used
as a suffix. Thus \fwlit{EX\_BEGIN} and \fwlit{EX\_END} strongly convey their identity
as the boundaries of an anonymous block, but also strongly indicate that the
block is designed to handle exceptions.
As mentioned earlier, the identifier \fwlit{EX\_FORGET} was chosen so as to
continually remind the programmer that, in the event of an exception, all
automatic variables defined within the function containing the exception block
and which have been written since the \fwlit{EX\_BEGIN} of the block, will be
undefined. This is such an important semantic point that it was considered
worthy of being embodied in the syntax too.
The hardest identifier to choose was \fwlit{EX\_RAISE}. For a long time, this
construct was simply to take the form of an exported function with the
name \fwlit{ex\_rai}. However, in the end, it was decided that it would be
better to give this facility the same syntactic feel as the other constructs.
In addition, the raising of an exception is an event that should probably
be syntactically loud. Finally \fwlit{EX\_RAISE} is more readable than \fwlit{ex\_rai}.
\fwsecb{6.4}{Catering for Multi-Threading}
An increasing amount of code is being run in a multi-threading environment
and it is important to ensure that all library packages (such as this one)
are compatible with multi-threading. A key requirement of multi-threading
is that there be no global variables, ar at least that all declarations
and uses of the global variables that do exist have been encapsulated in
macros that can be redefined to work with multi-threading. In this package,
the only global variables are \fwlit{\_ex\_curr}, \fwlit{\_ex\_id}, and \fwlit{\_ex\_info}.
To simplify possible future conversion for use with a multi-threading package,
all uses of these variables have been expressed in terms of the macros
\fwlit{\_EX\_CURR}, \fwlit{\_EX\_ID}, \fwlit{EX\_ID}, and \fwlit{EX\_INFO}.
\fwsecb{6.5}{Checking}
As mentioned before, exceptions are slippery things that can easily go
wrong particularly when the exception facility is hacked together, as it
has here, out of macros and \fwlit{longjmp}s. This section contains a list of
likely errors and failures and explains how the package copes with them.
The errors listed here are caught only in the checking version of this
package. If \fwlit{\_EX\_FAST} then no checking is performed.
\fwemp{Exception has no handler:} This simple case is caught by the \fwlit{\_exrai}
function which will detect a \fwlit{NULL} pointer in \fwlit{\_EX\_CURR} if there
are no more exception blocks to search for handlers.
\fwemp{Client pops the context too often:} This case will occur if the
client calls \fwlit{EX\_POP} and then exits the exception block normally, or
if the client calls \fwlit{EX\_POP} more than once within the same exception
block. Both these errors are detected by the \fwlit{\_expop} function which
tests to ensure that the context it is being asked to pop is the context
enclosing the \fwlit{EX\_POP} call. (Note: \fwlit{EX\_POP} won't compile unless it
is called from within an exception block, so there is no chance of a
context being popped from an external function).
\fwemp{Client exits context without popping it:} This is the single biggest
headache, as there is no way that this error can be detected when it occurs.
There is nothing to stop the client from executing a \fwlit{return} or explicit
\fwlit{longjmp} from within an exception block, and if the client does, the
exception block will still be registered as the top context on the stack
of exception contexts, and control will be transferred to it if an exception
goes off, and if this happens, the program is likely to crash, as the
stack frame containing the exception block no longer exists.
This problem cannot be solved, but it can be attenuated somewhat using
the three-pronged approach adopted in this package:
1) Whenever a context is popped, a check is performed to ensure that the
context being popped is the statically enclosing exception block. If it
isn't, the program bombs. This check means that the stack will be properly
aligned following each non-illegal termination of each exception block.
2) Whenever an exception is raised, a check is performed to ensure that
the address of the context on the top of the context stack is lower than
that of the start of the stack frame of the function raising the exception.
If it is, then it must be an erroneously unpopped context.
3) Corruptions of context records are detected by having two magic number
fields in the record, one at the start of the record and one at the end.
If a record is corrupted, the corruption is likely to be detected the
next time the record is manipulated.
The last two checks do a good job of covering the two main cases that can
arise. When an exception block terminates without its context being popped,
control will often leave the function. If it does, then the top of the
run-time stack will be higher (in address value for stacks that grow down)
than the unpopped context. This is detected by the second test. If, later,
the stack grows again, it is likely to overwrite the unpopped
context, including the magic numbers it contains. If this happens, then the
error will be detected the next time an exception is raised or the stack is
popped.
Obviously, there are ways in which a computer program might satisfy all
these tests and still be erroneous, but at least these tests provide some
sort of safety net.
\fwsecb{6.6}{Future Improvements}
This section gives a list of possible future improvements to this package.
* It might be useful to allow the client to specify a block of code that
is executed after an exception is raised, but before the handlers are scanned.
This would allow an opportunity for cleaning up.
* Rework the terminology so that it is more consistent.
* Hierarchical exceptions.
That's the end of this package!
\bye
%*******************************************************************************
%* END OF AUTOMATICALLY GENERATED TEX FILE *
%*******************************************************************************