Raise errors from the status registry instead of returning codes
akerr_reserve_status_range() and akerr_register_status_name() returned private int enumerations, which was the one place in the library where a failure was not an akerr_ErrorContext *. They now return one like everything else: NULL on success, and on refusal an error whose status is a real code in the library's reserved band, so it can be CATCH-ed, HANDLE-d, PASS-ed, or left to propagate into a stack trace. Both are marked AKERR_NOIGNORE, so discarding the result warns at compile time. AKERR_STATUS_RANGE_OK and AKERR_STATUS_NAME_OK are gone; the remaining seven codes move into the AKERR_* offset span and get registered names. AKERR_LAST_LIBRARY_STATUS replaces AKERR_BADEXC as the top of that span in the reserved-band static assert and the exhaustiveness sweep. The refusal detail that used to go straight to akerr_log_method now travels in the error message, so a caller that handles the error decides whether it is reported. The two-argument akerr_name_for_status() set path is the exception: it returns a name and cannot raise, so it logs and releases. akerr_init() likewise has no caller to raise into, so failing to reserve its own band or name its own codes is logged and fatal -- that can only happen on a misconfigured build, and continuing would degrade every later stack trace to "Unknown Error". Move the 1.0.0 upgrade notice out of README.md into UPGRADING.md and rewrite its return-code tables in terms of the statuses now raised. Tests: ctest 29/29, coverage 97.5% line / 64.5% branch, mutation 77.5% (was 77.6%; the new survivors are the fatal init path, which needs a library built with an undersized name table -- TODO item 7). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,25 @@
|
||||
#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) */
|
||||
|
||||
/*
|
||||
* Registry failures. These are ordinary status codes, not a private return
|
||||
* enumeration: akerr_reserve_status_range() and akerr_register_status_name()
|
||||
* return akerr_ErrorContext * like everything else in this library, so a refused
|
||||
* reservation can be CATCH-ed, HANDLE-d, PASS-ed, or left unhandled to produce a
|
||||
* stack trace and stop the program. Success returns NULL.
|
||||
*/
|
||||
#define AKERR_STATUS_RANGE_OVERLAP (AKERR_LAST_ERRNO_VALUE + 18) /** Some part of the range is already owned by someone else */
|
||||
#define AKERR_STATUS_RANGE_FULL (AKERR_LAST_ERRNO_VALUE + 19) /** No reservation slots remain (see AKERR_MAX_RESERVED_STATUS_RANGES) */
|
||||
#define AKERR_STATUS_RANGE_INVALID (AKERR_LAST_ERRNO_VALUE + 20) /** Bad count, bad owner string, or the range overflows int */
|
||||
#define AKERR_STATUS_NAME_UNRESERVED (AKERR_LAST_ERRNO_VALUE + 21) /** No owner has reserved a range containing this status */
|
||||
#define AKERR_STATUS_NAME_FOREIGN (AKERR_LAST_ERRNO_VALUE + 22) /** The status lies in a range reserved by a different owner */
|
||||
#define AKERR_STATUS_NAME_FULL (AKERR_LAST_ERRNO_VALUE + 23) /** The name registry is full (raise AKERR_STATUS_NAME_SLOTS) */
|
||||
#define AKERR_STATUS_NAME_INVALID (AKERR_LAST_ERRNO_VALUE + 24) /** NULL/empty/over-long owner, or a NULL name */
|
||||
|
||||
/* The last status the library defines for itself. Everything from
|
||||
* AKERR_LAST_ERRNO_VALUE + 1 through here must have a registered name. */
|
||||
#define AKERR_LAST_LIBRARY_STATUS AKERR_STATUS_NAME_INVALID
|
||||
|
||||
/*
|
||||
* 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
|
||||
@@ -49,19 +68,6 @@
|
||||
#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
|
||||
@@ -69,7 +75,7 @@
|
||||
* 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];
|
||||
typedef char akerr_assert_codes_within_reserved_band[(AKERR_LAST_LIBRARY_STATUS < 256) ? 1 : -1];
|
||||
|
||||
#define AKERR_MAX_ARRAY_ERROR 128
|
||||
|
||||
@@ -105,32 +111,43 @@ akerr_ErrorContext AKERR_NOIGNORE *akerr_next_error();
|
||||
* 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.
|
||||
* that the range belongs to you and raises an error saying why a registration
|
||||
* was refused. This entry point cannot return an error context, so a refusal
|
||||
* here is reported through akerr_log_method and reads back as "Unknown Error".
|
||||
* Never returns NULL -- an unregistered status reads back as "Unknown Error".
|
||||
*/
|
||||
char *akerr_name_for_status(int status, char *name);
|
||||
|
||||
/*
|
||||
* 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`.
|
||||
* Register a display name for a status you own. `owner` must match the owner
|
||||
* string passed to akerr_reserve_status_range() for the range containing
|
||||
* `status`. Returns NULL on success, or an error context whose status is one of
|
||||
* the AKERR_STATUS_NAME_* codes above -- CATCH it, HANDLE it, or let it
|
||||
* propagate.
|
||||
*/
|
||||
int akerr_register_status_name(const char *owner, int status, const char *name);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *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.
|
||||
* an identical reservation for the same owner is a no-op; any other collision is
|
||||
* refused. Returns NULL on success, or an error context whose status is one of
|
||||
* the AKERR_STATUS_RANGE_* codes above. Treat any error as an initialization
|
||||
* failure: either handle it or let it propagate out of your init function.
|
||||
*/
|
||||
int akerr_reserve_status_range(int first_status, int count, const char *owner);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akerr_reserve_status_range(int first_status, int count, const char *owner);
|
||||
void akerr_init();
|
||||
void akerr_default_handler_unhandled_error(akerr_ErrorContext *ptr);
|
||||
void akerr_default_logger(const char *f, ...);
|
||||
int akerr_valid_error_address(akerr_ErrorContext *ptr);
|
||||
/* defined in src/errno.c which is built dynamically at build time from system errno definitions */
|
||||
void akerr_init_errno(void);
|
||||
/*
|
||||
* Internal. Names a status in the library's own reserved band on behalf of
|
||||
* akerr_init() and the generated errno table, which have no caller to raise
|
||||
* into: a failure here is logged and terminates the program. Not part of the
|
||||
* consumer API -- use akerr_register_status_name(), which raises instead.
|
||||
*/
|
||||
void __akerr_name_library_status(int status, const char *name);
|
||||
|
||||
#define LOG_ERROR_WITH_MESSAGE(__err_context, __err_message) \
|
||||
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); \
|
||||
|
||||
Reference in New Issue
Block a user