2026-01-10 10:20:35 -05:00
# ifndef _AKERR_H_
# define _AKERR_H_
2025-07-20 21:40:14 -04:00
2026-01-10 22:03:14 -05:00
# if (defined(AKERR_USE_STDLIB) && AKERR_USE_STDLIB == 1) || (!defined(AKERR_USE_STDLIB))
2025-07-20 21:40:14 -04:00
# include <stdlib.h>
# include <stdbool.h>
# include <string.h>
# include <stdio.h>
2026-05-24 09:50:21 -04:00
# include <limits.h>
2026-01-04 22:56:31 -05:00
# endif
2025-07-20 21:40:14 -04:00
2026-05-24 09:50:21 -04:00
// FIXME: This is huge now. It used to be 1000 bytes, then I wanted to report errors
// related to filesystem paths, which made it grow beyond PATH_MAX, then I started
// reporting messages including 2 file paths (PATH_MAX * 2), so now to make the compiler warnings
// shut up, it's enormous (PATH_MAX*3).
# define AKERR_MAX_ERROR_CONTEXT_STRING_LENGTH 12384
2026-01-10 10:20:35 -05:00
# define AKERR_MAX_ERROR_NAME_LENGTH 64
2026-05-24 09:50:21 -04:00
# define AKERR_MAX_ERROR_FNAME_LENGTH PATH_MAX
2026-01-10 10:20:35 -05:00
# define AKERR_MAX_ERROR_FUNCTION_LENGTH 128
2026-05-24 09:50:21 -04:00
# define AKERR_MAX_ERROR_STACKTRACE_BUF_LENGTH (AKERR_MAX_ERROR_CONTEXT_STRING_LENGTH + AKERR_MAX_ERROR_NAME_LENGTH + AKERR_MAX_ERROR_FNAME_LENGTH + AKERR_MAX_ERROR_FUNCTION_LENGTH + 16)
2026-01-10 10:20:35 -05:00
2026-01-12 08:33:31 -05:00
# define AKERR_LAST_ERRNO_VALUE AKERR_LAST_ERRNO_VALUE_SED
2026-06-22 08:15:07 -04:00
# define AKERR_NULLPOINTER (AKERR_LAST_ERRNO_VALUE + 1) /** A pointer had a NULL value where such was not permissible */
# define AKERR_OUTOFBOUNDS (AKERR_LAST_ERRNO_VALUE + 2) /** Attempt to access a datastructure outside of bounds */
# define AKERR_API (AKERR_LAST_ERRNO_VALUE + 3) /** An otherwise unspecified API contract has been violated */
# define AKERR_ATTRIBUTE (AKERR_LAST_ERRNO_VALUE + 4) /** Relates to accessing of attributes on objects */
# define AKERR_TYPE (AKERR_LAST_ERRNO_VALUE + 5) /** An object had the incorrect type */
# define AKERR_KEY (AKERR_LAST_ERRNO_VALUE + 6) /** A key was either invalid for or not present in a map */
# define AKERR_INDEX (AKERR_LAST_ERRNO_VALUE + 8) /** An error occurred when attempting to index an indexable datastructure (other than out of bounds) */
# define AKERR_FORMAT (AKERR_LAST_ERRNO_VALUE + 9) /** An error occurred in the formatting of an object (usually a string) */
# define AKERR_IO (AKERR_LAST_ERRNO_VALUE + 10) /** An unspecified IO error occurred. */
# define AKERR_VALUE (AKERR_LAST_ERRNO_VALUE + 11) /** A provided value was invalid */
# define AKERR_RELATIONSHIP (AKERR_LAST_ERRNO_VALUE + 12) /** An error occurred in establishing, maintaining or severing a relationship between two objects */
# define AKERR_EOF (AKERR_LAST_ERRNO_VALUE + 13) /** The end of a stream or file has been encountered */
# define AKERR_CIRCULAR_REFERENCE (AKERR_LAST_ERRNO_VALUE + 14) /** Indicates that a circular reference has been found in a linked list */
# define AKERR_ITERATOR_BREAK (AKERR_LAST_ERRNO_VALUE + 15) /** Used to prematurely end an iteration cycle (such as when searching a graph and the desired node has been found) */
# define AKERR_NOT_IMPLEMENTED (AKERR_LAST_ERRNO_VALUE + 16) /** A method was called that is defined but not currently implemented */
# define AKERR_BADEXC (AKERR_LAST_ERRNO_VALUE + 17) /** The libakerr library was given an akerr_ErrorContext to parse that did not come from AKERR_ARRAY_ERROR (likely an uninitialized pointer) */
2026-01-10 10:20:35 -05:00
Enforce status-code ownership and harden the name registry
Reservations were advisory bookkeeping: any component could name any status,
so the registry only detected declared-range overlap between components that
both opted in. Naming a status now requires a reservation.
akerr_register_status_name() checks that the range belongs to the caller, and
the legacy two-argument akerr_name_for_status() set path, which cannot
identify its caller, requires that some reservation covers the status. Every
refusal is logged and names the real owner, because a name that fails to
register degrades that code to "Unknown Error" in every later stack trace.
Fix a reservation made before the first PREPARE_ERROR being silently
discarded. akerr_init() clears the tables, so whichever component first
triggered it wiped an earlier reservation and the next component to claim the
same range was told it was free, producing exactly the undetected aliasing
the registry exists to prevent. Every registry entry point now calls
akerr_init(), which sets its guard before doing any work so those calls do
not recurse.
Replace the linear-scan name array with an open-addressed hash table, taking
lookup from O(n) to O(1) and raising usable capacity from 512 entries (366
free to consumers after errno registration) to 3072 (~2900 free). Both table
sizes are build-time overridable and applied PRIVATE: they live entirely in
src/error.c, so raising them cannot desynchronize a library from its
consumers the way AKERR_MAX_ERR_VALUE could. Exhausting either table is now
logged and returned to the caller rather than silently dropping the entry.
No dynamic allocation is introduced; both tables remain file-scope arrays,
and the library's undefined-symbol set gains only strcmp and strlen.
Register names for AKERR_EOF, AKERR_ITERATOR_BREAK and AKERR_NOT_IMPLEMENTED,
which had none and rendered as "Unknown Error" in every stack trace carrying
them. err_error_names.c now sweeps the whole AKERR_* offset span so a code
added without a name fails there instead of in production traces.
Add static assertions that the slot count is a power of two and that
AKERR_BADEXC stays inside the library's own 0-255 band, the latter guarding
against a host errno space large enough to push library codes into the range
consumers are told to allocate from.
Set a project version and soname (1.0.0 / libakerror.so.1) so a stale
installed library can no longer be silently paired with newer headers, and so
akerror.pc ships a real Version field instead of an empty one.
Mutation testing surfaced an out-of-bounds probe in the new table that the
suite did not catch: masking with SLOTS rather than SLOTS-1 indexes past the
array, and err_maxval.c asserted only that some names registered before the
table filled, which a collapsed probe sequence still satisfies. It now
requires a substantial entry count and reads every entry back by its own
distinct name.
Tests: 28/28 pass. Coverage 99.4% line / 86.8% branch. Mutation score for
src/error.c 74% -> 77.3%.
Compatibility: source and ABI break. AKERR_MAX_ERR_VALUE and the
__AKERR_ERROR_NAMES data symbol are gone, custom codes must move out of
0-255, and names must be registered against a reserved range. README.md
carries the migration steps.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 18:19:25 -04:00
/*
* Status values 0 through 255 are reserved by libakerror at akerr_init ( ) time :
* the host ' s errno values plus the AKERR_ * codes above . Consumers allocate from
* 256 upwards . Reserving any part of this band fails with
* AKERR_STATUS_RANGE_OVERLAP naming AKERR_LIBRARY_OWNER .
*/
# define AKERR_LIBRARY_OWNER "libakerror"
# define AKERR_RESERVED_STATUS_COUNT 256
# define AKERR_FIRST_CONSUMER_STATUS AKERR_RESERVED_STATUS_COUNT
/* Results of akerr_reserve_status_range(). */
# define AKERR_STATUS_RANGE_OK 0 /** The range is now reserved by the caller */
# define AKERR_STATUS_RANGE_OVERLAP 1 /** Some part of the range is already owned by someone else */
# define AKERR_STATUS_RANGE_FULL 2 /** No reservation slots remain (see AKERR_MAX_RESERVED_STATUS_RANGES) */
# define AKERR_STATUS_RANGE_INVALID 3 /** Bad count, bad owner string, or the range overflows int */
/* Results of akerr_register_status_name(). */
# define AKERR_STATUS_NAME_OK 0 /** The name is registered */
# define AKERR_STATUS_NAME_UNRESERVED 1 /** No owner has reserved a range containing this status */
# define AKERR_STATUS_NAME_FOREIGN 2 /** The status lies in a range reserved by a different owner */
# define AKERR_STATUS_NAME_FULL 3 /** The name registry is full (raise AKERR_STATUS_NAME_SLOTS) */
# define AKERR_STATUS_NAME_INVALID 4 /** NULL/empty/over-long owner, or a NULL name */
/*
* The library reserves status values 0 through 255 for itself ( see akerr_init ) ,
* which must contain every AKERR_ * code above . AKERR_LAST_ERRNO_VALUE is
* derived from the host ' s errno list at build time , so on a platform with an
* unusually large errno space these codes could escape the band and collide
* with consumer codes allocated at 256. Fail the build instead .
*/
typedef char akerr_assert_codes_within_reserved_band [ ( AKERR_BADEXC < 256 ) ? 1 : - 1 ] ;
2025-07-20 21:40:14 -04:00
2026-01-10 10:20:35 -05:00
# define AKERR_MAX_ARRAY_ERROR 128
2025-07-20 21:40:14 -04:00
typedef struct
{
2026-01-10 10:20:35 -05:00
char message [ AKERR_MAX_ERROR_CONTEXT_STRING_LENGTH ] ;
2026-01-10 09:50:17 -05:00
int arrayid ;
2025-07-20 21:40:14 -04:00
int status ;
bool handled ;
int refcount ;
2026-01-10 10:20:35 -05:00
char fname [ AKERR_MAX_ERROR_FNAME_LENGTH ] ;
char function [ AKERR_MAX_ERROR_FNAME_LENGTH ] ;
2025-07-20 21:40:14 -04:00
int lineno ;
bool reported ;
2026-01-10 10:20:35 -05:00
char stacktracebuf [ AKERR_MAX_ERROR_STACKTRACE_BUF_LENGTH ] ;
2025-07-20 21:40:14 -04:00
char * stacktracebufptr ;
2026-01-10 10:20:35 -05:00
} akerr_ErrorContext ;
2025-07-20 21:40:14 -04:00
2026-01-10 10:20:35 -05:00
# define AKERR_NOIGNORE __attribute__((warn_unused_result))
2025-07-20 21:40:14 -04:00
2026-01-10 10:20:35 -05:00
typedef void ( * akerr_ErrorUnhandledErrorHandler ) ( akerr_ErrorContext * errctx ) ;
typedef void ( * akerr_ErrorLogFunction ) ( const char * f , . . . ) ;
2025-07-20 21:40:14 -04:00
2026-01-10 10:20:35 -05:00
extern akerr_ErrorContext AKERR_ARRAY_ERROR [ AKERR_MAX_ARRAY_ERROR ] ;
extern akerr_ErrorUnhandledErrorHandler akerr_handler_unhandled_error ;
extern akerr_ErrorLogFunction akerr_log_method ;
extern akerr_ErrorContext * __akerr_last_ignored ;
2025-07-20 21:40:14 -04:00
2026-01-10 10:20:35 -05:00
akerr_ErrorContext AKERR_NOIGNORE * akerr_release_error ( akerr_ErrorContext * ptr ) ;
akerr_ErrorContext AKERR_NOIGNORE * akerr_next_error ( ) ;
Enforce status-code ownership and harden the name registry
Reservations were advisory bookkeeping: any component could name any status,
so the registry only detected declared-range overlap between components that
both opted in. Naming a status now requires a reservation.
akerr_register_status_name() checks that the range belongs to the caller, and
the legacy two-argument akerr_name_for_status() set path, which cannot
identify its caller, requires that some reservation covers the status. Every
refusal is logged and names the real owner, because a name that fails to
register degrades that code to "Unknown Error" in every later stack trace.
Fix a reservation made before the first PREPARE_ERROR being silently
discarded. akerr_init() clears the tables, so whichever component first
triggered it wiped an earlier reservation and the next component to claim the
same range was told it was free, producing exactly the undetected aliasing
the registry exists to prevent. Every registry entry point now calls
akerr_init(), which sets its guard before doing any work so those calls do
not recurse.
Replace the linear-scan name array with an open-addressed hash table, taking
lookup from O(n) to O(1) and raising usable capacity from 512 entries (366
free to consumers after errno registration) to 3072 (~2900 free). Both table
sizes are build-time overridable and applied PRIVATE: they live entirely in
src/error.c, so raising them cannot desynchronize a library from its
consumers the way AKERR_MAX_ERR_VALUE could. Exhausting either table is now
logged and returned to the caller rather than silently dropping the entry.
No dynamic allocation is introduced; both tables remain file-scope arrays,
and the library's undefined-symbol set gains only strcmp and strlen.
Register names for AKERR_EOF, AKERR_ITERATOR_BREAK and AKERR_NOT_IMPLEMENTED,
which had none and rendered as "Unknown Error" in every stack trace carrying
them. err_error_names.c now sweeps the whole AKERR_* offset span so a code
added without a name fails there instead of in production traces.
Add static assertions that the slot count is a power of two and that
AKERR_BADEXC stays inside the library's own 0-255 band, the latter guarding
against a host errno space large enough to push library codes into the range
consumers are told to allocate from.
Set a project version and soname (1.0.0 / libakerror.so.1) so a stale
installed library can no longer be silently paired with newer headers, and so
akerror.pc ships a real Version field instead of an empty one.
Mutation testing surfaced an out-of-bounds probe in the new table that the
suite did not catch: masking with SLOTS rather than SLOTS-1 indexes past the
array, and err_maxval.c asserted only that some names registered before the
table filled, which a collapsed probe sequence still satisfies. It now
requires a substantial entry count and reads every entry back by its own
distinct name.
Tests: 28/28 pass. Coverage 99.4% line / 86.8% branch. Mutation score for
src/error.c 74% -> 77.3%.
Compatibility: source and ABI break. AKERR_MAX_ERR_VALUE and the
__AKERR_ERROR_NAMES data symbol are gone, custom codes must move out of
0-255, and names must be registered against a reserved range. README.md
carries the migration steps.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 18:19:25 -04:00
/*
* Look up ( name = = NULL ) or register ( name ! = NULL ) the display name for a
* status . Registration succeeds only if some owner has reserved a range
* containing ` status ` ; prefer akerr_register_status_name ( ) , which also checks
* that the range belongs to you and reports why a registration was refused .
* Never returns NULL - - an unregistered status reads back as " Unknown Error " .
*/
2026-01-10 10:20:35 -05:00
char * akerr_name_for_status ( int status , char * name ) ;
Enforce status-code ownership and harden the name registry
Reservations were advisory bookkeeping: any component could name any status,
so the registry only detected declared-range overlap between components that
both opted in. Naming a status now requires a reservation.
akerr_register_status_name() checks that the range belongs to the caller, and
the legacy two-argument akerr_name_for_status() set path, which cannot
identify its caller, requires that some reservation covers the status. Every
refusal is logged and names the real owner, because a name that fails to
register degrades that code to "Unknown Error" in every later stack trace.
Fix a reservation made before the first PREPARE_ERROR being silently
discarded. akerr_init() clears the tables, so whichever component first
triggered it wiped an earlier reservation and the next component to claim the
same range was told it was free, producing exactly the undetected aliasing
the registry exists to prevent. Every registry entry point now calls
akerr_init(), which sets its guard before doing any work so those calls do
not recurse.
Replace the linear-scan name array with an open-addressed hash table, taking
lookup from O(n) to O(1) and raising usable capacity from 512 entries (366
free to consumers after errno registration) to 3072 (~2900 free). Both table
sizes are build-time overridable and applied PRIVATE: they live entirely in
src/error.c, so raising them cannot desynchronize a library from its
consumers the way AKERR_MAX_ERR_VALUE could. Exhausting either table is now
logged and returned to the caller rather than silently dropping the entry.
No dynamic allocation is introduced; both tables remain file-scope arrays,
and the library's undefined-symbol set gains only strcmp and strlen.
Register names for AKERR_EOF, AKERR_ITERATOR_BREAK and AKERR_NOT_IMPLEMENTED,
which had none and rendered as "Unknown Error" in every stack trace carrying
them. err_error_names.c now sweeps the whole AKERR_* offset span so a code
added without a name fails there instead of in production traces.
Add static assertions that the slot count is a power of two and that
AKERR_BADEXC stays inside the library's own 0-255 band, the latter guarding
against a host errno space large enough to push library codes into the range
consumers are told to allocate from.
Set a project version and soname (1.0.0 / libakerror.so.1) so a stale
installed library can no longer be silently paired with newer headers, and so
akerror.pc ships a real Version field instead of an empty one.
Mutation testing surfaced an out-of-bounds probe in the new table that the
suite did not catch: masking with SLOTS rather than SLOTS-1 indexes past the
array, and err_maxval.c asserted only that some names registered before the
table filled, which a collapsed probe sequence still satisfies. It now
requires a substantial entry count and reads every entry back by its own
distinct name.
Tests: 28/28 pass. Coverage 99.4% line / 86.8% branch. Mutation score for
src/error.c 74% -> 77.3%.
Compatibility: source and ABI break. AKERR_MAX_ERR_VALUE and the
__AKERR_ERROR_NAMES data symbol are gone, custom codes must move out of
0-255, and names must be registered against a reserved range. README.md
carries the migration steps.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 18:19:25 -04:00
/*
* Register a display name for a status you own . Returns AKERR_STATUS_NAME_OK ,
* or one of the AKERR_STATUS_NAME_ * codes above ; every refusal is also logged
* through akerr_log_method . ` owner ` must match the owner string passed to
* akerr_reserve_status_range ( ) for the range containing ` status ` .
*/
int akerr_register_status_name ( const char * owner , int status , const char * name ) ;
/*
* Claim ` count ` status values starting at ` first_status ` for ` owner ` . Repeating
* an identical reservation for the same owner is a no - op ; any other collision
* is refused and logged . Returns one of the AKERR_STATUS_RANGE_ * codes ; treat
* anything other than AKERR_STATUS_RANGE_OK as an initialization failure .
*/
2026-07-30 13:53:39 -04:00
int akerr_reserve_status_range ( int first_status , int count , const char * owner ) ;
2026-01-10 10:20:35 -05:00
void akerr_init ( ) ;
void akerr_default_handler_unhandled_error ( akerr_ErrorContext * ptr ) ;
void akerr_default_logger ( const char * f , . . . ) ;
2026-05-21 21:44:52 -04:00
int akerr_valid_error_address ( akerr_ErrorContext * ptr ) ;
2026-01-12 08:33:31 -05:00
/* defined in src/errno.c which is built dynamically at build time from system errno definitions */
void akerr_init_errno ( void ) ;
2025-07-20 21:40:14 -04:00
# define LOG_ERROR_WITH_MESSAGE(__err_context, __err_message) \
2026-01-10 10:20:35 -05:00
akerr_log_method ( " %s%s:%s:%d: %s %d (%s): %s " , ( char * ) & __err_context - > stacktracebuf , ( char * ) __FILE__ , ( char * ) __func__ , __LINE__ , __err_message , __err_context - > status , akerr_name_for_status ( __err_context - > status , NULL ) , __err_context - > message ) ; \
2025-07-20 21:40:14 -04:00
# define LOG_ERROR(__err_context) \
LOG_ERROR_WITH_MESSAGE ( __err_context , " " ) ;
# define RELEASE_ERROR(__err_context) \
if ( __err_context ! = NULL ) { \
2026-01-10 10:20:35 -05:00
__err_context = akerr_release_error ( __err_context ) ; \
2025-07-20 21:40:14 -04:00
}
# define PREPARE_ERROR(__err_context) \
2026-01-10 10:20:35 -05:00
akerr_init ( ) ; \
akerr_ErrorContext __attribute__ ( ( unused ) ) * __err_context = NULL ;
2025-07-20 21:40:14 -04:00
# define ENSURE_ERROR_READY(__err_context) \
if ( __err_context = = NULL ) { \
2026-01-10 10:20:35 -05:00
__err_context = akerr_next_error ( ) ; \
2025-07-20 21:40:14 -04:00
if ( __err_context = = NULL ) { \
2026-01-10 10:20:35 -05:00
akerr_log_method ( " %s:%s:%d: Unable to pull an error context from the array! " , __FILE__ , ( char * ) __func__ , __LINE__ ) ; \
2025-07-20 21:40:14 -04:00
exit ( 1 ) ; \
} \
2026-07-28 09:22:19 -04:00
__err_context - > refcount + = 1 ; \
}
/*
* Append a formatted line to the error ' s stack - trace buffer , bounded by the
* space that remains so a deep propagation chain cannot write past the end of
* stacktracebuf . snprintf reports the length it * would * have written , which on
* truncation exceeds what it actually wrote , so the cursor advance is clamped
* to the remaining space .
*/
# define AKERR_STACKTRACE_APPEND(__err_context, ...) \
do { \
char * __akerr_stb = ( char * ) __err_context - > stacktracebuf ; \
size_t __akerr_used = ( size_t ) ( __err_context - > stacktracebufptr - __akerr_stb ) ; \
if ( __akerr_used < AKERR_MAX_ERROR_STACKTRACE_BUF_LENGTH ) { \
size_t __akerr_rem = AKERR_MAX_ERROR_STACKTRACE_BUF_LENGTH - __akerr_used ; \
int __akerr_n = snprintf ( __err_context - > stacktracebufptr , __akerr_rem , __VA_ARGS__ ) ; \
if ( __akerr_n < 0 ) { \
__akerr_n = 0 ; \
} \
__err_context - > stacktracebufptr + = ( ( size_t ) __akerr_n < __akerr_rem ) \
? ( size_t ) __akerr_n : ( __akerr_rem - 1 ) ; \
} \
} while ( 0 )
2025-07-20 21:40:14 -04:00
2026-07-28 09:22:19 -04:00
/*
2026-01-10 10:20:35 -05:00
* Failure and success methods for functions that return akerr_ErrorContext *
2025-07-20 21:40:14 -04:00
*/
# define FAIL_ZERO_RETURN(__err_context, __x, __err, __message, ...) \
if ( __x = = 0 ) { \
FAIL ( __err_context , __err , __message , # # __VA_ARGS__ ) ; \
return __err_context ; \
}
# define FAIL_NONZERO_RETURN(__err_context, __x, __err, __message, ...) \
if ( __x ! = 0 ) { \
FAIL ( __err_context , __err , __message , # # __VA_ARGS__ ) ; \
return __err_context ; \
}
# define FAIL_RETURN(__err_context, __err, __message, ...) \
FAIL ( __err_context , __err , __message , # # __VA_ARGS__ ) ; \
return __err_context ;
# define SUCCEED_RETURN(__err_context) \
RELEASE_ERROR ( __err_context ) ; \
return NULL ;
/*
* Failure and success methods for use inside of ATTEMPT ( ) blocks
*/
# define FAIL_ZERO_BREAK(__err_context, __x, __err, __message, ...) \
if ( __x = = 0 ) { \
FAIL ( __err_context , __err , __message , # # __VA_ARGS__ ) ; \
break ; \
}
# define FAIL_NONZERO_BREAK(__err_context, __x, __err, __message, ...) \
if ( __x ! = 0 ) { \
FAIL ( __err_context , __err , __message , # # __VA_ARGS__ ) ; \
break ; \
}
# define FAIL_BREAK(__err_context, __err_, __message, ...) \
FAIL ( __err_context , __err_ , __message , # # __VA_ARGS__ ) ; \
break ;
# define SUCCEED_BREAK(__err_context) \
SUCCEED ( __err_context ) ; \
break ;
/*
* General failure and success methods
*/
# define FAIL(__err_context, __err, __message, ...) \
ENSURE_ERROR_READY ( __err_context ) ; \
__err_context - > status = __err ; \
2026-07-28 10:52:29 -04:00
snprintf ( ( char * ) __err_context - > fname , AKERR_MAX_ERROR_FNAME_LENGTH , " %s " , __FILE__ ) ; \
snprintf ( ( char * ) __err_context - > function , AKERR_MAX_ERROR_FUNCTION_LENGTH , " %s " , __func__ ) ; \
2025-07-20 21:40:14 -04:00
__err_context - > lineno = __LINE__ ; \
2026-01-10 10:20:35 -05:00
snprintf ( ( char * ) __err_context - > message , AKERR_MAX_ERROR_CONTEXT_STRING_LENGTH , __message , # # __VA_ARGS__ ) ; \
2026-07-28 09:22:19 -04:00
AKERR_STACKTRACE_APPEND ( __err_context , " %s:%s:%d: %d (%s) : %s \n " , ( char * ) __err_context - > fname , ( char * ) __err_context - > function , __err_context - > lineno , __err_context - > status , akerr_name_for_status ( __err_context - > status , NULL ) , ( __err_context - > message = = NULL ? " " : __err_context - > message ) ) ;
2025-07-20 21:40:14 -04:00
# define SUCCEED(__err_context) \
ENSURE_ERROR_READY ( __err_context ) ; \
__err_context - > status = 0 ;
/*
* Defines for the ATTEMPT / CATCH / CLEANUP / PROCESS / HANDLE / FINISH process
*/
# define ATTEMPT \
switch ( 0 ) { \
case 0 : \
2026-05-15 19:41:22 -04:00
# define VALID(__err_context, __stmt) \
__stmt ; \
2026-05-24 19:14:35 -04:00
if ( akerr_valid_error_address ( __err_context ) = = 0 ) { \
2026-05-24 19:41:32 -04:00
__err_context = NULL ; \
2026-06-22 08:15:07 -04:00
FAIL ( __err_context , AKERR_BADEXC , " Received (akerr_ErrorContext *) from an invalid memory region. (Did the method finish without calling SUCCEED_RETURN?) " ) ; \
2026-05-15 19:41:22 -04:00
}
2025-07-20 21:40:14 -04:00
# define DETECT(__err_context, __stmt) \
2026-05-15 19:41:22 -04:00
VALID ( __err_context , __stmt ) ; \
2025-07-20 21:40:14 -04:00
if ( __err_context ! = NULL ) { \
if ( __err_context - > status ! = 0 ) { \
2026-07-28 09:22:19 -04:00
AKERR_STACKTRACE_APPEND ( __err_context , " %s:%s:%d \n " , ( char * ) __FILE__ , ( char * ) __func__ , __LINE__ ) ; \
2025-07-20 21:40:14 -04:00
break ; \
} \
}
# define CATCH(__err_context, __stmt) \
DETECT ( __err_context , __err_context = __stmt ) ;
2026-05-15 19:41:22 -04:00
# define PASS(__err_context, __stmt) \
switch ( 0 ) { \
case 0 : \
DETECT ( __err_context , __err_context = __stmt ) ; \
} \
FINISH_LOGIC ( __err_context , true ) ;
2025-07-20 21:40:14 -04:00
# define IGNORE(__stmt) \
2026-01-10 10:20:35 -05:00
__akerr_last_ignored = __stmt ; \
if ( __akerr_last_ignored ! = NULL ) { \
LOG_ERROR_WITH_MESSAGE ( __akerr_last_ignored , " ** IGNORED ERROR ** " ) ; \
2025-07-20 21:40:14 -04:00
}
# define CLEANUP \
} ;
# define PROCESS(__err_context) \
if ( __err_context ! = NULL ) { \
switch ( __err_context - > status ) { \
case 0 : \
__err_context - > handled = true ;
# define HANDLE(__err_context, __err_status) \
break ; \
case __err_status : \
__err_context - > stacktracebufptr = ( char * ) & __err_context - > stacktracebuf ; \
__err_context - > handled = true ;
# define HANDLE_GROUP(__err_context, __err_status) \
case __err_status : \
__err_context - > stacktracebufptr = ( char * ) & __err_context - > stacktracebuf ; \
__err_context - > handled = true ;
# define HANDLE_DEFAULT(__err_context) \
break ; \
default : \
__err_context - > stacktracebufptr = ( char * ) & __err_context - > stacktracebuf ; \
__err_context - > handled = true ;
2026-05-15 19:41:22 -04:00
# define FINISH_LOGIC(__err_context, __pass_up) \
2025-07-20 21:40:14 -04:00
if ( __err_context ! = NULL ) { \
if ( __err_context - > handled = = false & & __pass_up = = true ) { \
return __err_context ; \
} \
} \
2026-05-15 19:41:22 -04:00
# define FINISH(__err_context, __pass_up) \
} ; \
} ; \
FINISH_LOGIC ( __err_context , __pass_up ) \
2025-07-20 21:40:14 -04:00
RELEASE_ERROR ( __err_context ) ;
# define FINISH_NORETURN(__err_context) \
} ; \
} ; \
if ( __err_context ! = NULL ) { \
if ( __err_context - > handled = = false ) { \
LOG_ERROR_WITH_MESSAGE ( __err_context , " Unhandled Error " ) ; \
2026-01-10 10:20:35 -05:00
akerr_handler_unhandled_error ( __err_context ) ; \
2025-07-20 21:40:14 -04:00
} \
} \
RELEASE_ERROR ( __err_context ) ;
2026-01-10 10:20:35 -05:00
# endif // _AKERR_H_