exceptions4c 4.0
Exceptions for C
Loading...
Searching...
No Matches
exceptions4c.h File Reference

An exception handling library for C. More...

#include <stdlib.h>
#include <setjmp.h>

Data Structures

struct  e4c_exception_type
 Represents a category of problematic situations in a program. More...
 
struct  e4c_debug_info
 Contains debugging information about an exception. More...
 
struct  e4c_exception
 Represents a specific occurrence of an exceptional situation in a program. More...
 
struct  e4c_context
 Contains the configuration and the current status of exceptions. More...
 

Macros

#define EXCEPTIONS4C   4
 Returns the major version number of this library.
 
#define TRY
 Introduces a block of code that may throw exceptions during execution.
 
#define CATCH(type)
 Introduces a block of code that handles exceptions thrown by a preceding TRY block.
 
#define CATCH_ALL
 Introduces a block of code that handles any exception thrown by a preceding TRY block, regardless of its type.
 
#define FINALLY
 Introduces a block of code that is executed after a TRY block, regardless of whether an exception was thrown or not.
 
#define THROW(type, format, ...)
 Throws an exception, interrupting the normal flow of execution.
 
#define RETRY(max, type, format, ...)
 Repeats the previous TRY or USE block entirely.
 
#define USING(acquisition, test, disposal)
 Introduces a block of code with automatic resource acquisition and disposal.
 
#define WITH(disposal)
 Opens a block of code with automatic resource disposal.
 
#define USE(test)
 Closes a block of code with automatic resource disposal.
 
#define REACQUIRE(max, type, format, ...)
 Repeats the previous WITH or USING block entirely.
 

Functions

void e4c_set_context_supplier (struct e4c_context *(*supplier)(void))
 Sets the exception context supplier.
 
struct e4c_contexte4c_get_context (void)
 Supplies the current exception context.
 
const struct e4c_exceptione4c_get_exception (void)
 Retrieves the last exception that was thrown.
 
bool e4c_is_uncaught (void)
 Determines whether the thrown exception was not caught.
 

Detailed Description

An exception handling library for C.

This library consists of two files:

To use it in your project, include the header file in your source code files.

#include <exceptions4c.h>
An exception handling library for C.

And then link your program against the library code.

Version
4.0.0
Author
Guillermo Calvo
See also
For more information, visit the project on GitHub

Macro Definition Documentation

◆ EXCEPTIONS4C

#define EXCEPTIONS4C   4

Returns the major version number of this library.

◆ TRY

#define TRY
Value:
\
EXCEPTIONS4C_START_BLOCK(false) \
if (e4c_try(EXCEPTIONS4C_DEBUG))

Introduces a block of code that may throw exceptions during execution.

The TRY block is used to define a section of code where exceptions MAY occur. It allows you to handle exceptions gracefully using other blocks that follow it. If an exception occurs, control is transferred to the appropriate block.

A single TRY block MAY be followed by:

  1. One or more CATCH blocks to handle specific types of exceptions.
  2. Optionally, one CATCH_ALL block to handle all exception types (if present, it MUST appear after all CATCH blocks).
  3. Optionally, one FINALLY block to execute cleanup code, regardless of whether an exception was thrown or caught.
Attention
These blocks MUST NOT be exited through any of: goto, break, continue, or return.
Important
Local variables in the function containing the TRY block MUST be volatile because these blocks invoke setjump and their values would be indeterminate if they had been changed since the invocation.

Example:

/* Returns the status of a pet by id */
pet_status get_pet_status(int id) {
pet_status status = ERROR;
TRY {
status = pet_find(id)->status;
}
return status;
}
See also
CATCH
CATCH_ALL
FINALLY

◆ CATCH

#define CATCH ( type)
Value:
\
else if (e4c_catch(&type, EXCEPTIONS4C_DEBUG))

Introduces a block of code that handles exceptions thrown by a preceding TRY block.

Use this macro to to handle a specific type of exceptions when they occur.

If type is equal to (or a supertype of) the type of the thrown exception, then this block will be used to handle it.

One or more CATCH blocks MAY follow a TRY block. If type doesn't match the thrown exception, then this block will be ignored, and the exception MAY be caught by the following CATCH or CATCH_ALL blocks.

Important
When looking for a match, CATCH blocks are inspected in the order they appear. If you place a generic handler before a more specific one, the second block will be unreachable.
Attention
These blocks MUST NOT be exited through any of: goto, break, continue, or return.
Parameters
typeThe type of exception to catch.

Example:

/* Returns the status of a pet by id */
pet_status get_pet_status(int id) {
pet_status status = ERROR;
TRY {
status = pet_find(id)->status;
} CATCH (PET_ERROR) {
status = UNKNOWN;
} CATCH (NOT_ENOUGH_MEMORY) {
abort();
}
return status;
}
See also
TRY
CATCH_ALL

◆ CATCH_ALL

#define CATCH_ALL
Value:
\
else if (e4c_catch(NULL, EXCEPTIONS4C_DEBUG))

Introduces a block of code that handles any exception thrown by a preceding TRY block, regardless of its type.

The CATCH_ALL block works like a general CATCH block that does not require specifying the type of exception to handle. It MAY be used as a fallback for catching all exceptions, including those not explicitly declared in other CATCH blocks.

Only one CATCH_ALL block is allowed per TRY block, and it MUST appear after all type-specific CATCH blocks if any are present.

Remarks
Using a CATCH_ALL block is useful for logging, debugging, or handling unexpected exceptions that don't fit into specific categories. However, specific CATCH blocks SHOULD be used whenever possible to maintain clarity and precise control over exception handling.
Attention
These blocks MUST NOT be exited through any of: goto, break, continue, or return.

Example:

/* Returns the status of a pet by id */
pet_status get_pet_status(int id) {
pet_status status = ERROR;
TRY {
status = pet_find(id)->status;
status = UNKNOWN;
}
return status;
}
See also
TRY
CATCH

◆ FINALLY

#define FINALLY
Value:
\
else if (e4c_finally(EXCEPTIONS4C_DEBUG))

Introduces a block of code that is executed after a TRY block, regardless of whether an exception was thrown or not.

Use this macro to run a block, no matter whether an exception happens or not.

A FINALLY block MUST be preceded by a single TRY, USING, or WITH block, after all the accompanying CATCH and CATCH_ALL blocks (if any). Only one FINALLY block is allowed per block.

Attention
These blocks MUST NOT be exited through any of: goto, break, continue, or return.
Remarks
This macro SHOULD be used to release resources, close files, or perform cleanup tasks.

Example:

/* Returns the status of a pet by id */
pet_status get_pet_status(int id) {
pet_status status = ERROR;
Pet pet = NULL;
TRY {
pet = pet_find(id);
status = pet->status;
} CATCH (PET_NOT_FOUND) {
status = UNKNOWN;
} FINALLY {
pet_free(pet);
}
return status;
}
See also
TRY

◆ THROW

#define THROW ( type,
format,
... )
Value:
\
EXCEPTIONS4C_LONG_JUMP(e4c_throw( \
&type, #type, EXCEPTIONS4C_DEBUG, (format) __VA_OPT__(,) __VA_ARGS__))

Throws an exception, interrupting the normal flow of execution.

THROW is used within a TRY block, a CATCH block, or any other function to signal that an error has occurred. The thrown exception will be of the specified type, and it MAY be handled by a preceding CATCH block.

If a thrown exception is not handled by any of the CATCH blocks in the current function, it propagates up the call stack to the function that called the current function. This continues until the exception is either handled by a CATCH block higher in the stack, or it reaches the top level of the program. If no CATCH block handles the exception, the program terminates and an error message is printed to the console.

Remarks
The error message will be formatted just as it would with printf. If no message is specified, then the default message for the supplied type will be used.
Important
Control never returns to the THROW point.

Example:

/* Returns a pet by id */
Pet pet_find(int id) {
Pet pet = pet_clone(id);
if (!pet) {
THROW(PET_NOT_FOUND, "Pet %d not found", id);
}
return pet;
}
Parameters
typeThe type of the exception to throw.
formatThe error message.
...An optional list of arguments that will be formatted according to format.
See also
CATCH
CATCH_ALL

◆ RETRY

#define RETRY ( max,
type,
format,
... )
Value:
\
EXCEPTIONS4C_LONG_JUMP(e4c_restart(false, (max), \
&type, #type, EXCEPTIONS4C_DEBUG, (format) __VA_OPT__(,) __VA_ARGS__))

Repeats the previous TRY or USE block entirely.

This macro restarts the previous TRY or USE block up to a specified maximum number of attempts, or throws an exception if it has already been restarted too many times.

Remarks
This macro SHOULD be used in the body of a CATCH or CATCH_ALL block as a quick way to fix an error condition and try something again. It MAY also be used in the body of a FINALLY block provided that there is an uncaught exception about to propagate up the call stack.
Important
Control never returns to the RETRY point.

Example:

/* Returns the status of a pet by id */
pet_status get_pet_status(int id) {
pet_status status = ERROR;
TRY {
status = pet_find(id)->status;
} CATCH (TIMED_OUT) {
RETRY(3, PET_NOT_FOUND, "Timed out too many times");
}
return status;
}
Parameters
maxThe maximum number of attempts.
typeThe type of exception to throw if max is exceeded.
formatThe error message.
...An optional list of arguments that will be formatted according to format.
See also
REACQUIRE
TRY
USE
THROW
e4c_is_uncaught

◆ USING

#define USING ( acquisition,
test,
disposal )
Value:
\
WITH (disposal) { \
(void) (acquisition); \
} USE (test)
#define USE(test)
Closes a block of code with automatic resource disposal.
Definition exceptions4c.h:366

Introduces a block of code with automatic resource acquisition and disposal.

Use this macro to easily acquire, use, and dispose of a resource.

A USING block is similar to a for statement, because it receives three comma-separated expressions that will be evaluated in order: acquisition, test, and disposal.

Both these expressions and the code block that uses the resource MAY throw exceptions.

A single USING block MAY be followed by one or more CATCH, an optional CATCH_ALL block, and an optional FINALLY (in this order), just like TRY blocks.

Attention
These blocks MUST NOT be exited through any of: goto, break, continue, or return.
Important
Local variables in the function containing the USING block MUST be volatile because these blocks invoke setjump and their values would be indeterminate if they had been changed since the invocation.

Example:

/* Returns the status of a pet by id */
pet_status get_pet_status(int id) {
pet_status status = ERROR;
Pet pet = NULL;
USING (pet = pet_find(id), pet != NULL, pet_free(pet)) {
status = pet->status;
} CATCH (PET_NOT_FOUND) {
status = UNKNOWN;
}
return status;
}
Parameters
acquisitionThe expression to acquire the resource.
testThe condition for executing the code block.
disposalThe expression to dispose of the resource.
See also
WITH
USE

◆ WITH

#define WITH ( disposal)
Value:
\
EXCEPTIONS4C_START_BLOCK(true) \
if (e4c_dispose(EXCEPTIONS4C_DEBUG)) { \
(void) (disposal); \
} else if (e4c_acquire(EXCEPTIONS4C_DEBUG)) {

Opens a block of code with automatic resource disposal.

You MAY use this macro instead of USING when the steps to acquire a resource are more complex than simply evaluating an expression.

A WITH block MUST be followed by one USE block.

Any of these blocks MAY throw exceptions.

Note
The WITH... USE combination works exactly the same as the USING block, except that the WITH block is responsible for the resource acquisition, and the USE block manipulates the resource.
Attention
These blocks MUST NOT be exited through any of: goto, break, continue, or return.
Important
Local variables in the function containing the WITH block MUST be volatile because these blocks invoke setjump and their values would be indeterminate if they had been changed since the invocation.

Example:

/* Returns the status of a pet by id */
pet_status get_pet_status(int id) {
pet_status status = ERROR;
Pet pet = NULL;
WITH (pet_free(pet)) {
if (pet_store_is_closed()) {
THROW(PET_STORE_CLOSED, NULL);
}
pet = pet_find(id);
} USE (pet != NULL) {
status = pet->status;
} CATCH (PET_NOT_FOUND) {
status = UNKNOWN;
}
return status;
}
Parameters
disposalThe expression to dispose of the resource.
See also
USE
USING

◆ USE

#define USE ( test)
Value:
\
} else if (e4c_try(EXCEPTIONS4C_DEBUG) && (test))

Closes a block of code with automatic resource disposal.

You MUST use this macro to close a preceding WITH block.

A USE block MUST be preceded by one WITH block and MAY be followed by one or more CATCH, an optional CATCH_ALL block, and an optional FINALLY (in this order), just like TRY blocks.

Any of these blocks MAY throw exceptions.

Note
The WITH... USE combination works exactly the same as the USING block, except that the WITH block is responsible for resource acquisition, and the USE block is responsible for resource manipulation.
Attention
These blocks MUST NOT be exited through any of: goto, break, continue, or return.

Example:

/* Returns the status of a pet by id */
pet_status get_pet_status(int id) {
pet_status status = ERROR;
Pet pet = NULL;
WITH (pet_free(pet)) {
if (pet_store_is_closed()) {
THROW(PET_STORE_CLOSED, NULL);
}
pet = pet_find(id);
} USE (pet != NULL) {
status = pet->status;
} CATCH (PET_NOT_FOUND) {
status = UNKNOWN;
}
return status;
}
Parameters
testThe condition for executing the code block.
See also
WITH

◆ REACQUIRE

#define REACQUIRE ( max,
type,
format,
... )
Value:
\
EXCEPTIONS4C_LONG_JUMP(e4c_restart(true, (max), \
&type, #type, EXCEPTIONS4C_DEBUG, (format) __VA_OPT__(,) __VA_ARGS__))

Repeats the previous WITH or USING block entirely.

This macro restarts the previous WITH or USING block, up to a specified maximum number of attempts, or throws an exception if it has already been restarted too many times.

Remarks
This macro SHOULD be used in the body of a CATCH or CATCH_ALL block as a quick way to fix an error condition and reacquire a resource again. It MAY also be used in the body of a FINALLY block provided that there is an uncaught exception about to propagate up the call stack.
Once the resource has been acquired, the USE block MAY also be repeated using the macro RETRY.
Important
Control never returns to the REACQUIRE point.

Example:

/* Returns the status of a pet by id */
pet_status get_pet_status(int id) {
pet_status status = ERROR;
Pet pet = NULL;
USING (pet = pet_find(id), pet != NULL, pet_free(pet)) {
status = pet->status;
} CATCH (TIMED_OUT) {
REACQUIRE(3, PET_NOT_FOUND, "Timed out too many times");
}
return status;
}
Parameters
maxThe maximum number of attempts.
typeThe type of exception to throw if max is exceeded.
formatThe error message.
...An optional list of arguments that will be formatted according to format.
See also
RETRY
WITH
USE
THROW
e4c_is_uncaught

Function Documentation

◆ e4c_set_context_supplier()

void e4c_set_context_supplier ( struct e4c_context *(* supplier )(void))

Sets the exception context supplier.

The library relies on this context to handle the current status of exceptions. If no supplier is provided, a default one will be used.

Remarks
Since the default exception context does not support concurrency, this mechanism can be useful to provide a concurrent version. For example, a context supplier could return different instances, depending on which thread is active. In that case, the supplier MUST be responsible for the creation and deletion of those instances.

Example:

static struct e4c_context my_custom_context = {
.initialize_exception = my_initializer,
.finalize_exception = my_finalizer
};
static struct e4c_context * my_context_supplier(void) {
return &my_custom_context;
}
int main(void) {
e4c_set_context_supplier(my_context_supplier);
TRY {
THROW(PET_ERROR, "Bad dog");
const struct my_custom_data * data = e4c_get_exception()->data;
printf("MSG: %s\n", data->msg);
}
return EXIT_SUCCESS;
}
Parameters
supplierThe function that supplies the current exception context.
See also
e4c_context

◆ e4c_get_context()

struct e4c_context * e4c_get_context ( void )

Supplies the current exception context.

Remarks
The objected returned by this function MAY be used to configure the program's current exception context. For example, you can set a custom handler that will be executed when the program abruptly terminates due to an uncaught exception.

Example:

static void my_uncaught_handler(const struct e4c_exception * exception) {
fprintf(stderr, "UNCAUGHT: %s\n", exception->message);
}
int main(int argc, char * argv[]) {
e4c_get_context()->uncaught_handler = my_uncaught_handler;
THROW(MY_ERROR, "Oops");
}
Returns
The current exception context.
See also
e4c_context
e4c_set_context_supplier

◆ e4c_get_exception()

const struct e4c_exception * e4c_get_exception ( void )

Retrieves the last exception that was thrown.

Remarks
This function SHOULD be used in the body of a CATCH or CATCH_ALL block to inspect the exception being handled. It MAY also be used in the body of a FINALLY block to determine if an exception was thrown in the corresponding TRY block, or during the execution of a CATCH or CATCH_ALL block.

Example:

/* Returns the status of a pet by id */
pet_status get_pet_status(int id) {
pet_status status = ERROR;
TRY {
status = pet_find(id)->status;
if (e4c_get_exception()->type == &NOT_ENOUGH_MEMORY) {
abort();
}
status = UNKNOWN;
}
return status;
}
Returns
The last exception that was thrown.
See also
e4c_exception
THROW
CATCH
FINALLY
e4c_is_uncaught

◆ e4c_is_uncaught()

bool e4c_is_uncaught ( void )

Determines whether the thrown exception was not caught.

An exception is considered "uncaught" if no matching CATCH or CATCH_ALL block has been executed for it. In other words, this function returns true if the exception has bypassed all specific exception-handling logic and is propagating further. And it returns false if no exception was thrown in the TRY block, or if an exception was successfully caught.

Remarks
This function SHOULD be used exclusively in the body of a FINALLY block to check whether an exception thrown during the TRY block has propagated past all CATCH and CATCH_ALL blocks without being handled.

Example:

int main(int argc, char * argv[]) {
TRY {
process_data(argc, argv);
} FINALLY {
if (e4c_is_uncaught()) {
fprintf(stderr, "Fatal error while processing data.\n");
} else {
fprintf(stdout, "Data processed successfully.\n");
}
}
return EXIT_SUCCESS;
}
Returns
true if the current exception (if any) has not yet been handled by any CATCH or CATCH_ALL block; false otherwise.
See also
FINALLY
e4c_get_exception