palves/cexcept
Folders and files
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Repository files navigation
README for GNU cexcept, an exception and cleanup mechanism for C.
This is GNU cexcept (*), a library heavilly based on GDB's own C
exception mechanism, factored out for reuse on other projects.
Licensed under GPLv3 or (at your option) any later version.
(*) - not an official GNU project, but based on GNU GDB, and if proven
useful, it will aim at being one.
Documentation (extracted from GDB's gdbint manual)
*************
Cleanups are a structured way to deal with things that need to be done later.
When your code does something (e.g., malloc some memory, or open a
file) that needs to be undone later (e.g., free the memory or close
the file), it can make a cleanup. The cleanup will be done at some
future point: when the command is finished and control returns to the
top level; when an error occurs and the stack is unwound; or when your
code decides it's time to explicitly perform cleanups. Alternatively
you can elect to discard the cleanups you created.
Syntax:
struct cleanup *old_chain;
Declare a variable which will hold a cleanup chain handle.
old_chain = make_cleanup (function, arg);
Make a cleanup which will cause function to be called with arg (a
char *) later. The result, old_chain, is a handle that can later
be passed to do_cleanups or discard_cleanups. Unless you are
going to call do_cleanups or discard_cleanups, you can ignore the
result from make_cleanup.
do_cleanups (old_chain);
Do all cleanups added to the chain since the corresponding
make_cleanup call was made.
discard_cleanups (old_chain);
Same as do_cleanups except that it just removes the cleanups from
the chain and does not call the specified functions.
Cleanups are implemented as a chain. The handle returned by
make_cleanups includes the cleanup passed to the call and any later
cleanups appended to the chain (but not yet discarded or
performed). E.g.:
make_cleanup (a, 0);
{
struct cleanup *old = make_cleanup (b, 0);
make_cleanup (c, 0)
...
do_cleanups (old);
}
will call c() and b() but will not call a(). The cleanup that calls
a() will remain in the cleanup chain, and will be done later unless
otherwise discarded.
Your function should explicitly do or discard the cleanups it creates.
Failing to do this leads to non-deterministic behavior since the
caller will arbitrarily do or discard your functions cleanups. This
need leads to two common cleanup styles.
The first style is try/finally. Before it exits, your code-block
calls do_cleanups with the old cleanup chain and thus ensures that
your code-block's cleanups are always performed. For instance, the
following code-segment avoids a memory leak problem (even when error
is called and a forced stack unwind occurs) by ensuring that the xfree
will always be called:
struct cleanup *old = make_cleanup (null_cleanup, 0);
data = xmalloc (sizeof blah);
make_cleanup (xfree, data);
... blah blah ...
do_cleanups (old);
The second style is try/except. Before it exits, your code-block
calls discard_cleanups with the old cleanup chain and thus ensures
that any created cleanups are not performed. For instance, the
following code segment, ensures that the file will be closed but only
if there is an error:
FILE *file = fopen ("afile", "r");
struct cleanup *old = make_cleanup (close_file, file);
... blah blah ...
discard_cleanups (old);
return file;
Some functions, e.g., fputs_filtered() or error(), specify that they
“should not be called when cleanups are not in place”. This means
that any actions you need to reverse in the case of an error or
interruption must be on the cleanup chain before you call these
functions, since they might never return to your code (they `longjmp'
instead).