Exceptional C

Remo Dentato - Jan 6 '21 - - Dev Community

Don't worry, this is not an article about how great the C language is. No flames ahead. This is about how I added exception handling to C in 100 lines of code, comments included (available here: https://github.com/rdentato/try ).

Exceptions are a way to signal that something unexpected happened and some special measures need to be taken to be able to continue the program (or just exit the program if there is no way the program can execute).

Intended as a way to cleanly handle errors, they have been, sometimes, streteched to become a sort of glorified GOTO which led to some suspicion on their effectiveness (we all know that goto is evil, right?).

The idea is simple: when an exception is thrown, the control jumps back to a specified portion of the program, where the exception is caught, analyzed and proper actions are taken.

Languages that offers exceptions (C++, Java, Python, ...) do it with the try/catch instructions. Something like:

try {
    ... Your code here with calls to distant functions
}
catch {
    ... The code that is executed if an exception has been thrown 
    ... by the code in the try block or any function called by it.
}
Enter fullscreen mode Exit fullscreen mode

Error handling in C, instead, relies on functions returning error codes and/or setting the variable errno. It will be up to the caller to determine if an error occurred, taking the right measures and possibly propagating it up in the calling chain in a meaningful way.
This can be not an easy task because, to do it right, you need to properly plan how the errors will propagate in your applications and you may end up with one of this two, unfavourable, scenarios:

  • The overall logic gets more complicated just to gracefully handle errors;
  • Errors that are really exceptional (e.g. no more memory, file systems full, ...) cause the program to exit or, even, are not handled at all.

I do claim that there are situations where a C program could be written better (i.e. it would easier to write and to mantain) if we only had exceptions in C.

We don't have the try/catch block in C but we do have something that can serve the same purpose: setjmp()/longjmp().
They allow a non local goto: you set the return point with setjmp() and get back to it with a longjmp().
This mechanism is quite seldom used (and quite rightly so!) and involves saving the status in a variable of type jmp_buf that must be passed around to allow longjmp() to jump to the set return point in the code. Not exactly simple or clean.

I wanted to have something simple to use and that's why I created my own version of the try/catch instructions which you can find here: https://github.com/rdentato/try .

You can find on the Internet other implementations of try/catch in C (also based on setjmp()/longjmp()) but I couldn't find one that had all the features I wanted:

  • Fit nicely in the C syntax (no ugly STARTRY/ ENDTRY pairs, for example);
  • Allow throwing exceptions from any called function;
  • Nested try blocks, even within called functions;
  • Information (file and line) about where the exceptions was thrown, for better error messages.
  • Thread safe. (not really a priority for me, but nice to have)

About the last point, the better I could do is to be able to have try/catch working in a single thread but not across threads, which would have been extremely confusing!

Overall, I'm pleased with the result and wanted to share it with you all. Let me know in the comments below if you want me to write a followup on how it works internally, I'll be happy to write one.

Here is how exception handling looks like, you can find real example in the test directory on Github.

  #include "try.h"

  #define OUTOFMEM    1
  #define WRONGINPUT  2
  #define INTERNALERR 3

  try_t catch = 0; // Initialize the exception stack

  void some_other_func()
  {
    ... code ...
    if (invalid(getinput())) throw(WRONGINPUT);
    ... code ...
  }

  int some_func() 
  {
    ... code ...
    try {
      ... code ...
      if (no_more_memory) throw(OUTOFMEM) 

      some_other_func();      // you can trhow exceptions
                              // from other functions 

      ... code ...            // You MUST NEVER jump out of a try/catch block
                              // via return, goto or break.
    }  
    catch(OUTOFMEM) {
      ... code ...
    }
    catch(WRONGINPUT) {
       ... code ...
    }
    catch() {                 // catch any other exception
       ... code ...           // otherwise the program will abort.
    }
    ... code ...
 }
Enter fullscreen mode Exit fullscreen mode
. . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player