A Proposal for Exception Handling in C

Table of Contents

Preamble

Introduction

Presentation

Cleaning Up

Syntax

Example

Performance

Border Conditions

Simple Intra-function Exception Handling

Complex Intra-function Exception Handling

Inter-function Exception Handling

Disadvantages

Runtime Library Support

Alternatives

Future

Stack Traces

Exception Aware CPU

Summary

Epilogue

Implementations

Questions

Appendix A: Simple Example

History

v1.00

v1.02

v1.03 (2009.08.30)

v1.04 (2009.08.31)

v1.05 (2009.09.01)

v1.06 (2009.09.03)

v1.07 (2009.09.04)

v1.08 (2009.09.11)

v1.09 (2009.09.13)

v1.10 (2010.09.01)

v1.20 (2010.09.07)

v1.21 (2010.09.07)

v1.22 (2010.09.08)

v1.25 (2010.09.10)

v1.26 (2010.09.27)

v1.30 (2010.10.15)

v1.50 (2010.10.15)

v1.51 (2010.10.15)

v1.52 (2010.10.15)

v1.53 (2010.10.18)

v1.54 (2010.10.18)

v1.55 (2010.12.10)

v1.56 (2011.01.15)

v1.57 (2011.01.30)

v1.58 (2011.02.14)

v1.59 (2011.11.04)

v1.60 (2012.09.09)

v1.61 (2013.01.18)

v1.62 (2013.05.01)

Preamble

This document is getting rather dated, but the basic idea - to add a simple and efficient C-ish exception handling mechanism to the C language - remains just as good as it was when this document was first written sometime back in the Summer of 2009. One thing, however, that I feel is close to worthless in this article is the thought experiement of using the "spare" Intel x86/x64 Carry flag for returning information on whether the value in RAX is a mere function result value or it is an exception pointer. On paper, it looks nifty and is kind of entertaining (somewhat like the Brainfuck programming language), but in real life it doesn't measure up. So my suggestions should be read as follows:

  1. Let's for God's sake add a simple exception handling facility to the C language (C++ reeks and sometimes people have to do low-level stuff!).
  2. Let's use a designated exception instance register for returning the exception pointer. If the register is NULL, no exception has been thrown.
  3. Let's use the remaining proposal from this article (the extensible exception structure and so forth).

As for effiency, when comparing to a setjmp/longjmp exception handling mechanism, I still believe this one is significantly faster in real life because it does not lead to the excessive code bloat (read: CPU cache misses) that the setjmp/longjmp version normally does.

While I've got your attention, I'd like to thank all the readers who have contacted me about this article. I don't recall all of your names and email addresses, but I do recall the stuff that you've told me and asked me about.

Introduction

This paper presents an obvious extension of the C language implemented in a non-obvious way. It is basically a very rudimentary form of exception handling support that requires compiler support. I use the same method in my assembly programs but would prefer to code in C, if possible. I will outline what I do in assembly and you will quickly see what I am aiming at. Please notice that I usually code 64-bit assembly, for which reason the examples are given in x86-64 assembler - please substitute all occurrences of RAX with EAX to make it work on a 32-bit x86 platform. The mechanism presented here is suitable for all contemporary microcomputer architectures and all contemporary imperative languages (even if a full register has to be used instead of a single bit for passing the information that an exception has occured such as is the case with MIPS CPUs according to what I've been told).

The proposal here may be referred to as "C with exceptions" to keep up the good tradition established by Mr. Bjarne Stroustrup for his "C with Classes" proposal (which eventually became the C++ programming language). The proposal presented here is very fast, leads to very small code, and is completely thread-safe.

The idea is to provide C with a basic, yet highly usable exception handling mechanism whose performance is so great that it does not incur any noticeable overhead on the running program - whether or not one or many exceptions occur.

To many people, C is nearly a dead language. It seems everybody is using C++ and C# these days. This is not the case, however, as an extremely large number of legacy systems and even some contemporary systems (such as Linux) are being coded in C or a dialect thereof. Adding a simple and efficient exception handling mechanism to C would give many, many programmers an extremely useful tool for implementing their solutions.

As C is not an object-oriented language, I suggest that the exception item, the item that gets "thrown" by throw, is any word-size value - be it a pointer to a string, an integer, a pointer to an elaborate exception structure or whatever. The idea is to extend C minimally to give the programmer maximal freedom and efficiency so as to retain the simple and efficient nature of C. The most flexible seems to be to make throw take an arbitrary void * pointer and make that be the value that is passed down the call stack.

Despite being very interested in programming languages, compilers, interpreters, and their design, I have only some background in compiler design and implementation (even though I have been a programmer on an Ada95 compiler once). Therefore, I cannot easily extend existing open-source compilers with my ideas but have to appeal to you to do the actual work of adding the requested feature to your favorite open source C compiler.

Presentation

When I code in assembly, I use the following convention:

So far, so good. Each assembler function is coded as follows:

; void *MemoryCreate(size_t rax)
MemoryCreate:
        ; validate parameters
        or      rax, rax
        jz      .throw_ErrorParameter0

; now allocate the memory
        ...

; rax now points to the allocated memory
        clc     ; no exception occurred, clear Carry flag
        ret

ErrorParameter0:
        db      'Invalid parameter 0', 0

.throw_ErrorParameter0:
        ; an exception occurred, use rax for the exception instance
        mov     eax, ErrorParameter0
        stc
        ret

The exception instance, ErrorParameter0, could be anything, but for the time being I am only using simple strings.

The caller of MemoryCreate invokes it as follows:

        mov     rax, 1024   ; allocate 1024 bytes
        call    MemoryCreate
        jc      .catch

        ; use the allocated memory (in rax) for something
        ...

        ; release the allocated memory (pointer still in rax)
        call    MemoryDelete
        jc      .catch

        ...
        clc
        ret

.catch:
        ; catch (ErrorParameter0 rax)
        cmp     rax, ErrorParameter0
        jnz     .unknown

        ; handle exception ErrorParameter0
        clc
        jmp     short .done

.unknown:
        ; propagate this unknown exception to the caller
        stc

.done:
        ...
        ret

The really interesting thing here is that RAX is used simultaneously for the return code and for the exception instance pointer. As there is no exception instance pointer if there is no exception, the RAX register can safely be used for the function result in that case. And as there is no return value if an exception occurs, the RAX register can safely be used for the exception instance pointer in that case. The Carry flag is used to indicate whether an exception occured or not.

The Carry flag is used for one reason only: Because the Intel x86(-64) architecture offers instructions to clear and set this flag directly. On other architectures, any flag that can be directly set and cleared can be used.

Experienced readers may recognize the convention as almost identical to that of the BIOS int 13h functions: Upon error, the Carry flag is set and upon success the Carry flag is cleared. The main difference between this scheme and the BIOS int 13h services is that BIOS require the user to make another system call to query the actual error code, whereas this scheme already has loaded the EAX/RAX register with a near pointer to the exception instance.

Overall, the handling of exceptions is now reduced to the simple question of jumping to the exception handler if an exception occurs. If no user-defined exception handler (catch or finally block) is defined, all that needs to be done is to propagate the exception - which simply takes a simple ret instruction to do. The Carry flag remains set and the RAX register is already loaded up with the proper value, so all there is left is to return to the immediate caller who can then either repeat the pattern, of propagating the exception, or break the pattern by actually handling the exception.

Cleaning Up

If there is program data that needs to be cleaned up prior to returning from a given function, it is only a matter of specifying a finally handler. For languages that offer automatic cleanup, such as C++, the compiler can synthesize an appropriate finally handler to handle the cleaning up whether or not an exception occurs. In many cases, probably most cases, the compiler will only need to synthesize a single finally block for both of the cases.

Syntax

I suggest the most straightforward and commonly recognized syntax for exception handling:

try
{
    throw CreateException("Error: %s %s", a1, a2);
}
catch (void *pException)
{
}
finally
{
}

This would, in x86-64 assembly, be roughly equivalent to this code:

; the try block throws an exception
        mov     rax, Message
        mov     rbx, a1
        mov     rcx, a2
        call    ExceptionCreate
        jc      .catch     ; superfluous, retained only for readability
        stc                ; superfluous, retained only for readability

.catch:
        ; the catch block simply operates on the rax register (the exception instance pointer)
        cmp     rax, Exception
        jnz     .finally

        ; handle the Exception exception

        ; the finally block simply does nothing
.finally:

As C is not an object-oriented language, it does not make sense to derive exceptions from a predefined class. Instead, a simple void * pointer is used. The programmer can use this value for anything. This means the exception code will most likely only be usable in a single context, but that is still a major improvement over not having any exception handling in the language at all.

Obviously, the throw statement on its own would be used to re-throw an already signaled exception, just like in pretty much every other C-ish language out there, which would simply entail ensuring the Carry flag was still set and that RAX/EAX was loaded up with the former exception value.

Example

I imagine something like this:

Exception.h:

#ifndef _Exception_h_
#define _Exception_h_

typedef enum
{
    EXCEPTION__NONE = 0,             /* reserved for initializing the exception structures */

    /* Exception exception codes - these can be thrown without using the heap */
    EXCEPTION_EXIT_SUCCESS,          /* exit the program using EXIT_SUCCESS as the exit code */
    EXCEPTION_EXIT_FAILURE,          /* exit the program using EXIT_FAILURE as the exit code */
    EXCEPTION_STACK_OVERFLOW,        /* a stack overflow error was detected */
    EXCEPTION_DIVIDE_BY_ZERO,        /* a numeric operation attempted a zero division */
    EXCEPTION_MEMORY_ACCESS,         /* access to invalid memory location */
    EXCEPTION_MEMORY_CREATE,         /* unable to allocate the requested memory */
    EXCEPTION_MEMORY_DELETE,         /* unable to release the specified memory */

    /* PositionException exception codes */
    EXCEPTION__POSITION,             /* index of first PositionException exception kind */
    EXCEPTION_ASSERT_FAILED,         /* assertion failed */
    EXCEPTION_PARAMETER_0,           /* parameter 0 (the 'this' pointer) is invalid */
    EXCEPTION_PARAMETER_1,           /* parameter 1 is invalid */
    EXCEPTION_PARAMETER_2,           /* parameter 2 is invalid */
    EXCEPTION_PARAMETER_3,           /* parameter 3 is invalid */
    EXCEPTION_PARAMETER_4,           /* parameter 4 is invalid */
    EXCEPTION_PARAMETER_5,           /* parameter 5 is invalid */
    EXCEPTION_PARAMETER_6,           /* parameter 6 is invalid */
    EXCEPTION_PARAMETER_7,           /* parameter 7 is invalid */
    EXCEPTION_PARAMETER_8,           /* parameter 8 is invalid */
    EXCEPTION_PARAMETER_9,           /* parameter 9 is invalid */

    /* user-defined exception codes are greater than or equal to this value */
    EXCEPTION__USERCODE = 0x100
} EXCEPTION;


typedef void (*ExceptionDestructor)(struct _Exception *this);

struct _Exception
{
    EXCEPTION           Code;     /* the globally unique exception code */
    ExceptionDestructor Delete;   /* pointer to function to deallocate the exception instance */
};
typedef struct _Exception Exception;

/* destructor for the Exception structure */
extern void ExceptionDelete(Exception *that);


/* various basic exception definitions that must be usable even if the heap is corrupted */
extern Exception ExceptionExitSuccess;
extern Exception ExceptionExitFailure;
extern Exception ExceptionStackOverflow;
extern Exception ExceptionDivideByZero;
extern Exception ExceptionMemoryAccess;
extern Exception ExceptionMemoryCreate;
extern Exception ExceptionMemoryDelete;


/* an exception structure that includes context information and an error message */
struct PositionException
{
    Exception   Base;            /* the "base class" (inherited structure) */
    const char *File;            /* the name of the source file (__FILE__) (may be NULL!) */
    unsigned    Line;            /* the line number in the source file (__LINE__) (may be zero!) */
    char       *Text;            /* the error message (may be NULL!) */
};

/* destructor for the PositionException structure */
extern void PositionExceptionDelete(Exception *that);


/* throwf:
   Throws a newly allocated and filled in PositionException.
*/

extern void throwpf(
  int         code,
  const char *file,
  int         line,
  const char *format,
  ...
);

#endif /* _Exception_h_ */

Exception.c:

#include <stdarg.h>              /* va_list, va_start, va_end */
#include <stddef.h>              /* NULL */

#include "Exception.h"


/* ExceptionDelete:
   Destructor for the Exception structure; does nothing.
*/

void ExceptionDelete(Exception *that)
{
}


Exception ExceptionExitSuccess =
{
    EXCEPTION_EXIT_SUCCESS,
    ExceptionDelete
};

Exception ExceptionExitFailure =
{
    EXCEPTION_EXIT_FAILURE,
    ExceptionDelete
};

Exception ExceptionStackOverflow =
{
    EXCEPTION_STACK_OVERFLOW,
    ExceptionDelete
};

Exception ExceptionDivideByZero =
{
    EXCEPTION_DIVIDE_BY_ZERO,
    ExceptionDelete
};

Exception ExceptionMemoryAccess =
{
    EXCEPTION_MEMORY_ACCESS,
    ExceptionDelete
};

Exception ExceptionMemoryCreate =
{
    EXCEPTION_MEMORY_CREATE,
    ExceptionDelete
};

Exception ExceptionMemoryDelete =
{
    EXCEPTION_MEMORY_DELETE,
    ExceptionDelete
};


/* PositionExceptionDelete:
   Destructor for the PositionException structure; deallocates memory.
*/

void PositionExceptionDelete(Exception *base)
{
   PositionException *that = (PositionException *) base;
   free(that->Text);
   free(that);
}


/* throwf:
    Formats a text string and throws an exception instance embedding that text.
*/

void throwf(int code, const char *file, int line, const char *format, ...)
{
    PositionException *exception = NULL;
    char              *text      = NULL;

    exception = (PositionException *) calloc(1, sizeof(PositionException));
    if (exception == NULL)
        throw &ExceptionMemoryCreate;
    exception->Base.Delete = PositionExceptionDelete;
    exception->Base.Code   = code;
    exception->File = file;
    exception->Line = line;

    text = (char *) malloc(1024);
    if (text == NULL)
    {
        free(exception);
        throw &ExceptionMemoryCreate;
    }

    /* format the string according to the given parameter list */
    va_list args;
    va_start(args, format);
    vsprintf(text, format, args);
    va_end(args);

    /* hook in the text */
    exception->Text = text;

    /* finally, throw the exception */
    throw exception;
}

MyCode.c:

...

#include "Exception.h"

void ParseParameters(int argc, const char *argv[])
{
    if (argc == 1)
        throwf(EXCEPTION_PARAMETER_1, __FILE__, __LINE__, "No parameters specified");
    ...
}

int main(int argc, const char *argv[])
{
    int result = 0;

    try
    {
        ParseParameters(argc, argv);
    }
    catch (void *exception_void)
    {
        /* note: the code here is a bit hairy, but exception handlers are generally rare */
        Exception *exception = (exception *) exception_void;
        if (exception->Base.Code >= EXCEPTION__USERCODE)
        {
            /* decode the exception according to whatever formats are used in the system */
        }
        else if (exception->Base.Code >= EXCEPTION__POSITION)
        {
            PositionException position = (PositionException *) exception;
            printf("Error %d: %s\n", position->Base.Code, position->Text);
        }
        else
        {
             printf("Error: %d\n", pException->Base.Code);
        }
        exception->Delete(exception);  /* free up any allocated memory, if applicable */
        exception = NULL;

        result = EXIT_FAILURE;
    }

    return result;
}

Obviously, it would be nice if one could write things like:

try
{
    ...
}
catch (Exception that)
{
}
catch (MyException that)
{
}

But that goes way beyond the scope of what I have in mind. Such a solution would require some sort of run-time identification of every exception type and that basically means redoing what has already been done for C++. Instead, I propose the exception instance to be a simple void * pointer that by convention points to an Exception structure, or a structure whose first field is an Exception structure, so that it is possible to mix and match modules from various vendors in one single project. However, my primary goal is to implement a C exception handling mechanism that is very efficient while beneficial in a single project. If people need or want a better exception handling mechanism than the one offered here, I suggest they make the transition to C++ or a similar modern language.

Performance

You may worry that all the jump-if-carry (jc) instructions will throw a huge performance impact on the generated code. Not so. I have compared by exception handling mechanism to that of C++ and my method is four times faster than the exception handling mechanism of C++. Furthermore, it generates code that is much smaller than the code generated by compilers that implement the traditional C++ exception handling mechanism. Notice that the jump instructions are almost never taken. I have read somewhere that forward jumps are assumed, by the Intel 80x86 processor, to not being taken. In other words: The processor happily assumes that forward jumps are not to be executed, so no stalls occur, which is exactly what we want, and everything works as we want it to. I have not been able to find this information again, though. In comparison to the Intel processors, processors such as the PowerPC 603 defines a mechanism that lets the programmer, or translator, specify if the jump is likely to be taken or not. On such architectures, all of the jump-if-carry instructions should be encoded so that their opcodes indicate that they are not likely to be taken.

Worth noticing, for those worrying about the extra clock cycles overhead imposed by the stc, clc, and jc instructions: Usually C programmers explicitly have to check the return value of the called function to determine if an error has occurred. This is tedious, error-prone, and adds a lot of overhead. Using the presented exception handling mechanism, the programmer no longer has to explicitly check the return value of the called function but can freely focus on his or her real task at hand: To code some code that makes use of some functions. The model presented here is actually much more efficient than the explicit checking of return values because it only requires the use of the clc and jc instructions in the typical case that no exception occurs. If an exception occurs, the run-time overhead entails an additional move instruction that loads the RAX/EAX register with the exception instance pointer. I have intentionally used a very complex example of throwing a non-trivial exception instance, allocated on the heap, so as to demonstrate just how powerful this scheme is. Anything can be thrown and exceptions typically occur only very rarely, for which reason it is of no importance how complex and time-consuming the code that generates the exception is.

I have today, Friday 4th of September, 2009, written two simple C and C++ test programs to test an approximation of the performance of this method. The result was that the C version (which simulates the method presented) performed four times faster than the C++ test program. I'll be happy to email my test programs to anyone who cares for them, but they basically do the following:

for (long i = 1; i < 1000000000000; i += 1)  /* one trillion times */
{
    try
    {
        /* call external function that may signal error
          (by throwing an exception or by returning a return value of -1) */

    }
    catch (Exception that)
    {
        printf("Error\n");
    }
}

And the C version took 7.5816 seconds to execute whereas the C++ version took 28.2984 seconds. These numbers were obtained on a Dell XPS 420 3.16 GHz Intel Core 2 Duo running Microsoft Windows Vista Home Premium x64. The code was compiled and run in 32-bit mode using the OpenWatcom compiler v1.8. As the C version is not as efficient as it would be had it proper compiler support, I guess performance is five to ten times better than the C++ version. The reason for this being that the C version does not have to perform a setjmp() call for each try-catch block, but can happily execute as if nothing had happened at all (please notice that try expands to nothing in the scheme that has been presented in this article). Also, the C version was precisely 33 percent smaller than the C++ version.

Border Conditions

I have received a single complaint that I did not properly address border conditions in my paper. I feel that I actually do, because the scheme presented here is so trivial and simple that there are no difficult border conditions. Nonetheless, I will try to complete the presentation so that each and every possible case is considered and explained.

Simple Intra-function Exception Handling

There's the simple case where an exception is thrown and caught within the same function:

int a;

try
{
    throw (void *) 1;
    a += 1;
}
catch (void *exception)
{
    a = (int) exception;
}

return a;

When optimized, this code becomes:

        mov     rax, 1    ; load return value
        clc               ; signal that no exception occurred (it was handled completely)
        ret

Because the "a += 1;" statement is clearly superfluous and will be optimized away. And the catch handler does not reraise the exception nor does it raise a new exception.

Complex Intra-function Exception Handling

The "C with Exceptions" code below:

int a = 1;
int b = 2;

try
{
    if (a > b)
        throw (void *) "Whoa!";
    b *= a;
}
catch (void *exception)
{
    /* dummy; no exception is ever raised */
}

return a;

Becomes the Intel x64 assembler code shown below:

        mov     rbx, 1
        mov     rax, 2
        ; try always expands to nothing
        cmp     rbx, rax
        jbe     .1
        mov     rax, offset "Whoa!"
        stc
        jmp     short .catch
.1:
        mul     rbx
        clc
        ret
.catch: ; never used, but the compiler does not know that
        clc
        ret

Inter-function Exception Handling

Handling exceptions across functions is actually just as simple as handling exceptions within functions. If the Carry flag is set, the caller jumps to its local catch block. If there is no catch block, the caller simply returns the unaltered Carry flag and Exception Instance Pointer (probably RAX) to its caller and so forth, until an exception handler exists. If an exception handler exists, the caller simply transfers control to that insofar the Carry flag is set. It couldn't be much simpler than this. And at the same time, it cannot be much faster than this. This just shows that the KISS philosophy (Keep It Simple, Silly!) generally produces extremely fast code.

Disadvantages

A major disadvantage of the proposal presented here is that it only works if the client code is compiled to conform to the new exception handling mechanism (which is also the case with many C++ exception handling mechanisms). The compiler needs to generate code to propagate unhandled exceptions also in code that does not make use of exception handling. This could easily be implemented by making a new calling convention, say excall, which is automatically enabled for all functions in the program if a given command-line option is specified. Functions that do not make use of the exception handling feature still need to clear the Carry flag upon return - or, alternatively, the compiler needs to keep track of what functions are compiled using the excall calling convention and what functions are not compiled using this calling convention and handle them appropriately.

The disadvantage mentioned above is really rather small: Most PC compilers offer a number of calling conventions, suited to interfacing with various forms of code, and more importantly, the overhead of using the proposed exception handling mechanism is so slight, that it actually is smaller than the overhead of manually testing return codes to see if an error has occurred.

Runtime Library Support

The most beautiful thing about the proposal that I have made here is that it requires no run-time library support whatsoever. Those of us accustomed to making embedded systems using C or C++ know all too well what a pain it is to make a C++ program that uses exceptions run on a bare system. The scheme presented here works right out of the box without any additional run-time library support. The only run-time library support that can be of relevance is a small set of predefined exception instances that define errors that are likely to happen in any program. I have shown a subset of this set in the definition of the header file Exception.h.

Alternatives

There are a number of existing alternatives to the proposal presented here:

All of the presented alternatives, except the undocumented xerror1 module by myself, use setjmp()/longjmp() as the means of transferring control across call frames, which is a very compatible, space consuming, and slow way of doing it. "C with Exceptions", the proposal described in this document, relies on compiler support but in return results in code that will be a bit smaller than the equivalent manually coded error checking code and will also run a bit faster than such code because the exception handling mechanism presented here relies on checking a single architecture-dependent CPU flag whereas manually coded error handling code generally relies on comparing a host of different return values against another host of predefined constants (some functions return an integer that indicate success/failure, some return a pointer, some return a char, some set a global variable, and it is all one big mess).

Future

Stack Traces

A compiler such as the OpenWatcom compiler already includes a feature that allows the running program to query the name of the containing function (by deciphering an encoded string that is stored right before the start of the function). Combining this feature with the presented exception handling mechanism, it would be possible to offer full stack traces while retaining the extraordinary high execution speed that this scheme results in.

Exception Aware CPU

Like most nerds out there, I'd love to participate in the design and implementation of a microprocessor. I often think that what we need from contemporary microprocessors is a more high-level approach rather than loads of useless, unused features such as segmentation and so on.

The scheme presented here is extremely well suited for inclusion in a future microprocessor: With a few additions to the basic instruction set, the CPU could become exception aware on the lowest level, opening up for the most beautiful implementation of various system error conditions.

Let's assume that the processor gets a hardware register devoted to the exception instance pointer. Then the processor also needs a jump-if-the-exception-register-is-nonzero instruction (and the complimentary jump-if-the-exception-register-is-zero instruction, just for the sake of sensible completeness).

On Intel x86/x64 processors, there's already a jump-if-rcx-is-zero instruction. But what we need is the complimentary instruction, jump-if-rcx-is-nonzero, instruction. Unfortunately, this instruction does not exist - otherwise it would have been logical to use the RCX register as the exception instance register.

But back to our dream microprocessor:

  1. It would need a LEI instruction: Load Exception Instance. The LEI instruction would do the following: load the Exception Instance Register (EIR) with the operand. If the operand was non-zero, i.e. a non-NULL pointer, it would set the processor flag Exception to one. Otherwise, it would set it to zero. (This is an awesome place for a hardware debug exception: "Break on Exception. This exception would occur whenever the proper debug flags were set up to indicate that the CPU should trap on the Exception flag becoming one.)
  2. It would need a JEX instruction: Jump on Exception. The JEX instruction would jump to the specified target if and only if the Exception Instance Register was non-zero (the Exception register was set to one). The CPU should be optimized to assume that the JEX instruction would never be taken. This would probably incur a CPU decode stall whenever an exception happened, but these are so rare, in a typical system, that it is of no importance how long, within reasonable limits, it takes to process an exception.

The great thing about adding an exception instance register and an exception flag to the CPU is that many, many low-level error conditions can be handled directly by the application code - without involving the kernel - so that the CPU could simply assign the Exception Instance Register a predefined exception instance pointer whenever a system error occurred. For instance, let's take the instance of an invalid address being addressed. There's really no need to notify the system about this as long as the application code is exception aware and can handle the exception directly. So if client code X addressed illegal address Y, the processor would simply load up the Exception Instance Register with a pointer to a predefined system exception instance and let the client code X handle the exception without notifying the operating system about the addressing error. Some will probably feel that the operating system must know all serious errors in the system, but not so. Why involve the operating system in something that can be settled right out of the box between the CPU and the application code? I see no reason why, perhaps except for statistical purposes, but in that case, the CPU could possibly be programmed into signaling an interrupt whenever something untoward happened. Again, the CPU could be configured to behave however the operating system designer wants, but still offer a very neat and elegant way of handling low-level errors.

Summary

This informal paper presented a compact and fast alternative to the traditional stack unwinding that takes place in typical C++ programs. The alternative has been measured to be about four times faster than the equivalent C++ stack unwinding mechanism. The performance gain is achieved by letting the CPU perform the stack unwinding rather than by programmatically implementing an explicit stack unwinding procedure. It is the belief of the author that the method presented here is suited for all languages that implement exception handling (C++, Ada, Java, C#, etc.). The basic idea of using a flag to indicate whether an exception has occurred or not, and to use the primary return value register for storing a pointer to an exception instance, can easily be applied to any imperative language.

Epilogue

I am sending this article to a number of independent compiler writers. I am doing so because I really hope to one day see this excellent error handling method implemented in a real compiler on the market. Most of all, I hope to see this mechanism implemented in one of the open source, freeware compilers that I use:

  1. GNU Compiler Collection.
  2. OpenWatcom C/C++ Compiler.
  3. Tiny C Compiler.

But I encourage commercial recipients of this article to explore what I have written and to implement it in their compilers. Doesn't a speed-up of four times sound promising? Exception handling is becoming more and more widely used for each and every day for which reason more and more can benefit from what this article presents.

I am certain that the Linux coders out there would appreciate having some form of exception handling mechanism in their code - even though some of them would probably laugh at the idea. But having coded in C and C++ for about 20 years, I can only say that the greatest problem of C is not the lack of object-oriented features (these are straightforward to simulate to a certain degree), but the lack of an efficient exception handling mechanism.

I am aware that traditional stack unwind exception handling can be simulated by using the setjmp() and longjmp() functions instead of try/catch blocks, but such an approach is way too expensive, in terms of clock cycles, and also makes the size of the program explode.

Implementations

As of January, 2011, Mr. Keith Reynolds has made a patch that modifies the Tiny C Compiler to support the scheme outlined here. The patch is available from SourceForge under the name tcc-exceptions. If you need a patched Windows version of the Tiny C Compiler, please feel free to write me at mikael@lyngvig.org: The procedure for building a patched Windows version is somewhat difficult, unless you are familiar with Unix tools on Windows.

I have contacted the Tiny C folks and they adamantly refused to incorporate my ideas on the grounds that they are not ISO 1999 compliant (despite them having a number of GNU extension in the Tiny C compiler).

I have contacted the OpenWatcom folks and they basically ignored my proposal; a few supporting comments were made but nobody seemed interested in undertaking the project.

I have not yet contacted the GNU folks. If you read this, feel free to forward a pointer to this article to the GNU folks.

Questions

If this text is unclear, you have questions about specific details or you have ideas, please feel free to contact me. If you think I am nuts and that the project is insane, feel free to not contact me. I know that the idea of adding (simple) exception handling support to C will make many ask things like Why not just use C++? and Why not write your own compiler?, but look at it from my point of view: I am already programming some assembler modules that make use of this error handling strategy, with great results and very simple code as a result, and would like to write even more code in C and interface this code with my existing assembler code. Existing projects, such as the Linux kernel, could greatly benefit from a simple exception handling mechanism that makes it a breeze to define and handle errors in such a complex software system. Also, C is still widely used in embedded systems development and such systems very often suffer from inadequate error handling and complex code due to the constant problem of handling runtime errors.

Appendix A: Simple Example

#include <stddef.h>
#include <string.h>

void ParseArgs(int argc, const char *argv[])
{
    int i;
    if (argc == 0)
        throw "No parameters specified";

    for (i = 0; i < argc; i++)
    {
        const char *arg = argv[i];

        /* reject empty parameters */
        if (arg[0] == '\0')
            throw "Empty parameter";

        /* an option */
        if (arg[0] == '-')
        {
            const char *pos = strchr(arg, ':');
            if (pos == NULL)
                throw "No parameter specified in option";

            /* blah blah blah*/
        }

        /* a pure parameter - not an option */
        if (strcmp(arg, "foo") != 0)
            throw "Parameter is not 'foo'";
    }
}

int main(int argc, const char *argv[])
{
    int result = 0;

    try
    {
        ParseArgs(argc - 1, argv + 1);
        printf("Success\n");
    }
    catch (void *exception)
    {
        printf("Error: %s\n", exception);
        result = 1;
    }

    return result;
}

History

v1.00

  1. Initial version.

v1.02

  1. Added section on run-time library support (of which none is required).

v1.03 (2009.08.30)

  1. Added include statements to the examples (I'm mostly using C# these days...).
  2. Fixed minor grammatical errors (I am not a native speaker of English...).

v1.04 (2009.08.31)

  1. Added explanation that the run-time overhead of the presented exception handling mechanism is so small that it is actually less than the traditional C method of explicitly checking the return value of each function.

v1.05 (2009.09.01)

  1. Removed a few spelling errors ("an" -> "and").

v1.06 (2009.09.03)

  1. Added appendix A (simple example).

v1.07 (2009.09.04)

  1. Added: Performance update. This method is not 35 to 50 percent faster than the traditional setjmp()-based implementation of C++ exception handling but FOUR times faster!

v1.08 (2009.09.11)

  1. Removed: The About section (it really had no place in a paper like this).
  2. Added: The Implementations section.
  3. Fixed: Changed statements about 35-50 percent speedup to four times speedup.

v1.09 (2009.09.13)

  1. Added: Chapter on border conditions.
  2. Added: Chapter on future development of the scheme presented here.

v1.10 (2010.09.01)

  1. Redid in ScrewTurn Wiki markup language and published it on www.lyngvig.org.

v1.20 (2010.09.07)

  1. Officially introduced the name "C with Exceptions" for this proposal with due respect to Mr. Bjarne Stroustrup and his "C with Classes" proposal that became C++.
  2. Added a chapter on alternatives per request of Mr. Cesar Rabak from the OpenWatcom.org C/C++ mailing list at news.openwatcom.org.

v1.21 (2010.09.07)

  1. Added note that "C with Exceptions" is thread-safe. This is so inherent in my programming style that I altogether forgot to mention it in all previous versions.

v1.22 (2010.09.08)

  1. Fixed a tiny writing error ("There is" became "There are" in the section on Alternatives): English is not my native language and it sometimes shows very clearly.

v1.25 (2010.09.10)

  1. Fixed typing error - removed a superflous closing parenthesis ()).
  2. Added section on Stack Traces.

v1.26 (2010.09.27)

  1. Updated the implementations chapter to include my experiences with the OpenWatcom folks.

v1.30 (2010.10.15)

  1. Fixed: Bug in sample (a comparison against the USERCODE macro was incorrect).
  2. Changed: To use void * as the exception item rather than const void *; in a typical system, the exception item would be a pointer to user-allocated memory, not a const void * pointer.
  3. Clarified some passages and added elaborations so as to make the document easier to follow.
  4. Added: A couple of additional pre-defined exception types (the list is nowhere near complete, though). The most important addition being the ErrorExit exception type, which is used to exit the program gracefully so that all stack frames are unwound and everything is cleaned up nicely prior to exiting the application.
  5. Fixed: Missing spaces all through the document (I like ''two'' spaces between sentences so as to increase readability).

v1.50 (2010.10.15)

  1. Added: A few more exception instances so as to provide the basic set needed in most apps.
  2. Fixed: Various short-comings in the sample definitions of the Exception classes:
  1. Added: The PositionException structure for exceptions with context information.
  2. Fixed: Ensured that the basic, fatal errors can be reported WITHOUT touching the heap.
  3. Added: The throwf() helper function to the proposed standard run-time support code.
  4. Fixed: Changed USERCODE to 0x100 (256) so as to support 16-bit systems.
  5. Fixed: Mismatch between "sException" and "Exception"; the former spelling has been retired.

v1.51 (2010.10.15)

  1. Fixed: Spelling error in version list ("and" -> "an" => "an" -> "and").
  2. Fixed: Spelling error in comment in sample Exception.c file.

v1.52 (2010.10.15)

  1. Fixed: Two spelling errors in the run-time library support code samples; it ain't easy when you can't compile the thing.

v1.53 (2010.10.18)

  1. Fixed: Adam M. Costello recently updated his website on cexcept to correct the broken links. I have updated the Alternatives section to include coverage of the three packages whose links were previously broken.

v1.54 (2010.10.18)

  1. Added: Color-coding to the C examples using the ScrewTurn Wiki Syntax Highlighter plugin (only works when viewing the web page).

v1.55 (2010.12.10)

  1. Fixed: Incorrect cast to const void * - it should be void *.

v1.56 (2011.01.15)

  1. Fixed: Removed extranous 's' in comment regarding PositionException.
  2. Fixed: Changed quotes (") from Word's beatified versions into plain ASCII quotes.
  3. Fixed: Removed extranous uppercase C in EXCEPTION_EXIT_SUCCESS.
  4. Fixed: Changed invalid references to ErrorMemoryCreate into ExceptionMemoryCreate.
  5. Fixed: Bogus reference to (PositionException *)->Code, which should be (PositionException *)->Base.Code.
  6. Fixed: Changed Unicode-single-character "..." into three consecutive ASCII dots (an artifact from when this document was written in Microsoft Word).
  7. Alert: I have now been able to compile the sample source code with a C compiler that partially supports the features documented in this article. Details will be released soon.

v1.57 (2011.01.30)

  1. Added: Official description of the "Tiny C Compiler with Exceptions" (TCC-E) now available on SourceForge under the name tcc-exceptions.

v1.58 (2011.02.14)

  1. Added: Comment on the new "C with Exceptions" website.

v1.59 (2011.11.04)

  1. Removed: Bogus comment on new "C with Exceptions" website.

v1.60 (2012.09.09)

  1. Changed: Changed wiki engine from ScrewTurn Wiki to MiWiKi and therefore had to manually reformat the entire document.
  2. Fixed: Various Unicode characters that were a leftover from back when the document was originally written in Word 2007.
  3. Fixed: Inconsistent use of x86 vs x64 samples; all samples should be x64 only.
  4. Fixed: Few remaining Unicode "..." that were left over from the Office days of this document.
  5. Fixed: Replaced Unicode dashes (-) with their ASCII counterpart.

v1.61 (2013.01.18)

  1. Fixed: Minor C#-ish typo: exception.Code became exception->Code, thanks to Denis Spir for this.

v1.62 (2013.05.01)

  1. Added: The preamble.