Simulating try-catch block in C

Using try-catch blocks in any programming language provides a way to handle errors and deal with them in an elegant manner. Not only the code looks beautiful, it is also easy to write and comprehend. try-catch blocks are an integral part of most of the Object oriented programming languages, but our very own 'C' doesn't have a counterpart. But, that doesn't mean we need to live with this limitation. There are many ways to simulate the same in language C. In this article we will be discussing one such easy way to implement try-catch block in C.

Let us first have a look at the syntax of a try-catch block:

try {
    code-section
} catch (exception-variable) {
    exception-handler-code
}

* How it Works ?
    As soon as any error occurs in the code-section (we call this situation as - exception caught), the execution of program leaves the code-section and enters the exception-handler-code, where we can write the error handling code based on the error found (we call this situation as - exception handling). If no error is caught, the program skips the exception-handler-code.

* Simulation of exception handling in C.
    To achieve the same effect as above, we can simply write a few #define macros and we are all set to GO !!
   
#define NULL 0
#define ERR  1
#define INFO 2

#define _try

#define _call(e, fun, args) {\
    if ((e=(fun args)) != NULL) {\
        goto __##e##__; }\
    }

#define _catch(e) goto __##e##__;\
__##e##__: if (e!=NULL)

#define _throw(e, severity, c) {                    \
    e = (ex_t *)malloc(sizeof(ex_t));\
    e->sev = severity; e->code = c; goto __##e##__; \
    }

#define _free(e) {free(e); e = NULL; }

typedef struct {
    int sev;
    int code;
} ex_t;

ex_t *fun1(int, int);

int main()
{
    ex_t *e = NULL;
    _try {
        printf("Function call - 1\n");
        _call(e, fun1, (5, 5));

        printf("Function call - 2\n");
        _call(e, fun1, (5, 10));

        printf("All fine if you are here\n");
    } _catch(e) {
        if (e->sev == ERR) {
            // displayexception(e);
            printf("Exception caught with severity - %d and code %d\n", e->sev, e->code);
        }
        _free(e);
    }
}

ex_t *fun1(int a, int b)
{
    ex_t *e = NULL;

    _try {

        if (a != b)
            _throw(e, ERR, 100);

    } _catch(e) {
    }

    return e;
}


And here is the output -

Function call - 1
Function call - 2
Exception caught with severity - 1 and code 100


* How it works ?
    I guess the above piece of code is self-explanatory, still for the sake of completeness let us look at the preprocessor output:

typedef struct {
 int sev;
 int code;
} ex_t;

ex_t *fun1(int, int);

int main()
{
 ex_t *e = 0;
 {
  printf("Function call - 1\n");
  { if ((e=(fun1 (5, 5))) != 0) { goto __e__; } };

  printf("Function call - 2\n");
  { if ((e=(fun1 (5, 10))) != 0) { goto __e__; } };

  printf("All fine if you are here\n");
 } goto __e__;
__e__: if (e!=0) {
  if (e->sev == 1) {
   printf("Exception caught with severity - %d and code %d\n", e->sev, e->code);
  }
  {free(e); e = 0; };
 }
}

ex_t *fun1(int a, int b)
{
 ex_t *e = 0;

 {

  if (a != b)
   { e = (ex_t *)malloc(sizeof(ex_t)); e->sev = 1; e->code = 100; goto __e__; };

 } goto __e__;
__e__: if (e!=0) {
 }

 return e;
}


The macros defined here provide a way to jump around the code and change the flow of program. The idea is to use _call macro to call a function wherein we pass the arguments along with the exception handler variable. To throw an exception we use _throw macro. This macro allocates the exception handler structure with the severity being passed to it. We can pass other arguments as required. Also, this macro would use goto to jump to the label __e__ where we need to put the error handling code. We may choose to return the exception as such to the main calling routine which is main() in this case. In this main() routine we are handling the exceptions with severity ERR and displaying it before freeing the exception with the use of macro _free. You can write any piece of code to handle the erros the way you want.

Hence, we can see from the above output that with the wise use of goto statements we can change the program flow to suit our needs of handling exceptions in the same way other languages do.

This pretty much summarizes our topic at hand, but this is by no means an end to your skills of improvisation. You can enhance the exception structure to include error messages, classification of errors, linked list of exceptions to contain multiple error ans so on....

-Pankaj Pal
Happy Programming !!

comments powered by Disqus