Primitive exception processing using macros

Ricky Bennett March 22, 2013 Coded in C

C doesn't have exception processing but a primitive version can be implemented with macros that use the setjmp and longjmp calls.  The macros are Try, Catch, Finally, and Throw.  These macros may be nested.

The Try macro defines a block that may contain Throw macros to throw an  exception on an error.  The setjmp macro will return 0 if executed directly.  If an exception is thrown, a longjmp will return control to the setjmp point but with a nonzero value, so that the "if" block will fail and pass control to a Catch or Finally block.

The Catch macro will catch a thrown exception with the specified value and perform exception processing.  This macro is optional.

The Finally macro catches all other values that weren't defined by a Catch block.  It can also be used to perform cleanup if an exception is rethrown with a value that won't be caught otherwise.  This macro is optional.

The Throw macro will throw an exception if given a nonzero value.  This allows constructs such as "Throw (DoFunction ( ))" where DoFunction returns an error value and an exception shouldn't be thrown if the result is zero (success).  Note that throwing a constant value will cause the optimizer to eliminate the if block.  The Throw macro may stand alone without a Catch or Finally macro, in which case a thrown exception will simply jump out of the Try block.

#define Try \
      jmp_buf ExceptionBuffer; \
      int ExceptionStatus = setjmp (ExceptionBuffer); \
      if (ExceptionStatus == 0)

#define Catch(Value)    else if (ExceptionStatus == (int) (Value))

#define Finally         else

#define Throw(Value) \
      { \
         int Result = (int) (Value); \
         if (Result != 0) { \
            longjmp (ExceptionBuffer, Result); \
         } \
      }

An example using a function to read the OCR register on an SD card:

uint32_t SDCard_OCR (void) {
   SDCard_ConfigSSP ( );
   Try {

      // The card is asked to send its OCR.  The OCR is
      // placed in module variable mOCRValue by the
      // function.
      Throw (SDCard_SendCommand (CMD58_READ_OCR));
   }

   // Error processing.
   Catch (SD_TIMEOUT) {
      GUI_SendMessage (SDCARD_TIMEOUT);
   }
   Catch (SD_NO_CARD) {
      GUI_SendMessage (SDCARD_NOT_FOUND);
   }
   Finally {   // Unknown SD card error
      Throw (SD_NO_CARD);   // Rethrow the exception
   }

   // Executed even if an exception thrown:
   SDCard_DeconfigSSP ( );
   return mOCRValue;   // 0xFFFFFFFF if error
}