General Exception-Handling Facility

for

The C Programming Language

featuring

Design by Contract

by

Bruce W. Bigby


GEF (pronounced Jeff) is an independent general-purpose error-handling facility for the C programming language that exists in the form of a C library.  The facility defines a collection of macros that support exception-handling blocks like those that one may find in C++ code and Java -- try, catch, etc.  However, the semantics of GEF are more consistent with the exception-handling facility of the Eiffel programming language.  As such, GEF also promotes programming by contract with its gef_invariants, gef_preconditions, gef_postconditions, and gef_assert blocks and statements.  To view a real program that illustrates the use of exception-handling with GEF, see

add.c
square_root.c
square_root2.c
gef_test.c

As time progresses, I will update GEF with more complete documentation.  For now, refer to the November 1998 issue of Dr. Dobb's for a description of GEF.

The latest version is available for the Fedora Core 4, Sun Solaris 2.x systems, and DOS.  The UNIX tar file contains a library, which is compatible with Posix Pthreads and is copyrighted under the GNU Library General Public License.  You may reach the author by e-mail at bbigby@alum.MIT.edu.

I would appreciate any feedback that you may have on this library.  I will try to incorporate changes and publish releases as soon as I am able.  I hope that the library becomes useful to many.  For references on Design by Contract, see http://www.eiffel.com.  Enjoy.
 

Caveats

When your code is executing from within a GEF block, DO NOT execute any statements, or functions, that will cause control to pass over gef_end, such as return.  You may only use return when the statement is not in the scope of a GEF block.

You must also be careful that you do not use the standard C break statement in such a way that it causes control in your program to pass over any GEF statement, such as gef_catch, gef_end, etc.  This will cause GEF's stack to get out of sync with your program's stack.

Follow these rules and you, and your program, will be very happy!
 

UNIX: Linux, Solaris, and Other Unix OS'es

The source only currently supports Linux 5.2, 6.x, and Solaris 2.x. However, if you believe that your system is source-compatible with Solaris, you may build GEF, using PLATFORM=solaris and ARCH=ultra.

RPM Installation

If you are installing via RPM, beginning with version 7.0.0, different versions of GEF-client RPMS may coexist.  However, when you attempt to install multiple client packages, RPM may warn you about the existence of another client RPM and refuse to install the package.  If this happens, use the '--force' option to force the install.  However, if you plan to develop applications that use GEF, you must install the corresponding version of the development RPM and each development package requires the presence of the corresponding client package.  For example, if you want to develop with version, 8.0.0, of GEF, you need to install GEF-client-8.0.0-1.i386.rpm and GEF-devel-8.0.0-1.i386.rpm.  If you wish only to use an application that uses the 8.0.0 version of GEF, then you need only install the 8.0.0 client RPM of GEF.

PGP Public Key

Typically, I sign my RPM packages with my private key. To verify that signature, you may use my GPG public key, which is as follows:

GPG Public Key

GEF 9.0.1

This release adds support for building on Solaris x86. For Solaris, the choices for ARCH are "ultra2" or "pentium4".

Here are the source packages:

GEF-archive-9.0.1.tar.gz
GEF-client-9.0.1-1.src.rpm
GEF-devel-9.0.1-1.src.rpm

Here are the binary packages:

GEF-client-9.0.1-1.i686.rpm
GEF-devel-9.0.1-1.i686.rpm

GEF 9.0.0

This release modifies the assertion macros so that they provide assertion expression information to the default and user-specific assertion-violation call-back functions. This change is an API change -- thus, the reason for the major version number change.

GEF 8.4.0

This release adds a new command-line switch, "--gef-abort-on-assertion-violation={true|false}", which causes GEF to abort the program at the first assertion violation. For Posix systems, abort means that GEF invokes the system call, abort(). For non-Posix systems, GEF calls exit().

Here are the source packages:

GEF-archive-8.4.0.tar.gz
GEF-client-8.4.0-1.src.rpm
GEF-devel-8.4.0-1.src.rpm

Here are the binary packages:

GEF-client-8.4.0-1.i686.rpm
GEF-devel-8.4.0-1.i686.rpm

GEF 8.3.1

This release fixes the following user-visible bugs:
(1) Further optimized the regular gef_try macros when the NDEBUG macro exists.
(2) Make gef_break and gef_retry macros throw an exception when NDEBUG macro exists and the corresponding try macro does not possess the "volatile" or "posix" keyword.
(3) Added an exception macro, called GEF_EXCEPTION_LAST to indicate the identifier for the last GEF-specific exception. A developer can add additional exceptions to his or her program that do not conflict with GEF's exceptions.

GEF 8.2.3

This release fixes the following user-visible bugs:
(1) The Solaris client library contained a partially-qualified library symbolic link. I thought I had moved all of them to the devel library. I think that I got the last one.
(2) The macro, gef_posix_repeat_try(), did not properly enable async support. This macro would also cause a compiler error. It is fixed now.

GEF 8.2.2

This release fixes a bug where the try macros, such as gef_volatile_try_with_preconditions, would override the command-line assertion level, such as --gef-postconditions=true. If the assertion level is already at postconditions or invariants, then the macro, above, should not set the assertion level to preconditions. The macro should only change the level when the current level is less than preconditions -- e.g. none. This release also fixes the same bugs that relate to the macros, gef_volatile_try_with_postconditions, and gef_volatile_try_with_invariants.

GEF 8.2.1

This release moves the symbolic link files, such as libgef_ansi.so.8 from the client package to the development package. Now, the client package only has fully-qualified library versions or links.

GEF 8.2.0

Mostly, this release contains primarily 5 changes. Each involves the modification of GEF keyword macros. Basically, any keyword that contained "try_preconditions" has become "try_with_preconditions". Any keyword that contained "try_postconditions" has become "try_with_postconditions". Any keyword that contained "try_invariants" has become "try_with_invariants". Also, any keyword that contained the word, "except", now contains "without". The last change involved changing all occurrences of "finally" to "finalize". For example, the keyword, "gef_try_preconditions_except_finally", has become "gef_try_with_preconditions_without_finalize". Although all of the old keyword macros work, you cannot count on them to work in the next major release.

GEF 8.1.0

Changed the qualifier in macros, such as gef_always_try, to "gef_volatile_try". The qualifier, "volatile", better expresses the meaning that I intended. It prevents the compiler from removing the exception-handling instrumentation when the NDEBUG flag exists.

GEF 8.0.6

Fixed a problem where the libgef_pthreads.so and libgef_posix.so libraries would not contain all of the relevant GEF functions under Solaris.

GEF 8.0.5

Fixed a defect where GEF exits because of a seemingly illegal state transition. Basically, if you set the assertion checking level to postconditions, the following code would cause GEF to exit with an error:

gef_try { /* Some code */ } gef_end;

Basically, the state of transitioning from gef_finally to gef_postconditions was coded as illegal when it should have been legal. This problem only occurs when the assertion-checking level is "postconditions". This bug appeared in the 8.0.1 version of GEF.

Version 8.0.5 of GEF also fixes a bug in the macro function, "gef_always_assert()". The assertion macro would only operate when the level of assertion checking was "preconditions". It should operate regardless of the level of assertion checking -- even a level of none.

GEF 8.0.1

Modified GEF so that it executes each preconditions block before the first pass of each invariants block and executes the second pass of each invariants block before each postconditions block -- not vice-versa. Versions 8.0.0 and prior execute the first pass of each invariants block before each preconditions block and execute each postconditions block before the second pass of each invariants block. The new order makes more sense in that the minimal requirements for executing a function are its preconditions -- not its invariants. The typical order, presuming no errors, is (1) preconditions, (2) first pass of invariants, (3) try, (4) finally, (5) second pass of invariants, (6) postconditions.

GEF 8.0.0

Modified GEF so that the assertion macros produce the function name. This change required an interface change to the supporting assertion functions so I had to increase the major version number.

GEF 7.0.0

Modified GEF so that it treats its first pass of the gef_invariants block distinctly from its second pass.  This helps the programmer identify whether an invariant was broken on entrance to a routine or whether it was broken on the way out of the routine.  As such, the programmer can now specify a function that GEF will call whenever an invariant violation occurs during the first pass of a gef_invariants block; the programmer may also specify another call-back function that GEF will call whenever an invariant violation occurs during the second pass of a gef_invariants block.

I also fixed a few bugs related to memory release, although the problem is minor, since GEF exception-handling stack exists for the life-time of the process.

Reduced the initial size of GEF's exception-handling stack to 64.  As GEF needs additional space, it will double the size of its stack until it can't anymore.

Modified the behavior of GEF so that any exceptions, that occur within gef_finally, gef_postconditions, and the second pass of gef_invariants, pass control to the gef_catch block.  After that, GEF will, by default, throw the exception to the exception-handler that is the parent of the currently executing exception-handler.  This enables the programmer to release any resources that, for example, a function may have been attempting to return to its caller.  The gef_finally block would not account for these types of resources.  Previous versions of GEF would simply pass control to the parent exception-handler, if any.  Note: When a program reaches the gef_catch block from either the gef_finally, gef_postconditions, or the second pass of the gef_invariants, block GEF does not subsequently pass control to the gef_finally block.  Instead, GEF passes the exception to the parent exception-handler.

If an exception occurs within the scope of a gef_catch block, GEF transfers control back to the gef_catch block in a loop fashion.  The programmer must make sure that there is a way to get out of the gef_catch block without looping indefinitely.

Notes

GEF supports three separate libraries:
 
  • gef_ansi - A basic ANSI version of GEF
  • gef_posix - ANSI + UNIX signal support--e.g., to catch SIGFPE (floating point exceptions).
  • gef_pthreads - ANSI + UNIX support + pthreads support.
  • In addition, the sources will enable you to build 2 different variants: debug and opt.

    To make GEF,

    cd GEF
    make OS=<linux|solaris> ARCH=<i386|i486|i586|ultra>VARIANT=product distribution

    To create the various test programs,

    cd test
    make OS=<linux|solaris> ARCH=<i386|i486|i586|ultra> all

    The makefiles will deposit the test executables in the following directories:

    GEF/test/bin/<platform>/ansi/<variant>
    GEF/test/bin/<platform>/posix/<variant>
    GEF/test/bin/<platform>/pthreads/<variant>

    cd to one of the variant subdirectories, and update your LD_LIBRARY_PATH="../../../../../dist/lib". Then run one of the test programs.

    To clean everything:

    cd GEF
    make clean
    cd test
    make clean
    cd ..

    Enjoy!
     

    This page was last updated on Monday, May 22, 2006.