139

I was thinking today about the try/catch blocks existent in another languages. Googled for a while this but with no result. From what I know, there is not such a thing as try/catch in C. However, is there a way to "simulate" them?
Sure, there is assert and other tricks but nothing like try/catch, that also catch the raised exception. Thank you

Andrew
  • 6,254
  • 16
  • 59
  • 93
  • 6
    Exception-like mechanisms are not going to be generally useful without a mechanism to automatically free resources when the stack is unwound. C++ uses RAII; Java, C#, Python, etc. use garbage collectors. (And note that garbage collectors free only memory. To automatically free other types of resources, they also add things like finalizers or context managers...) – jamesdlin May 03 '15 at 05:34
  • @jamesdlin, Why couldn't we do RAII with C? – Pacerier May 15 '15 at 22:45
  • 1
    @Pacerier RAII requires calling functions automatically when objects are destroyed (i.e., destructors). How do you propose doing that in C? – jamesdlin May 15 '15 at 23:02

14 Answers14

116

C itself doesn't support exceptions but you can simulate them to a degree with setjmp and longjmp calls.

static jmp_buf s_jumpBuffer;

void Example() { 
  if (setjmp(s_jumpBuffer)) {
    // The longjmp was executed and returned control here
    printf("Exception happened here\n");
  } else {
    // Normal code execution starts here
    Test();
  }
}

void Test() {
  // Rough equivalent of `throw`
  longjmp(s_jumpBuffer, 42);
}

This website has a nice tutorial on how to simulate exceptions with setjmp and longjmp

Saurabh P Bhandari
  • 6,014
  • 1
  • 19
  • 50
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 1
    awesome solution! is this solution cross? It worked for me on MSVC2012 but didn't in MacOSX Clang compiler. – mannysz Sep 05 '16 at 18:03
  • 2
    clue me in: I thought that try catch clauses allowed you to catch exceptions (like dividing by zero). This function seems to only allow you to catch exceptions that you throw yourself. Real exceptions are not thrown by calling longjmp right? If I use this code to do something like `try{ x = 7 / 0; } catch(divideByZeroException) {print('divided by zero')}; ` it won't work right? – Sam Feb 13 '19 at 15:52
  • Devide by zero is not even an exception in C++, to handle it you need to either check the divisor is not zero and handle it or handle the SIGFPE that is thrown when you run a devide by zero formula. – James Mar 27 '19 at 13:15
  • I come from the Java world where you can catch a general exception. I have the same concern as @Sam. What about Null pointer dereference? Or calling a function which fails internally? – zeitgeist Mar 18 '21 at 19:34
  • 4
    @Heroman C doesn't have exceptions _at all_. C++ has exceptions but their use is generally discouraged; and unlike Java and .NET, there is no language-standard exception base type (i.e. [you can throw anything](https://stackoverflow.com/questions/27179011), not just subclasses of `std:exception`). In C++ dereferencing a `nullptr` [does not throw anything](https://stackoverflow.com/questions/1823721/), instead it's UB which means you need to see if your compiler or OS has a way of gracefully handling access-violations (most do, but it's not in ISO Standard C++). – Dai Nov 16 '21 at 01:21
27

You use goto in C for similar error handling situations.
That is the closest equivalent of exceptions you can get in C.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • 4
    @JensGustedt This is exactly what goto is currently used for very often and example where it makes sense (setjmp/ljmp is better alternative, but label+goto is _typically_ used more). – Tomas Pruzina Feb 09 '13 at 12:26
  • 2
    @AoeAoe, probably `goto` is more used for error handling, but so what? The question is not about error handling as such but explicitly about try/catch equivalents. `goto` is not an equivalent fro try/catch since it is restricted to the same function. – Jens Gustedt Feb 09 '13 at 14:40
  • @JensGustedt I kinda reacted towards hate/fear of goto and people who use it (my teachers told me scary stories of goto usage on university too). [OT] Only thing that is really, really risky and 'cloudy' thing about goto is to 'goto backwards', but I've seen that in Linux VFS (git blame guy swore that it was performance critical-beneficial). – Tomas Pruzina Feb 12 '13 at 18:56
  • 1
    See [systemctl sources](https://github.com/systemd/systemd/blob/master/src/systemctl/systemctl.c) for legitimate uses of `goto` as a try/catch mechanism used in a modern, widly-accepted, peer reviewed source. Search `goto` for a "throw" equivalent, and `finish` for a "catch" equivalent. – Stewart Feb 28 '19 at 13:36
  • This answer seems to be the best and portable, see how php/bcmath uses it: https://github.com/php/php-src/blob/php-8.0.7/ext/bcmath/bcmath.c#L455 – mvorisek Jun 25 '21 at 14:39
23

Ok, I couldn't resist replying to this. Let me first say I don't think it's a good idea to simulate this in C as it really is a foreign concept to C.

We can use abuse the preprocessor and local stack variables to give use a limited version of C++ try/throw/catch.

Version 1 (local scope throws)

#include <stdbool.h>

#define try bool __HadError=false;
#define catch(x) ExitJmp:if(__HadError)
#define throw(x) {__HadError=true;goto ExitJmp;}

Version 1 is a local throw only (can't leave the function's scope). It does rely on C99's ability to declare variables in code (it should work in C89 if the try is first thing in the function).

This function just makes a local var so it knows if there was an error and uses a goto to jump to the catch block.

For example:

#include <stdio.h>
#include <stdbool.h>

#define try bool __HadError=false;
#define catch(x) ExitJmp:if(__HadError)
#define throw(x) {__HadError=true;goto ExitJmp;}

int main(void)
{
    try
    {
        printf("One\n");
        throw();
        printf("Two\n");
    }
    catch(...)
    {
        printf("Error\n");
    }
    return 0;
}

This works out to something like:

int main(void)
{
    bool HadError=false;
    {
        printf("One\n");
        {
            HadError=true;
            goto ExitJmp;
        }
        printf("Two\n");
    }
ExitJmp:
    if(HadError)
    {
        printf("Error\n");
    }
    return 0;
}

Version 2 (scope jumping)

#include <stdbool.h>
#include <setjmp.h>

jmp_buf *g__ActiveBuf;

#define try jmp_buf __LocalJmpBuff;jmp_buf *__OldActiveBuf=g__ActiveBuf;bool __WasThrown=false;g__ActiveBuf=&__LocalJmpBuff;if(setjmp(__LocalJmpBuff)){__WasThrown=true;}else
#define catch(x) g__ActiveBuf=__OldActiveBuf;if(__WasThrown)
#define throw(x) longjmp(*g__ActiveBuf,1);

Version 2 is a lot more complex but basically works the same way. It uses a long jump out of the current function to the try block. The try block then uses an if/else to skip the code block to the catch block which check the local variable to see if it should catch.

The example expanded again:

jmp_buf *g_ActiveBuf;

int main(void)
{
    jmp_buf LocalJmpBuff;
    jmp_buf *OldActiveBuf=g_ActiveBuf;
    bool WasThrown=false;
    g_ActiveBuf=&LocalJmpBuff;

    if(setjmp(LocalJmpBuff))
    {
        WasThrown=true;
    }
    else
    {
        printf("One\n");
        longjmp(*g_ActiveBuf,1);
        printf("Two\n");
    }
    g_ActiveBuf=OldActiveBuf;
    if(WasThrown)
    {
        printf("Error\n");
    }
    return 0;
}

This uses a global pointer so the longjmp() knows what try was last run. We are using abusing the stack so child functions can also have a try/catch block.

Using this code has a number of down sides (but is a fun mental exercise):

  • It will not free allocated memory as there are no deconstructors being called.
  • You can't have more than 1 try/catch in a scope (no nesting)
  • You can't actually throw exceptions or other data like in C++
  • Not thread safe at all
  • You are setting up other programmers for failure because they will likely not notice the hack and try using them like C++ try/catch blocks.
Paul Hutchinson
  • 1,578
  • 15
  • 21
  • nice alternate solutions. – Haseeb Mir Mar 17 '18 at 05:35
  • version 1 is nice idea, but that __HadError variable would need to be reset or scoped. Otherwise you will not be able to use more than one try-catch in the same block. Maybe use a global function like `bool __ErrorCheck(bool &e){bool _e = e;e=false;return _e;}`. But the local variable would also get redefined, so things get a little out of hand. – flamewave000 Mar 11 '20 at 20:06
  • Yes, it is limited to one try-catch in the same function. A bigger issue then the variable however is the label as you can't have duplicate labels in the same function. – Paul Hutchinson Mar 23 '20 at 15:25
  • Actually i have the same problem that i cant use t2o or more than try catch in one scope, do u have solution for it ? – Parham sagharichi ha Sep 18 '21 at 06:34
  • @Parhamsagharichiha there is no easy fix for this. I would suggest you just use the goto directly instead of trying to use a try..catch. – Paul Hutchinson Sep 23 '21 at 21:05
10

In C99, you can use setjmp/longjmp for non-local control flow.

Within a single scope, the generic, structured coding pattern for C in the presence of multiple resource allocations and multiple exits uses goto, like in this example. This is similar to how C++ implements destructor calls of automatic objects under the hood, and if you stick to this diligently, it should allow you for a certain degree of cleanness even in complex functions.

Community
  • 1
  • 1
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
6

While some of the other answers have covered the simple cases using setjmp and longjmp, in a real application there's two concerns that really matter.

  1. Nesting of try/catch blocks. Using a single global variable for your jmp_buf will make these not work.
  2. Threading. A single global variable for you jmp_buf will cause all kinds of pain in this situation.

The solution to these is to maintain a thread-local stack of jmp_buf that get updated as you go. (I think this is what lua uses internally).

So instead of this (from JaredPar's awesome answer)

static jmp_buf s_jumpBuffer;

void Example() { 
  if (setjmp(s_jumpBuffer)) {
    // The longjmp was executed and returned control here
    printf("Exception happened\n");
  } else {
    // Normal code execution starts here
    Test();
  }
}

void Test() {
  // Rough equivalent of `throw`
  longjump(s_jumpBuffer, 42);
}

You'd use something like:

#define MAX_EXCEPTION_DEPTH 10;
struct exception_state {
  jmp_buf s_jumpBuffer[MAX_EXCEPTION_DEPTH];
  int current_depth;
};

int try_point(struct exception_state * state) {
  if(current_depth==MAX_EXCEPTION_DEPTH) {
     abort();
  }
  int ok = setjmp(state->jumpBuffer[state->current_depth]);
  if(ok) {
    state->current_depth++;
  } else {
    //We've had an exception update the stack.
    state->current_depth--;
  }
  return ok;
}

void throw_exception(struct exception_state * state) {
  longjump(state->current_depth-1,1);
}

void catch_point(struct exception_state * state) {
    state->current_depth--;
}

void end_try_point(struct exception_state * state) {
    state->current_depth--;
}

__thread struct exception_state g_exception_state; 

void Example() { 
  if (try_point(&g_exception_state)) {
    catch_point(&g_exception_state);
    printf("Exception happened\n");
  } else {
    // Normal code execution starts here
    Test();
    end_try_point(&g_exception_state);
  }
}

void Test() {
  // Rough equivalent of `throw`
  throw_exception(g_exception_state);
}

Again a more realistic version of this would include some way to store error information into the exception_state, better handling of MAX_EXCEPTION_DEPTH (maybe using realloc to grow the buffer, or something like that).

DISCLAIMER: The above code was written without any testing whatsoever. It is purely so you get an idea of how to structure things. Different systems and different compilers will need to implement the thread local storage differently. The code probably contains both compile errors and logic errors - so while you're free to use it as you choose, TEST it before using it ;)

Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
5

This is another way to do error handling in C which is more performant than using setjmp/longjmp. Unfortunately, it will not work with MSVC but if using only GCC/Clang is an option, then you might consider it. Specifically, it uses the "label as value" extension, which allows you to take the address of a label, store it in a value and and jump to it unconditionally. I'll present it using an example:

GameEngine *CreateGameEngine(GameEngineParams const *params)
{
    /* Declare an error handler variable. This will hold the address
       to jump to if an error occurs to cleanup pending resources.
       Initialize it to the err label which simply returns an
       error value (NULL in this example). The && operator resolves to
       the address of the label err */
    void *eh = &&err;

    /* Try the allocation */
    GameEngine *engine = malloc(sizeof *engine);
    if (!engine)
        goto *eh; /* this is essentially your "throw" */

    /* Now make sure that if we throw from this point on, the memory
       gets deallocated. As a convention you could name the label "undo_"
       followed by the operation to rollback. */
    eh = &&undo_malloc;

    /* Now carry on with the initialization. */
    engine->window = OpenWindow(...);
    if (!engine->window)
        goto *eh;   /* The neat trick about using approach is that you don't
                       need to remember what "undo" label to go to in code.
                       Simply go to *eh. */

    eh = &&undo_window_open;

    /* etc */

    /* Everything went well, just return the device. */
    return device;

    /* After the return, insert your cleanup code in reverse order. */
undo_window_open: CloseWindow(engine->window);
undo_malloc: free(engine);
err: return NULL;
}

If you so wish, you could refactor common code in defines, effectively implementing your own error-handling system.

/* Put at the beginning of a function that may fail. */
#define declthrows void *_eh = &&err

/* Cleans up resources and returns error result. */
#define throw goto *_eh

/* Sets a new undo checkpoint. */
#define undo(label) _eh = &&undo_##label

/* Throws if [condition] evaluates to false. */
#define check(condition) if (!(condition)) throw

/* Throws if [condition] evaluates to false. Then sets a new undo checkpoint. */
#define checkpoint(label, condition) { check(condition); undo(label); }

Then the example becomes

GameEngine *CreateGameEngine(GameEngineParams const *params)
{
    declthrows;

    /* Try the allocation */
    GameEngine *engine = malloc(sizeof *engine);
    checkpoint(malloc, engine);

    /* Now carry on with the initialization. */
    engine->window = OpenWindow(...);
    checkpoint(window_open, engine->window);

    /* etc */

    /* Everything went well, just return the device. */
    return device;

    /* After the return, insert your cleanup code in reverse order. */
undo_window_open: CloseWindow(engine->window);
undo_malloc: free(engine);
err: return NULL;
}
keebus
  • 990
  • 1
  • 8
  • 15
4

A quick google search yields kludgey solutions such as this that use setjmp/longjmp as others have mentioned. Nothing as straightforward and elegant as C++/Java's try/catch. I'm rather partial to Ada's exception handling myself.

Check everything with if statements :)

James Adam
  • 2,324
  • 3
  • 16
  • 19
4

This can be done with setjmp/longjmp in C. P99 has a quite comfortable toolset for this that also is consistent with the new thread model of C11.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
3

In C, you can "emulate" exceptions along with automatic "object reclamation" through manual use of if + goto for explicit error handling.

I often write C code like the following (boiled down to highlight error handling):

#include <assert.h>

typedef int errcode;

errcode init_or_fail( foo *f, goo *g, poo *p, loo *l )
{
    errcode ret = 0;

    if ( ( ret = foo_init( f ) ) )
        goto FAIL;

    if ( ( ret = goo_init( g ) ) )
        goto FAIL_F;

    if ( ( ret = poo_init( p ) ) )
        goto FAIL_G;

    if ( ( ret = loo_init( l ) ) )
        goto FAIL_P;

    assert( 0 == ret );
    goto END;

    /* error handling and return */

    /* Note that we finalize in opposite order of initialization because we are unwinding a *STACK* of initialized objects */

FAIL_P:
    poo_fini( p );

FAIL_G:
    goo_fini( g );

FAIL_F:
    foo_fini( f );

FAIL:
    assert( 0 != ret );

END:
    return ret;        
}

This is completely standard ANSI C, separates the error handling away from your mainline code, allows for (manual) stack unwinding of initialized objects much like C++ does, and it is completely obvious what is happening here. Because you are explicitly testing for failure at each point it does make it easier to insert specific logging or error handling at each place an error can occur.

If you don't mind a little macro magic, then you can make this more concise while doing other things like logging errors with stack traces. For example:

#include <assert.h>
#include <stdio.h>
#include <string.h>

#define TRY( X, LABEL ) do { if ( ( X ) ) { fprintf( stderr, "%s:%d: Statement '%s' failed! %d, %s\n", __FILE__, __LINE__, #X, ret, strerror( ret ) ); goto LABEL; } while ( 0 )

typedef int errcode;

errcode init_or_fail( foo *f, goo *g, poo *p, loo *l )
{
    errcode ret = 0;

    TRY( ret = foo_init( f ), FAIL );
    TRY( ret = goo_init( g ), FAIL_F );
    TRY( ret = poo_init( p ), FAIL_G );
    TRY( ret = loo_init( l ), FAIL_P );

    assert( 0 == ret );
    goto END;

    /* error handling and return */

FAIL_P:
    poo_fini( p );

FAIL_G:
    goo_fini( g );

FAIL_F:
    foo_fini( f );

FAIL:
    assert( 0 != ret );

END:
    return ret;        
}

Of course, this isn't as elegant as C++ exceptions + destructors. For example, nesting multiple error handling stacks within one function this way isn't very clean. Instead, you'd probably want to break those out into self contained sub functions that similarly handle errors, initialize + finalize explicitly like this.

This also only works within a single function and won't keep jumping up the stack unless higher level callers implement similar explicit error handling logic, whereas a C++ exception will just keep jumping up the stack until it finds an appropriate handler. Nor does it allow you to throw an arbitrary type, but instead only an error code.

Systematically coding this way (i.e. - with a single entry and single exit point) also makes it very easy to insert pre and post ("finally") logic that will execute no matter what. You just put your "finally" logic after the END label.

jschultz410
  • 2,849
  • 14
  • 22
  • 1
    Very nice. I tend to do something similar. goto is great for this scenario. The only difference is I don't see the need for that last "goto END", I just insert a success return at that point, a fail return after the rest. – Neil Roy Jan 30 '19 at 21:58
  • 1
    Thanks @NeilRoy The reason for the goto END is that I like the vast majority of my functions to have a single entry point and a single exit point. That way if I want to add some "finally" logic to any function I always easily can without needing to worry there is some other hidden returns lurking somewhere. :) – jschultz410 Jan 31 '19 at 20:51
2

Warning: the following is not very nice but it does the job.

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    unsigned int  id;
    char         *name;
    char         *msg;
} error;

#define _printerr(e, s, ...) fprintf(stderr, "\033[1m\033[37m" "%s:%d: " "\033[1m\033[31m" e ":" "\033[1m\033[37m" " ‘%s_error’ " "\033[0m" s "\n", __FILE__, __LINE__, (*__err)->name, ##__VA_ARGS__)
#define printerr(s, ...) _printerr("error", s, ##__VA_ARGS__)
#define printuncaughterr() _printerr("uncaught error", "%s", (*__err)->msg)

#define _errordef(n, _id) \
error* new_##n##_error_msg(char* msg) { \
    error* self = malloc(sizeof(error)); \
    self->id = _id; \
    self->name = #n; \
    self->msg = msg; \
    return self; \
} \
error* new_##n##_error() { return new_##n##_error_msg(""); }

#define errordef(n) _errordef(n, __COUNTER__ +1)

#define try(try_block, err, err_name, catch_block) { \
    error * err_name = NULL; \
    error ** __err = & err_name; \
    void __try_fn() try_block \
    __try_fn(); \
    void __catch_fn() { \
        if (err_name == NULL) return; \
        unsigned int __##err_name##_id = new_##err##_error()->id; \
        if (__##err_name##_id != 0 && __##err_name##_id != err_name->id) \
            printuncaughterr(); \
        else if (__##err_name##_id != 0 || __##err_name##_id != err_name->id) \
            catch_block \
    } \
    __catch_fn(); \
}

#define throw(e) { *__err = e; return; }

_errordef(any, 0)

Usage:

errordef(my_err1)
errordef(my_err2)

try ({
    printf("Helloo\n");
    throw(new_my_err1_error_msg("hiiiii!"));
    printf("This will not be printed!\n");
}, /*catch*/ any, e, {
    printf("My lovely error: %s %s\n", e->name, e->msg);
})

printf("\n");

try ({
    printf("Helloo\n");
    throw(new_my_err2_error_msg("my msg!"));
    printf("This will not be printed!\n");
}, /*catch*/ my_err2, e, {
    printerr("%s", e->msg);
})

printf("\n");

try ({
    printf("Helloo\n");
    throw(new_my_err1_error());
    printf("This will not be printed!\n");
}, /*catch*/ my_err2, e, {
    printf("Catch %s if you can!\n", e->name);
})

Output:

Helloo
My lovely error: my_err1 hiiiii!

Helloo
/home/naheel/Desktop/aa.c:28: error: ‘my_err2_error’ my msg!

Helloo
/home/naheel/Desktop/aa.c:38: uncaught error: ‘my_err1_error’ 

Keep on mind that this is using nested functions and __COUNTER__. You'll be on the safe side if you're using gcc.

Naheel
  • 497
  • 3
  • 13
1

Redis use goto to simulate try/catch, IMHO it is very clean and elegant:

/* Save the DB on disk. Return REDIS_ERR on error, REDIS_OK on success. */
int rdbSave(char *filename) {
    char tmpfile[256];
    FILE *fp;
    rio rdb;
    int error = 0;

    snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
    fp = fopen(tmpfile,"w");
    if (!fp) {
        redisLog(REDIS_WARNING, "Failed opening .rdb for saving: %s",
            strerror(errno));
        return REDIS_ERR;
    }

    rioInitWithFile(&rdb,fp);
    if (rdbSaveRio(&rdb,&error) == REDIS_ERR) {
        errno = error;
        goto werr;
    }

    /* Make sure data will not remain on the OS's output buffers */
    if (fflush(fp) == EOF) goto werr;
    if (fsync(fileno(fp)) == -1) goto werr;
    if (fclose(fp) == EOF) goto werr;

    /* Use RENAME to make sure the DB file is changed atomically only
     * if the generate DB file is ok. */
    if (rename(tmpfile,filename) == -1) {
        redisLog(REDIS_WARNING,"Error moving temp DB file on the final destination: %s", strerror(errno));
        unlink(tmpfile);
        return REDIS_ERR;
    }
    redisLog(REDIS_NOTICE,"DB saved on disk");
    server.dirty = 0;
    server.lastsave = time(NULL);
    server.lastbgsave_status = REDIS_OK;
    return REDIS_OK;

werr:
    fclose(fp);
    unlink(tmpfile);
    redisLog(REDIS_WARNING,"Write error saving DB on disk: %s", strerror(errno));
    return REDIS_ERR;
}
Forrest Ye
  • 826
  • 6
  • 11
  • The code is broken. `errno` must only be used just after the failed system call and not three calls later. – ceving Sep 04 '15 at 14:06
  • This code duplicates the error handling logic in multiple places and may do incorrect things like calling fclose(fp) multiple times. It'd be far better to use multiple labels and encode what still needs to be reclaimed using those labels (rather than just one for all errors) and then jump into the correct error handling spot depending on where in the code the error occurs. – jschultz410 Jul 17 '18 at 19:30
0

If you're using C with Win32, you can leverage its Structured Exception Handling (SEH) to simulate try/catch.

If you're using C in platforms that don't support setjmp() and longjmp(), have a look at this Exception Handling of pjsip library, it does provide its own implementation

onmyway133
  • 45,645
  • 31
  • 257
  • 263
0

After studying the answers given above, I set up a system that automatically handles nested exceptions well. Here is the code I wrote to test my system:

#include "MyOtherTricks.h"
#include "Exceptions.h"

void Testing_InnerMethod();
void Testing_PossibleExceptionThrower();

void TestExceptionHandling()
{
    try
    {
        Testing_InnerMethod();
        Say("The inner method exited without an exception.");
    }
    catch (Exception)
    {
        Say("I caught an Exception that the inner method did not catch.");
    }
    end_try
}

void Testing_InnerMethod()
{
    try
    {
        Say("I am in a try block.");
        Testing_PossibleExceptionThrower();
        Say("The possible exception thrower didn't throw an exception.");
    }
    catch (ExceptionSubtype1)
        Say("I caught an exception, subtype 1.");
    catch (ExceptionSubtype2)
    {
        Say("I caught an exception, subtype 2.");
        Say("I will now rethrow it.");
        throw(exception);
    }
    end_try
}

void Testing_PossibleExceptionThrower()
{
    Say("Here is the possible exception thrower.");
    throw(new(ExceptionSubtype2));                          // To further test exception handling, replace ExceptionSubtype2 in this line with Exception or ExceptionSubtype1, or comment out this line entirely.
    Say("No, I won't throw an exception!");
}

The example code relies on two files, Exceptions.h and Exceptions.c. Here is Exceptions.h:

#include <setjmp.h>

extern jmp_buf* Exception_Handler;

#define try                     do                                                                  \
                                {                                                                   \
                                    jmp_buf* outerExceptionHandler = Exception_Handler;             \
                                    jmp_buf exceptionHandler;                                       \
                                    Exception_Handler = &exceptionHandler;                          \
                                    Exception exception = (Exception)setjmp(exceptionHandler);      \
                                    if (exception != 0) Exception_Handler = outerExceptionHandler;  \
                                    if (exception == 0)                                             \
                                    {                                                               \
                                        // The try block goes here. It must not include a return statement or anything else that exits the try...end_try block, because then the outer exception handler will not be restored.
#define catch(exceptionType)            Exception_Handler = outerExceptionHandler;                  \
                                    }                                                               \
                                    else if (Object_IsSomeTypeOf(exception, exceptionType))         \
                                    {
                                        // The catch block goes here. It may include a return statement or anything else that exits the try...end_try block. A break statement will exit only the try...end_try block.
#define end_try                     }                                                               \
                                    else                                                            \
                                        throw(exception);                                           \
                                } while(0);

void throw(Exception exception);

And here is Exceptions.c:

#include "MyOtherTricks.h"
#include "Exceptions.h"

jmp_buf* Exception_Handler = 0;

void throw(Exception exception)
{
    if (Exception_Handler == 0) FailBecause("Uncaught exception.");
    longjmp(*Exception_Handler, (int)exception);
}

Note that this code references some additional methods that I'm not including here (because class inheritance in C is off-topic). To make this code work for you, you'll have to understand this code well enough to replace a few things. In particular, if you want to distinguish between different types of exceptions, you'll need to realize that this code assumes that Object_IsSomeTypeOf(new(ExceptionSubtype1), Exception) returns true and Object_IsSomeTypeOf(new(ExceptionSubtype1), ExceptionSubtype2) returns false, and you'll need to either make your own version of my Object_IsSomeTypeOf macro or replace it with something else.

Aulimaitar
  • 119
  • 5
-1

Perhaps not a major language (unfortunately), but in APL, theres the ⎕EA operation (stand for Execute Alternate).

Usage: 'Y' ⎕EA 'X' where X and Y are either code snippets supplied as strings or function names.

If X runs into an error, Y (usually error-handling) will be executed instead.

mappo
  • 1
  • 2
    Hi mappo, welcome to StackOverflow. While interesting, the question was specifically about doing this in C. So this doesn't really *answer the question.* – luser droog Apr 11 '13 at 06:10