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:
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:
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:
In the above example, to convert to unrecoverable error we would change the implementation this way:
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:
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 0 |
When enabled, increases verbosity of information passed to EXPECT_OnAbortHandler
#define EXPECT_INTERNAL_NORETURN __attribute__((noreturn)) |
#define EXPECT | ( | expr | ) | if (!(expr)) |
Checks whether expression expr holds true value.
#define EXPECT_EXTRA | ( | expr | ) | if (0) |
Checks whether expression expr holds true value.
#define EXPECT_ABORT | ( | why | ) | EXPECT_INTERNAL_CALL_ABORT_HANDLER(why) |
Calls directly the abort handler (EXPECT_OnAbortHandler) with given reason.
[in] | why | Error message This macro must be preceded either EXPECT, or EXPECT_EXTRA macro |
#define OR_ABORT | ( | why | ) | EXPECT_INTERNAL_CALL_ABORT_HANDLER(why) |
Calls the abort handler (EXPECT_OnAbortHandler) with given reason.
[in] | why | Error message This macro must be preceded either EXPECT, or EXPECT_EXTRA macro |
#define OR_RETURN | ( | retval | ) | return retval |
Returns from the function with given error value.
[in] | retval | Error value to be returned This macro must be preceded either EXPECT, or EXPECT_EXTRA macro |
#define EXPECT_INTERNAL_CALL_ABORT_HANDLER | ( | why | ) | EXPECT_OnAbortHandler("<verbose-disabled>", "<verbose-disabled>", 0) |
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
[in] | why | message describing reason why the contract was violated |
[in] | file | printable filename |
[in] | line | line where check was performed |