Split the README reference material into docs/
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>
This commit is contained in:
50
docs/uncaught-errors.md
Normal file
50
docs/uncaught-errors.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# 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`.
|
||||
|
||||
```c
|
||||
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`.
|
||||
|
||||
```c
|
||||
#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 `FINISH` macro call that triggered the stacktrace.
|
||||
* Above that, the `CATCH()` inside of `main()` which caught the exception from `func1()` 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 the `func1` method which detected the presence of an unhandled error and returned it up the calling stack
|
||||
* Above that, the `CATCH()` macro in the `func1` method which caught the error coming out of `func2()`
|
||||
* Above that, a statement that the error was detected in the `CATCH()` statement at the same line
|
||||
* Above that, the `FINISH()` macro in `func2()` 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
|
||||
|
||||
Reference in New Issue
Block a user