Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

147 lines (110 sloc) 5.167 kB

Exceptions - Exception mechanism for C

Motivation

When following a strict error checking hygiene (as should be the case), the code can soon be overtaken by error checking code. The problem being that error codes need to be checked at each level in the call stack. An exception mechanism allows to circumvent this limitation.

Features

  • Full try/catch/finally structure.
  • Arbitrary nesting of try statements, even inside the catch and finally clauses.
  • Use arbitrary data structures as exception data.
  • Supply your own logic to determine if an exception was caught.
  • Relies only on the C standard library, no low-level tricks.

Description

The implementation is based on the standard library functions setjmp() and longjmp(). To each try statement corresponds a datastructure. Nested try statement form a linked list, whose head is available globally trough a function.

The implementation assumes malloc() never fails.

Performance

A try statement saves about 84 bytes of data on the stack on my test system, among which 64 bytes for the setjmp() environement. It may not be wise use try statements in performance-critical code. Simply put your try statements higher up the call hierarchy and you should be fine.

Reserved Keywords

The library reserves the use of the identifiers throw, try, catch, finally, endtry and all identifiers starting by Ex_. Inside try statements, the identifier EX is reserved.

Conventions

  • {X} means "one or more X"
  • [X] means "zero or one X"
  • <X> means "something of nature X"

Interface

throw(void *ptr);

Throw a new exception. The exception data is pointed to by ptr. Throwing an exception registers the exceptions then jumps to the end of innermost try clause. If there is no such clause, a warning is printed on stderr and the program exits.

try <try_clause>
{catch(<catch_expr>) <catch_clause>}
[finally <finally_clause>]
endtry

The whole construct is called a "try statement".

<try_clause>, <catch_clause> and <finally_clause> are any statement that would be appropriate after if (true), excepted those who can break the flow of the program: return, break, continue, goto, setjmp() or longjmp().

You can however use break and continue, but only if they are relative to loops contained within a clause.

A <catch_expr> is an expression that can make use of the variable EX, which is the thrown exception (the parameter to throw(), of type void *). The expression should evaluate to true if and only if EX should be considered as caught (triggering the execution of the associated <catch_clause>).

EX can also be used in catch clauses.

The try clause is executed. If at some point during its execution an exception is thrown, the execution jumps to its end.

If there are any, the first <catch_expr> is evaluated. If it evaluates to true, the associated <catch_clause> is executed. If not, we go to next catch expression. We repeat this process until either we have run out of catch expressions, or we have entered a catch clause.

Whether an exception was thrown or not, and whether a thrown exception was caught or not, <finally_clause> is executed if present. Then, if the exception was not caught, it is re-thrown so that it may be caught by an enclosing try statement or cause an error if no enclosing try statement exists.

If an exception happens inside a catch expression or catch clause, the finally clause is executed and then the exception is re-thrown. If an exception happens inside a finally clause, it is re-thrown.

Ex_ctx *    (*Ex_ctx_get)   ();
void        (*Ex_ctx_set)   (Ex_ctx *);

These function pointers are used by the exception mechanism to access and set the context corresponding to the innermost try statement. If you use exceptions in a single-threaded program, you don't need to modify them. But if you want to use exceptions in more than one thread of a multi-threaded program, then you need to supply suitable replacements. We suggest using thread-local storage to store one context per thread.

Important Notice

Since the exceptions mechanism is based on setjmp(), any variable local to a function containing a try statement must be declared volatile if there is any chance that the value of the variable might have changed between the time the try statement was entered and the time an exception was thrown.

Toy Example

static struc my_exception_type foo, bar, baz;

void func1() {
    int result = some_function();

    if (is_foo(result)) {
        throw(&foo_exception);
    }
    else if (is_bar(result)) {
        throw(&bar_exception);
    }
    else {
        some_other_function();
    }
}

void func2() {
    try {
        func1();
    }
    catch(EX == &foo) {
        cleanup_foo();
    }
    catch(EX == &bar) {
        cleanup_bar();
    }
    finally {
        cleanup();
    } endtry
}

Implementation

See files exceptions.h and exceptions.c.

Jump to Line
Something went wrong with that request. Please try again.