0% found this document useful (0 votes)
95 views9 pages

Exceptions in C With - Longjmp and Setjmp - Try Catch

This document describes an implementation of exceptions in C using longjmp and setjmp. It introduces the concepts with a basic try-catch example, then expands on it by adding different exception types and a finally block. The full code and license terms are included at the end.

Uploaded by

oraculus_lunae
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
95 views9 pages

Exceptions in C With - Longjmp and Setjmp - Try Catch

This document describes an implementation of exceptions in C using longjmp and setjmp. It introduces the concepts with a basic try-catch example, then expands on it by adding different exception types and a finally block. The full code and license terms are included at the end.

Uploaded by

oraculus_lunae
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
You are on page 1/ 9

EXCEPTIONS IN C WITH LONGJMP AND

SETJMP

Abstract

This document describes a very simple implementation (with many limitations) of a


system to add exceptions on top of C using the libc calls longjump and setjump.
This system does not pretend to be really useful in practice but it is a useful lesson
about longjump and setjump with a fun example.

INTRODUCTION
Exception are a very powerful way to program error safe programs. Exceptions let
you write straight code without testing for errors at each statement. In modern
programming languages, such as C++, Java or C#, exceptions are expressed with
the try-throw-catch statement.

...
try
{
...
/*errorpronestatements*/
...
}
catch(SomeExceptionTypee)
{
...
/*dosomethingintelligenthere*/
...
}
...

In previous example every exception raised by operations performed in try-block is


passed to the right catch-black. If the exception type
match SomeExceptionType than the code in that block is executed. Otherwise the
exception is passed to the try-block that contains the actual one (if any).

Our solution is not a fully functional try-throw-catch system. It does not forward
exceptions from one block to one more external if no handler is provided.

Real exception mechanisms need run-time support. We only want to explore the
potentiality of longjmp and setjmp function with a non trivial example.
LONGJMP AND SETJMP
ANSI-C provide a lot of functions: math functions (log, sqrt...), string handling
functions (strdup, strcmp, ...) and I/O functions (getc, printf, ...). All these
functions are widely used and simple to understand (...strtok is not so intuitive after
all...): only two functions are considered strange beasts.

These functions are longjmp and setjmp.

longjmp and setjmp are defined in setjmp.h header file...

#include<setjmp.h>

...and are defined as follows:

intsetjmp(jmp_bufenv);
voidlongjmp(jmp_bufenv,intval);

setjmp takes a jmp_buf type variable as only input and has a strange return
behavior: it returns 0 when invoked directly and when longjmp is invoked with the
same jmp_buf variable it returns the value passed as second argument
of longjmp.

Do you think that this is obscure? Strange? Without sense?

Probably you are right! The behavior of there functions is really strange: you have a
function (setjmp with two return values).

setjmp and longjmp mechanism works as follows: when setjmp is invoked the
first time it returns 0 and fill the jmp_buf structure with the calling environment and
the signal mask. The calling environmentrepresents the state of registers and the
point in the code where the function was called. When longjmp is called the state
saved in the jmp_buf variable is copied back in the processor and computation
starts over from the return point of setjmp function but the returned value is the one
passed as second argument to longjmp function.

Probably now you are thinking something like: "Hey dude are you kiddin' me?". The
replay is "No". The behavior is exactly the one stated before.

There are 10 kind of people in the world:

1. people thinking that this is awful (and probably are asking themselves why
only two cases if there are 10 kind of people)
2. people thinking that it can be amazing!

The rest of document is for second ones.

BASIC TRY-CATCH
First version is a real simple one. Probably if you are here you know the solution.
(this solution was presented also by other authors... my contribution is represented
by the second and the third version of the solution).

The general idea is to map TRY statement on if statement. The first time that it is
invoked it return 0 and the executed code is the one stated
in then branch. CATCH statement is simply the elsestatement. When
the THROW statement is executed it simply calls the longjmp function with the
second parameter equals to 1 (or anything not 0).

#include<stdio.h>
#include<setjmp.h>

#defineTRYdo{jmp_bufex_buf__;if(!setjmp(ex_buf__)){
#defineCATCH}else{
#defineETRY}}while(0)
#defineTHROWlongjmp(ex_buf__,1)

int
main(intargc,char**argv)
{
TRY
{
printf("InTryStatement\n");
THROW;
printf("Idonotappear\n");
}
CATCH
{
printf("GotException!\n");
}
ETRY;

return0;
}

In our solution we provide also an ETRY statement that represents the end of try-
throw-catch block. This is needed because we include all operations performed
by try-throw-catch block in a do...while(0)block. This solution has two main
reasons:

all the TRY-ETRY expression is treated as a single statement


we can define multiple, not nested, TRY-ETRY statements in the same block
(reuse of ex_buf__ variable).

The following represents the compilation and execution steps of the previous
example.

[nids@vultusttc]%gccex1.c
[nids@vultusttc]%./a.out
InTryStatement
GotException!
[nids@vultusttc]%

ADDING EXCEPTIONS
Real exception systems have the possibility to define various kinds of exceptions.
These kinds are mapped over types and catch statements intercept exceptions
using these types.

In our solution we cannot define different types for different exceptions. Our solution
maps different exception on different return values of function setjmp. To do this we
use defines like the following:

#defineFOO_EXCEPTION(1)

Now, our TRY-ETRY must use a switch statement instead of if statement.


Each CATCH statement is no more a simple else but it maps over a case.

CATCH now become a macro with parameters. Parameter represents the


exception kind that is treated in that block. Each CATCH statement must also close
the previous case block (with a break.)

#include<stdio.h>
#include<setjmp.h>

#defineTRYdo{jmp_bufex_buf__;switch(setjmp(ex_buf__)){
case0:
#defineCATCH(x)break;casex:
#defineETRY}}while(0)
#defineTHROW(x)longjmp(ex_buf__,x)

#defineFOO_EXCEPTION(1)
#defineBAR_EXCEPTION(2)
#defineBAZ_EXCEPTION(3)

int
main(intargc,char**argv)
{
TRY
{
printf("InTryStatement\n");
THROW(BAR_EXCEPTION);
printf("Idonotappear\n");
}
CATCH(FOO_EXCEPTION)
{
printf("GotFoo!\n");
}
CATCH(BAR_EXCEPTION)
{
printf("GotBar!\n");
}
CATCH(BAZ_EXCEPTION)
{
printf("GotBaz!\n");
}
ETRY;

return0;
}

The following represents the compilation and execution steps of the previous
example.

[nids@vultusttc]%gccex2.c
[nids@vultusttc]%./a.out
InTryStatement
GotBar!
[nids@vultusttc]%

ADDING FINALLY-BLOCK
A nasty feature of real exception systems is represented by finally statement.

Finally statement is really powerful. The block guarded by finally statement is


executed after the try block or any of the catch blocks. In real exception
systems finally block is executed also is try or catchblock execute an exit or return
statement. We cannot build over the language a system like this.

Our FINALLY statement is executed in three cases:

after a TRY block code (with out exiting)


after a CATCH block code (with out exiting)

when an exception kind is not a known one

Ok... wait a moment... how can we do it?

In line of principle it is simple: our FINALLY block must map the default case of the
switch. This respect exactly the third event (when an exception kind is not a known
one).

To respect the other two events it looks simple enough: instead of breaking out of
switch, we must jump to the default case for instance with:

gotodefault;

...bad answer: ANSI-C does not provide you to goto to a case of a switch (but it
works perfectly with C#).
We must find another solution for this problem. Do you know Duff Device? It is a
really funny way to use a switch statement to do hand made loop unrolling. It uses
a do{...}while(0) weaved in a switchstatement.

We want to use a similar technique. Read the following code:

...
switch(/*someexpression*/)
{
case0:while(1){
...
/*case0codehere*/
...
break;
case1:
...
/*case1codehere*/
...
break;
}
default:
...
/*defaultcasecodehere*/
...
}
...

Yes... it is a while statement weaved in a switch statement. Break statements,


when invoked, exit from the while statement and not from switch because while is
the nearest one.

Now our system is complete: we must weave a while in the switch statement.
Our TRY-ETRY now become:

#include<stdio.h>
#include<setjmp.h>

#defineTRYdo{jmp_bufex_buf__;switch(setjmp(ex_buf__)){
case0:while(1){
#defineCATCH(x)break;casex:
#defineFINALLYbreak;}default:
#defineETRY}}while(0)
#defineTHROW(x)longjmp(ex_buf__,x)

#defineFOO_EXCEPTION(1)
#defineBAR_EXCEPTION(2)
#defineBAZ_EXCEPTION(3)

int
main(intargc,char**argv)
{
TRY
{
printf("InTryStatement\n");
THROW(BAR_EXCEPTION);
printf("Idonotappear\n");
}
CATCH(FOO_EXCEPTION)
{
printf("GotFoo!\n");
}
CATCH(BAR_EXCEPTION)
{
printf("GotBar!\n");
}
CATCH(BAZ_EXCEPTION)
{
printf("GotBaz!\n");
}
FINALLY
{
printf("...etinarcadiaEgo\n");
}
ETRY;

return0;
}

The following represents the compilation and execution steps of the previous
example.

[nids@vultusttc]%gccex3.c
[nids@vultusttc]%./a.out
InTryStatement
GotBar!
...etinarcadiaEgo
[nids@vultusttc]%

COMPLETE CODE AND LICENSE TERMS (MODIFIED ON


FEBRUARY 28TH, 2013)
After the publishing of this document, I received quite a bit of questions about how
the code, if it can be possible to use it and what license covers the code.

For the license, I choose the MIT license and the full text of the "library" is reported
below:

/*Copyright(C)20092013FrancescoNidito
*
* Permission is hereby granted, free of charge, to any
personobtainingacopy
*ofthissoftwareandassociateddocumentationfiles(the
"Software"),todeal
* in the Software without restriction, including without
limitationtherightsto
*use,copy,modify,merge,publish,distribute,sublicense,
and/orsellcopies
* of the Software, and to permit persons to whom the
Softwareisfurnishedtodo
*so,subjecttothefollowingconditions:
*
* The above copyright notice and this permission notice
shallbeincludedinall
*copiesorsubstantialportionsoftheSoftware.
*
*THESOFTWAREISPROVIDED"ASIS",WITHOUTWARRANTYOFANY
KIND,EXPRESSOR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY,
*FITNESSFORAPARTICULARPURPOSEANDNONINFRINGEMENT.IN
NOEVENTSHALLTHE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGESOROTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE,ARISINGFROM,
*OUTOFORINCONNECTIONWITHTHESOFTWAREORTHEUSEOR
OTHERDEALINGSINTHE
*SOFTWARE.
*/

#ifndef_TRY_THROW_CATCH_H_
#define_TRY_THROW_CATCH_H_

#include<stdio.h>
#include<setjmp.h>

/* For the full documentation and explanation of the code


below,pleasereferto
*
http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.htm
l
*/

#defineTRYdo{jmp_bufex_buf__;switch(setjmp(ex_buf__)){
case0:while(1){
#defineCATCH(x)break;casex:
#defineFINALLYbreak;}default:
#defineETRY}}while(0)
#defineTHROW(x)longjmp(ex_buf__,x)
#endif/*!_TRY_THROW_CATCH_H_*/

If you prefer, the code can be downloaded from this link .

CONCLUSIONS
In this documents we learned something about setjmp and longjmp. These two
functions are seen, by the largest part of programmers, as esoteric, dangerous and
difficult to use.

We, also, learned how to use that functions to provide a simple try-throw-
catch system. Our system has some limitations but can be useful and it helped us
to learn something. This last thing could be considered enough by itself.

To implement the try-throw-catch system we stressed the C language weaving


together switch statement and while statement in a non conventional way.

But the must important thing is that we had a lot of fun!

For any feedback, suggestion or comment feel free to mail to me at the address:
francesco [dot] nidito [at] gmail [dot] com

You might also like