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.