EXPECT error handling utility

Table of contents Table of contents

Detailed Description

This tiny library provides simple utilities to manage error handling in a unified way across many software modules.

Rationale.

This header-only C library was born in order to provide a unified abstraction for writing sanity checks and handling errors in a way that is easy to read to a human being. The idea was to be able to write short but meaningful statements that can be easily understood and that could document the error checking process - especially the input argument validation.

What may happen when you call a function?

We distinguish two types of things that may happen if you call a function:

  • it runs throughout and produces a meaningful result that can be handled
  • it aborts because an unexpected state was detected, which cannot be handled

The first case includes both the situations in which the function ends with a success result as well as errors, that can somehow be managed by the caller. For example, if you open a file for writing it may be successfully opened, but it may also happen that the file is already open or the disk is write protected. Such cases usually end with an error code being returned by the function to indicate, that it was unable to handle the request. This typically also includes situations in which the input to the function is outside the valid range the function can handle - for example the filename was too long. We will call these kind of errors recoverable

But what should happen if during file access it turns out that the whole filesystem on the disk is corrupted and nothing can be done to fix it? We call these situations as unrecoverable errors. In such case the application might not be able to perform at all and should be aborted. This should however be considered as a rare and unexpected behavior, where fixing it by the application is not feasible.

Handling recoverable errors with EXPECT

Below are some examples of function input validation with expect:

// This function returns one of 5 coefficients. For coeffNo >= 5 it always returns 0.0.
int getCoefficient(unsigned int coeffNo) {
EXPECT(coeffNo < 5) OR_RETURN (0.0)
static double coeffTab[5] = { 1.0, 1.1, 1.2, 1.3, 1.4 };
return coeffTab[coeffNo];
}
#define EXPECT(expr)
Checks whether expression expr holds true value.
Definition: expect.h:118
#define OR_RETURN(retval)
Returns from the function with given error value.
Definition: expect.h:152

Handling unrecoverable errors with EXPECT

When unrecoverable error happens the expect library calls the EXPECT_OnAbortHandler function that should be defined by the user. The program must not continue operation after calling this function. A typical behavior of such function is to:

  • go to a safe state
  • log the error
  • halt or reset the application

In the above example, to convert to unrecoverable error we would change the implementation this way:

// This function returns one of 5 coefficients. For coeffNo >= 5 it causes the program to abort.
int getCoefficient(unsigned int coeffNo) {
EXPECT(coeffNo < 5) OR_ABORT()
static double coeffTab[5] = { 1.0, 1.1, 1.2, 1.3, 1.4 };
return coeffTab[coeffNo];
}
#define OR_ABORT(why)
Calls the abort handler (EXPECT_OnAbortHandler) with given reason.
Definition: expect.h:144

Standard and extra checks

For convenience the EXPECT library provides two levels of checks - standard checks and extra checks. Extra checks are defined with the _EXTRA suffix. These checks are only performed if the EXPECT_CHECK_EXTRA definition is visible when building the code.

For example:

int myFunc(const char* str) {
EXPECT_EXTRA(NULL != str) OR_RETURN(0)
}
#define EXPECT_EXTRA(expr)
Definition: expect.h:129

Runtime verbosity of abort handler

During initial development of the application, it is benefitial to quickly get the information about the abort location, and reason. To enable this, you have to provide the following macro:

#define EXPECT_VERBOSE
Definition: expect.h:99

Macro Definition Documentation

◆ EXPECT_VERBOSE

#define EXPECT_VERBOSE   0

When enabled, increases verbosity of information passed to EXPECT_OnAbortHandler

Note
Enabling this macro WILL generate measurable memory overhead

◆ EXPECT_INTERNAL_NORETURN

#define EXPECT_INTERNAL_NORETURN   __attribute__((noreturn))

◆ EXPECT

#define EXPECT (   expr)    if (!(expr))

Checks whether expression expr holds true value.

Note
the macro MUST be followed by either OR_RETURN, OR_ABORT, or OR_THROW macro

◆ EXPECT_EXTRA

#define EXPECT_EXTRA (   expr)    if (0)

Checks whether expression expr holds true value.

Note
the macro MUST be followed by either OR_RETURN, OR_ABORT, or OR_THROW macro As the evaluation of the expression may be time consuming, it is possible to globally disable this kind of runtime checks by defining EXPECT_CHECK_EXTRA to 0

◆ EXPECT_ABORT

#define EXPECT_ABORT (   why)    EXPECT_INTERNAL_CALL_ABORT_HANDLER(why)

Calls directly the abort handler (EXPECT_OnAbortHandler) with given reason.

Parameters
[in]whyError message This macro must be preceded either EXPECT, or EXPECT_EXTRA macro

◆ OR_ABORT

#define OR_ABORT (   why)    EXPECT_INTERNAL_CALL_ABORT_HANDLER(why)

Calls the abort handler (EXPECT_OnAbortHandler) with given reason.

Parameters
[in]whyError message This macro must be preceded either EXPECT, or EXPECT_EXTRA macro

◆ OR_RETURN

#define OR_RETURN (   retval)    return retval

Returns from the function with given error value.

Parameters
[in]retvalError value to be returned This macro must be preceded either EXPECT, or EXPECT_EXTRA macro

◆ EXPECT_INTERNAL_CALL_ABORT_HANDLER

#define EXPECT_INTERNAL_CALL_ABORT_HANDLER (   why)    EXPECT_OnAbortHandler("<verbose-disabled>", "<verbose-disabled>", 0)

Function Documentation

◆ EXPECT_OnAbortHandler()

EXPECT_INTERNAL_NORETURN void EXPECT_OnAbortHandler ( char const *  why,
char const *  file,
int  line 
)

Abort handler. The program MUST not continue operation after calling this function. Its context MAY be used to safely restart the program

Parameters
[in]whymessage describing reason why the contract was violated
[in]fileprintable filename
[in]lineline where check was performed