A C (not C++) Exception Mechanism
While C does not have exceptions built-in, it is possible to add a reasonably efficient and portable (albeit somewhat crude) exception mechanism using macros and the setjmp/longjmp C Run-Time-Library functions (see here).
The Roberts Proposal
A very useful C exception mechanism was proposed by Eric S. Roberts in 1989:
Implementing Exceptions in C, by Eric S. Roberts, Digital Systems Research Center Report #40, 1989
Roberts’ approach allows the following (example in C):
#include "exception.h"
...
exception OpenError; // exception is a type
...
TRY // TRY is a C macro
...
file = OpenFile("test.dat", "r");
...
EXCEPT(OpenError) // EXCEPT and ENDTRY are C macros
fprintf(stderr, "Cannot open file 'test.dat'\n");
ENDTRYThe general form of a TRY–ENDTRY block is:
TRY
statements
EXCEPT(exception-name-1)
statements
EXCEPT(exception-name-2)
statements
...
ENDTRYAn exception is raised by:
RAISE(exception-name, value);where the value is an int to be communicated back to the handler, to provide additional information about the exception.
The TRY-FINALLY Block, and Resource Acquisition
This approach also included the option of a finally block (again, in C):
Roberts also specified a TRY–FINALLY block, which is of the form:
TRY
statements
FINALLY
statements
ENDTRYThe major point of a FINALLY block is to allow for convenient cleanup in all cases:
#include "exception.h"
...
Acquire(Resource);
TRY
/* code for accessing the resource */
FINALLY // FINALLY is a C macro
/* this is executed whether or not an exception occurs */
Release(Resource);
ENDTRYThis allows for acquired resources to be released, whether the access is successful or not
setjmp, longjmp and jmp_buf
Roberts’ exception handling mechanism made use of the setjmp and longjmp functions, which use a buffer called jmp_buf.
Function setjmp
#include <setjmp.h>
...
int setjmp(jmp_buf env);saves the current ‘environment’ in its jmp_buf argument, and returns 0.
Function longjmp
#include <setjmp.h>
...
void longjmp(jmp_buf env, int val);restores the environment saved by setjmp, which causes a return from the setjmp call with a value of val. longjmp never itself returns!
jmp_buf
Type jmp_buf is a system-specific type that is of sufficient size to save the ‘environment’ (such as machine registers, etc.) so that the machine state may be saved, and later restored. (It must be an array type, so that a pointer to it is always passed to a function.)
An Example:
//
// setjmpTest.c
// setjmp & longjmp
//
// Created by Bryan Higgs on 9/23/24.
//
#include <stdio.h>
#include <setjmp.h>
jmp_buf main_env;
int InputNumber(void)
{
int n, ret;
fflush(stdin); // ensure there's nothing left
// from the last scanf
ret = scanf("%d", &n);
// read an integer from stdin
if (ret <= 0)
{
// something went wrong during the read...
printf("Return value from scanf = %d ", ret);
longjmp(main_env, 1);
}
return n;
}
int main()
{
int n;
if (setjmp(main_env) != 0)
printf("\nInvalid, try again...\n");
printf("Enter a valid number:");
n = InputNumber();
printf("You entered the number: %d\n", n);
return 0;
}Here’s the user’s (my) interaction with the program:
Enter a valid number:x
Return value from scanf = 0
Invalid, try again...
Enter a valid number:52
You entered the number: 52
Program ended with exit code: 0
See if you can figure out how the program works!
Note that longjmp does not clean up any resources that may have been allocated since the setjmp. That is the programmer’s responsibility!
Well, enough already of Ada and C implementations.
Let’s see what C++ implements…