Skip to content

Commit d40fb6a

Browse files
committed
add TRY/CATCH
1 parent 58069dc commit d40fb6a

3 files changed

Lines changed: 62 additions & 129 deletions

File tree

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) [2025] [Jakub Buczynski <KubaTaba1uga>]
3+
Copyright (c) [2026] [Jakub Buczynski <KubaTaba1uga>]
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 57 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,65 @@
1-
# C Development Kit: Error
1+
# C Development Kit: Error
22

3-
The C Development Kit (CDK) is a collection of lightweight, MIT-licensed libraries to make C development on Linux simpler and more consistent.
3+
Wouldn't it be awesome if we would have errors with messages, try, catche and even backtraces in C?
4+
Wouldn't it be even more awesome if such library wouldn't be bloated with savejumps mallocs and similiar monsrotities?
45

5-
C Development Kit: Error is an error component. It works much like `errno`, but adds richer context: not just an error code, but also a message and a lightweight backtrace showing where the error has been passed along. All of this comes without any heap allocations, making it safe and fast even for embedded or low-overhead systems.
6+
It fact it would, so i created this simple lib. It allow using errno alike mechanism but with additionall string messages, backtraces and try-catch mechanism. All in form of one header file. Feel free to just drop the lib into your project to add simple error stack to your lib.
67

7-
What is nice about `cdk_errno` is that on fast path it is as fast as normal `errno`, so if no error occured in the function there is no penalty hit. The difference show only on slow path so when error occurs:
8-
```
9-
❯ ./build/example/bench
10-
5-lvl errno-trace avg: 32.8 ns
11-
5-lvl fmt errno-trace avg: (disabled by CDK_ERROR_OPTIMIZE)
12-
5-lvl int avg: 5.7 ns
8+
Usage example:
9+
```c
10+
#include <cdk_error.h>
11+
12+
cdk_error_t bar(void) {
13+
cdk_errno = cdk_errnos(EINVAL, "Something went wrong in bar");
14+
return cdk_errno;
15+
}
16+
17+
void bar_cleanup(void) {}
18+
19+
cdk_error_t foo(void) {
20+
cdk_errno = bar();
21+
CDK_TRY(cdk_errno);
22+
23+
return 0;
24+
25+
error_out:
26+
return cdk_errno;
27+
}
28+
29+
int main(void) {
30+
char buf[2048];
31+
32+
cdk_errno = bar();
33+
CDK_TRY(cdk_errno);
34+
35+
cdk_errno = foo();
36+
CDK_TRY_CATCH(cdk_errno, error_bar_cleanup);
37+
38+
return 0;
39+
40+
error_bar_cleanup:
41+
bar_cleanup();
42+
error_out:
43+
cdk_error_dumps(cdk_errno, sizeof(buf), buf);
44+
return cdk_errno->code;
45+
}
1346
```
1447
15-
With `CDK_ERROR_OPTIMIZE` enabled `cdk_error` is like 6-8 times slower than normal errno. This performence hit is perfectly accaptable in most cases, because there will be plenty of other app critical processes wich will slow you more than this 32ns overhead.
48+
## Architecture overview
49+
50+
We use few simple ideas in the library.
51+
52+
First: to do not require lock we use _Thread_local storage, which allow use to drop locking because each thread has it's own error stack in the moment it is created.
1653
17-
---
54+
Second: there is `struct Error` which describe generic error and it can be used in three different modes. We do it in such a way because if you want the mosy speed on error you do not want to use additional string hence int error type, we also have str type and fstr type, int is the fastest formatted string is the slowest.
1855
19-
## 📦 Getting started
56+
Third: We gather backtrace manually, the gather is hidden behind CDK_TRY and CDK_TRY_CATCH so user do not need to remember about it.
2057
21-
The library is header-only. To use it, copy the single header file into **your own project or library**. Each project should keep its own copy to avoid sharing one global error state across unrelated code.
58+
Fourth: all code is in one header file which make it adjusted to be dropped in the app or in the lib. This way ecery lib or app has it's own error stack separeated from the rest of the system. Because of that we try to make `struct Error` as tiny as possible, on my amd64 pc it has 664 bytes. If you need to make it smaller compile with `-DCDK_ERROR_OPTIMIZE=1`, on my amd64 machine this bring single error object to 48 bytes.
2259
23-
Create a small wrapper header and add one `.c` file that defines the thread-local variables:
60+
## Getting started
2461
62+
The library is header only. To use it, copy the single header file into your own project or library. By default library has enabled errno mechanism so you will need two definitions for errno, `cdk_errno` and `cdk_hidden_errno`:
2563
```c
2664
// myerror.h
2765
#ifndef MYERROR_H
@@ -42,8 +80,7 @@ _Thread_local cdk_error_t cdk_errno = NULL;
4280
_Thread_local struct cdk_Error cdk_hidden_errno = {0};
4381
```
4482
45-
Every other file in your project just includes `myerror.h`.
46-
The `.c` file is required, because it provides the actual definitions of the thread-local globals; without it you’d only have declarations, and the linker would complain.
83+
Every file in your project includes `myerror.h`.
4784
4885
🔧 If you’d like to change the prefix (for example, from `cdk_` to `my_`), there’s a helper script:
4986
@@ -54,115 +91,12 @@ python3 tools/change_prefix.py \
5491
--old cdk --new my
5592
```
5693

57-
This produces a copy of the header with your own prefix, ready to drop into a project.
58-
59-
---
60-
61-
## ❓ Why copy instead of link?
94+
## Why copy instead of link?
6295

6396
Unlike traditional libraries, `cdk_error` is designed to be embedded into each project separately. The reason is simple: every library or program should have its **own private error state**.
6497

65-
If multiple components linked against the same global `cdk_errno`, their errors could overwrite each other, leading to confusing or incorrect traces. By copying the header into your project (and optionally renaming the prefix), you guarantee isolation: errors raised inside your library stay inside your library, and don’t clash with others.
66-
67-
---
68-
69-
## 💡 Usage
70-
71-
Here’s the simplest way to use the errno-style API:
72-
73-
```c
74-
#include <stdio.h>
75-
#include "myerror.h"
76-
77-
const char *nested_failing_func(void) {
78-
if (1) {
79-
cdk_errno = cdk_errnos(ENOBUFS, "My error");
80-
return NULL;
81-
}
82-
return "All good";
83-
}
84-
85-
int failing_func(void) {
86-
const char *s = nested_failing_func();
87-
if (!s) {
88-
return cdk_ereturn(-1);
89-
}
90-
return 13;
91-
}
92-
93-
void api_entry(void) {
94-
int res = failing_func();
95-
if (res < 0) {
96-
cdk_ewrap();
97-
return;
98-
}
99-
}
100-
101-
int main(void) {
102-
char buf[1024];
103-
cdk_errno = 0;
104-
105-
api_entry();
106-
if (cdk_errno) {
107-
cdk_edumps(sizeof(buf), buf);
108-
printf("%s", buf);
109-
return -1;
110-
}
111-
return 0;
112-
}
113-
```
114-
115-
The pattern is straightforward: when something fails, set `cdk_errno` with a code or message, return an error value, and wrap it if you need to add another frame. At the top level, dump the error to a buffer or file to see the full trace.
116-
117-
---
118-
119-
### ⚡ Performance
120-
121-
One of the nice things about `cdk_errno` is that on the **fast path** it behaves just like plain `errno`: if no error occurs in a function, there’s no extra overhead at all.
122-
The difference only shows up on the **slow path**, when an error is actually created and propagated:
123-
124-
```
125-
❯ ./build/example/bench
126-
5-lvl errno-trace avg: 32.8 ns
127-
5-lvl fmt errno-trace avg: (disabled by CDK_ERROR_OPTIMIZE)
128-
5-lvl int avg: 5.7 ns
129-
```
130-
131-
With `CDK_ERROR_OPTIMIZE` enabled (which removes formatted errors), the library is about **6–8× slower than a plain `errno` write** when an error occurs.
132-
That sounds big, but remember: it’s \~32 nanoseconds to build a full 5-level trace. In practice this overhead is negligible compared to real application work (I/O, syscalls, allocations, etc.).
133-
134-
In other words: you get structured errors and backtraces “for free” in the common case, and only pay a small price when something actually goes wrong.
135-
136-
---
137-
138-
139-
## 🛠️ Building examples and tests
14098

141-
This project uses [Meson](https://mesonbuild.com/) for its build system. To build with examples and tests enabled, run:
142-
143-
```sh
144-
meson setup build -Dtests=true -Dexamples=true
145-
meson compile -C build
146-
```
147-
148-
Examples will be placed in `build/example/` and test binaries in `build/test/`. You can run them directly:
149-
150-
```sh
151-
./build/example/example_1
152-
./build/example/example_2
153-
```
154-
155-
and execute the full test suite with:
156-
157-
```sh
158-
meson test -C build
159-
```
160-
161-
🧪 Tests are written using the Unity framework, pulled in automatically as a Meson subproject.
162-
163-
---
164-
165-
## ⚙️ Development workflow
99+
## Development workflow
166100

167101
For convenience, there’s an [Invoke](https://www.pyinvoke.org/) setup that automates common tasks:
168102

@@ -175,11 +109,6 @@ inv lint # run clang-tidy checks
175109
inv clean # remove build artifacts
176110
```
177111

178-
This makes it easy to keep the environment consistent and catch issues early.
179-
180-
---
181-
182-
## 📄 License
183-
184-
Released under the [MIT License](LICENSE) © 2025 Jakub Buczynski (KubaTaba1uga).
112+
## License
185113

114+
Released under the [MIT License](LICENSE) © 2026 Jakub Buczynski (KubaTaba1uga).

include/cdk_error.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ static inline int cdk_error_dumps(cdk_error_t err, size_t buf_size, char *buf) {
267267

268268
return 0;
269269
}
270+
270271
static inline void cdk_error_add_frame(cdk_error_t err,
271272
struct cdk_EFrame *frame) {
272273
if (err->eframes_len >= CDK_ERROR_BTRACE_MAX) {
@@ -303,6 +304,9 @@ static inline void cdk_error_add_frame(cdk_error_t err,
303304
cdk_error_fstr((err), (code), __FILE_NAME__, __func__, __LINE__, (fmt), \
304305
##__VA_ARGS__)
305306

307+
#define CDK_TRY_CATCH(err, label) if (err){ cdk_error_wrap(err); goto label; }
308+
#define CDK_TRY(err) CDK_TRY_CATCH(err, error_out)
309+
306310
/******************************************************************************
307311
* Errno API *
308312
******************************************************************************/

0 commit comments

Comments
 (0)