Files
libakerror/README.md

292 lines
6.2 KiB
Markdown
Raw Permalink Normal View History

2025-12-29 22:29:28 -05:00
# libsdlerror
2025-07-20 21:44:17 -04:00
2025-12-29 22:29:28 -05:00
**Explicit, disciplined error handling for C — built on SDL3.**
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
`libsdlerror` is a small library that helps C programmers write correct, readable, and maintainable error-handling code without pretending that C is something it is not.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
It does not add exceptions to C.
It does not hide control flow.
It does not allocate memory at runtime.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
If you want magic, look elsewhere.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
---
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
## Why This Library Exists
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
Error handling in C is not hard — but it is tedious, repetitive, and easy to get wrong.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
The usual pattern:
```c
2025-12-29 22:29:28 -05:00
if (foo() < 0) {
fprintf(stderr, "%s\n", SDL_GetError());
cleanup();
return -1;
}
```
2025-12-29 22:29:28 -05:00
works, until it doesnt. As programs grow:
2025-12-29 22:29:28 -05:00
- Error checks get duplicated
- Cleanup paths multiply
- Context disappears
- Control flow becomes fragmented
2025-12-29 22:29:28 -05:00
SDL compounds this by storing errors in a single thread-local string, forcing programmers to either handle errors immediately or lose information.
2025-12-29 22:29:28 -05:00
`libsdlerror` exists to impose structure and discipline on this process — without compromising the explicitness that makes C reliable.
2025-12-29 22:29:28 -05:00
---
2025-12-29 22:29:28 -05:00
## What This Library Is — and Is Not
2025-12-29 22:29:28 -05:00
This library is deliberately conservative.
2025-12-29 22:29:28 -05:00
### 🚫 No `setjmp`, No `longjmp`
2025-12-29 22:29:28 -05:00
Many C error libraries attempt to simulate exceptions using `setjmp` / `longjmp`.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
That approach is rejected here.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
Non-local jumps:
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
- Bypass normal control flow
- Skip cleanup invisibly
- Break assumptions about stack lifetime
- Make reasoning about correctness harder, not easier
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
If control flow moves in `libsdlerror`, it does so because the source code says so.
2025-12-29 22:29:28 -05:00
Nothing jumps. Nothing unwinds itself. Nothing “just happens.”
2025-12-29 22:29:28 -05:00
---
2025-12-29 22:29:28 -05:00
### 🚫 No Runtime Memory Allocation — Ever
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
`libsdlerror` never performs dynamic memory allocation at runtime.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
It does not call:
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
- `malloc`
- `calloc`
- `realloc`
- `free`
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
There is no allocator dependency and no runtime allocation failure mode.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
---
2025-07-20 22:02:21 -04:00
2025-12-29 22:29:28 -05:00
### 📌 Explicit Storage Model
2025-07-20 21:44:17 -04:00
2025-12-29 22:29:28 -05:00
All memory used by `libsdlerror` falls into one of two categories:
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
- **Automatic storage (stack)**
Caller-owned error contexts and local state
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
- **Static storage duration**
Fixed-size internal tables allocated at program load time
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
There is no heap allocation and no runtime resizing.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
---
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
### ✅ C Means C
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
- No compiler extensions
- No undefined behavior
- No optimizer tricks
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
If it compiles as C, it works as C.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
---
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
### ✅ Designed for SDL, Not Against It
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
SDL already has an error system. It is simple, global, and fragile.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
`libsdlerror` does not replace it. It captures SDL error state at the right moment, preserves context, and makes it usable without forcing error checks everywhere.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
---
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
## The Six Guiding Principles
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
This library is opinionated, and intentionally so.
2025-07-20 21:44:17 -04:00
2025-12-29 22:29:28 -05:00
1. **Control flow must be explicit**
If execution moves, the source code must show why.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
2. **No `setjmp` / `longjmp`**
Errors must not bypass the stack.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
3. **No runtime memory allocation**
The library relies only on stack and static storage.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
4. **The caller owns all state**
No hidden globals exposed to the user.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
5. **Cleanup must be deterministic**
Cleanup always runs, exactly once.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
6. **Errors are values, not side effects**
They are captured, inspected, propagated, or handled deliberately.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
---
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
## Quick Start
### Build and Install
```sh
cmake -S . -B build
cmake --build build
cmake --install build
2025-07-21 07:19:41 -04:00
```
2025-12-29 22:29:28 -05:00
### Compile Your Program
```sh
gcc `pkg-config --cflags sdlerror` main.c \
`pkg-config --libs sdlerror` \
-o app
```
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
### Minimal Example
2025-07-21 07:19:41 -04:00
```c
2025-12-29 22:29:28 -05:00
#include <sdlerror.h>
int main(void) {
PREPARE_ERROR(err);
ATTEMPT {
CATCH(err, SDL_Init(SDL_INIT_VIDEO));
CATCH(err, do_something_that_can_fail());
}
CLEANUP {
SDL_Quit();
}
PROCESS(err) {
fprintf(stderr, "Error: %s\n", ERROR_MESSAGE(err));
return 1;
}
FINISH(err, true);
return 0;
2025-07-21 07:19:41 -04:00
}
```
2025-12-29 22:29:28 -05:00
---
## User-Configurable Error Codes
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
While most aspects of `libsdlerror` are intentionally fixed, error codes themselves are user-defined.
### Defining `ERR_*` Codes
2025-07-21 07:19:41 -04:00
```c
2025-12-29 22:29:28 -05:00
#define ERR_OK 0
#define ERR_SDL 1
#define ERR_IO 2
#define ERR_CONFIG 3
2025-07-21 07:19:41 -04:00
```
2025-12-29 22:29:28 -05:00
These are simple integer status values.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
---
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
### `MAX_ERR_VALUE`
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
`MAX_ERR_VALUE` must be greater than or equal to the highest `ERR_*` value you define.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
It determines the size of internal static tables used to map error codes to names.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
If you add new error codes, you must update `MAX_ERR_VALUE` and recompile.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
---
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
### Naming Errors: `error_name_for_status`
2025-07-21 07:19:41 -04:00
```c
2025-12-29 22:29:28 -05:00
error_name_for_status(ERR_IO, "I/O error");
error_name_for_status(ERR_CONFIG, "Configuration error");
2025-07-21 07:19:41 -04:00
```
2025-12-29 22:29:28 -05:00
Names are copied into fixed-size static buffers and persist for the lifetime of the program.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
No allocation occurs.
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
---
2025-07-21 07:19:41 -04:00
2025-12-29 22:29:28 -05:00
## Appendix: Memory Model
2025-07-20 21:44:17 -04:00
2025-12-29 22:29:28 -05:00
### Storage Duration Overview
2025-12-29 22:29:28 -05:00
`libsdlerror` uses only automatic (stack) storage and static storage duration.
2025-12-29 22:29:28 -05:00
No heap allocation occurs at runtime.
2025-12-29 22:29:28 -05:00
---
2025-12-29 22:29:28 -05:00
### Compile-Time Limits (`MAX_ERR*`)
2025-12-29 22:29:28 -05:00
The `MAX_ERR*` macros in `sdlerror.h` define fixed upper bounds for:
2025-12-29 22:29:28 -05:00
- Error message length
- Error name length
- Stacktrace buffer size
- Number of error contexts
2025-12-29 22:29:28 -05:00
These values are not user-tunable at runtime.
2025-12-29 22:29:28 -05:00
If you need different limits, you are expected to edit the header and recompile.
2025-12-29 22:29:28 -05:00
---
2025-12-29 22:29:28 -05:00
### Static Memory Usage (x86_64)
On a typical x86_64 system:
- Error name table: ~1 KB
- Static error pool: ~450 KB
- Miscellaneous globals: a few KB
**Total static footprint: ~470 KB**
All of this memory is allocated at program load time.
---
## FAQ
### Why Use Static Globals Instead of the Heap?
Because error handling infrastructure must not itself fail.
Heap allocation introduces failure modes, ordering problems, and unpredictability.
Static storage avoids all of this.
The globals used by `libsdlerror` are fixed-size, initialized at load time, and free of allocator dependencies.
---
### Why Not Allocate Per-Error Structures Dynamically?
Because error handling must work when memory is tight, during early startup, and during shutdown.
Dynamic allocation undermines all three.
---
## Summary
`libsdlerror` is deliberately conservative.
No jumps.
No heap allocation.
No hidden control flow.
2025-12-29 22:29:28 -05:00
Just explicit, disciplined error handling — the way C has always worked when used correctly.