The README was 794 lines: the summary, the design rationale, the whole macro reference, the threading contract, the build internals and the exit-status specification in one file. It is now 178 lines -- summary, installation, quickstart, and an index -- and the reference material lives in docs/, one file per topic: architecture, usage, status-codes, uncaught-errors, exit-status, thread-safety, building. The prose moved as written. Inbound references followed it: UPGRADING.md, TODO.md, include/akerror.tmpl.h and tests/err_threads_handoff.c now name the docs/ file that owns the text they cite, and AGENTS.md says where new documentation goes so the README does not grow back. Five factual errors fixed in the moved text: - Both NULL-pointer examples inverted their test. FAIL_ZERO_* fails when the expression is zero, so `(somePointer == NULL)` failed on a *valid* pointer. They now read `(somePointer != NULL)`. - AKERROR_NOIGNORE, four times including the #define, is AKERR_NOIGNORE. - FINISH_NORExbTURN is FINISH_NORETURN. - "functiions" is "functions". - The architecture link pointed at include/akerror.h, which is generated and not in the tree; it points at include/akerror.tmpl.h. The quickstart is new text. It compiles under -Wall -Wextra -Werror and was run through all three of its paths: handled usage error exits 0, unhandled IO error prints a trace and exits with the status, success exits 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
3.0 KiB
Uncaught errors
Misbehaving methods
Any function which returns akerr_ErrorContext * and completes successfully MUST call SUCCEED_RETURN(errctx). Failure to do this may result in an invalid akerr_ErrorContext * being returned, which will cause an AKERR_BEHAVIOR error to be triggered from your code.
Ensuring that all error codes are captured
Any function which returns akerr_ErrorContext * should also be marked with AKERR_NOIGNORE.
akerr_ErrorContext AKERR_NOIGNORE *f(...);
This will cause a compile-time error if the return value of such a function is not used. "Used" here means assigned to a variable - it does not necessarily mean that the value is checked. However assuming that such functions are called inside of ATTEMPT { ... } blocks, it is safe to assume that such returns will be caught with CATCH(...); therefore this error is a generally effective safeguard against careless coding where errors are not checked.
Beware that AKERR_NOIGNORE is not a failsafe - it implements the warn_unused_result mechanic. By design users may explicitly ignore an error code from a function marked with warn_unused_result by explicitly casting the return to void.
#define AKERR_NOIGNORE __attribute__((warn_unused_result))
Stack Traces
Whenever an error is captured using the FAIL_* or CATCH methods, and is unhandled such that it manages to propagate all the way to the top of the caller stack without being managed, the last FINISH macro to touch the error will trigger a stack trace and kill the program.
Consider the tests/err_trace.c program which intentionally triggers this behavior. It produces output like this:
tests/err_trace.c:func2:7: 1 (Null Pointer Error) : This is a failure in func2
tests/err_trace.c:func2:10
tests/err_trace.c:func1:18: Detected error 0 from array (refcount 1)
tests/err_trace.c:func1:18
tests/err_trace.c:func1:21
tests/err_trace.c:main:30: Detected error 0 from array (refcount 1)
tests/err_trace.c:main:30
tests/err_trace.c:main:33: Unhandled Error 1 (Null Pointer Error): This is a failure in func2
From bottom to top, we have:
- The last line printed is the
FINISHmacro call that triggered the stacktrace. - Above that, the
CATCH()inside ofmain()which caught the exception fromfunc1()but did not handle it - Above that, a statement that the error was detected in the
CATCH()statement at the same line - Above that, the
FINISH()macro in thefunc1method which detected the presence of an unhandled error and returned it up the calling stack - Above that, the
CATCH()macro in thefunc1method which caught the error coming out offunc2() - Above that, a statement that the error was detected in the
CATCH()statement at the same line - Above that, the
FINISH()macro infunc2()which detected an unhandled error and passed it out of the function - Above that, a reference to the line where the
FAIL()macro set the error code and provided the message which is printed here