https://github.com/zelang-dev/c-raii
An robust high-level Defer, RAII implementation for C89, automatic memory safety, smartly!
https://github.com/zelang-dev/c-raii
c c89 defer memory-management raii
Last synced: 8 months ago
JSON representation
An robust high-level Defer, RAII implementation for C89, automatic memory safety, smartly!
- Host: GitHub
- URL: https://github.com/zelang-dev/c-raii
- Owner: zelang-dev
- License: mit
- Created: 2024-04-19T01:42:12.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2024-09-26T03:19:06.000Z (over 1 year ago)
- Last Synced: 2024-09-30T01:21:44.203Z (over 1 year ago)
- Topics: c, c89, defer, memory-management, raii
- Language: C
- Homepage: https://zelang-dev.github.io/c-raii/
- Size: 5.5 MB
- Stars: 4
- Watchers: 2
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-c - c-raii - An general RAII implementation for C, with Defer, simple high-level C11/C++11 like Threading/Futures, custom malloc/heap allocation. [MIT](https://spdx.org/licenses/MIT.html) (Memory Management / Advanced books)
- awesome-c - c-raii - An general RAII implementation for C, with Defer, simple high-level C11/C++11 like Threading/Futures, custom malloc/heap allocation. [MIT](https://spdx.org/licenses/MIT.html) (Memory Management / Advanced books)
README
# c-raii
[](https://github.com/zelang-dev/c-raii/actions/workflows/ci.yml)
[](https://github.com/zelang-dev/c-raii/actions/workflows/ci_centos.yml)
[](https://github.com/zelang-dev/c-raii/actions/workflows/ci_cpu.yml)
[](https://github.com/zelang-dev/c-raii/actions/workflows/ci_cpu-ppc64le.yml)
An robust high-level **Defer**, _RAII_ implementation for `C89`, automatic memory safety _smartly_, with **ultra** simple `threading` capabilities.
* [Synopsis](#synopsis)
* [There is _1 way_ to create an smart memory pointer.](#there-is-1-way-to-create-an-smart-memory-pointer)
* [The following _malloc/calloc_ wrapper functions are used to get an raw memory pointer.](#the-following-malloccalloc-wrapper-functions-are-used-to-get-an-raw-memory-pointer)
* [Thereafter, an smart pointer can be use with these _raii__* functions.](#thereafter-an-smart-pointer-can-be-use-with-these-raii_-functions)
* [Using `thread local storage` for an default smart pointer, the following functions always available.](#using-thread-local-storage-for-an-default-smart-pointer-the-following-functions-always-available)
* [Fully automatic memory safety, using `guard/unguarded/guarded` macro.](#fully-automatic-memory-safety-using-guardunguardedguarded-macro)
* [Installation](#installation)
* [Contributing](#contributing)
* [License](#license)
This branch, version `2.x` of **c-raii**, has an expanded feature set. It differs greatly from [1.x](https://github.com/zelang-dev/c-raii/blob/1.x/) to be in line with the fact that most ordinary `C` _libraries_ in-use will need _refactoring_ aka **rewrite**, to be use effectually with _memory safety_ as first class.
The features now encompass most things you might find in higher level languages, specificity **ease of use**, _with the same old_ **C89**. Not new concepts, you'll find most implicit in every _program/application_, if not explicit, now available in additional header files:
* dynamic data-structures [vector.h](https://github.com/zelang-dev/c-raii/blob/main/include/vector.h), [hashtable.h](https://github.com/zelang-dev/c-raii/blob/main/include/hashtable.h) and [map.h](https://github.com/zelang-dev/c-raii/blob/main/include/map.h).
* parsers [url_http.h](https://github.com/zelang-dev/c-raii/blob/main/include/url_http.h) and [json.h](https://github.com/zelang-dev/c-raii/blob/main/include/json.h).
* execution context and control flow constructs, _threads, coroutines, and channels_, [future.h](https://github.com/zelang-dev/c-raii/blob/main/include/future.h), [coro.h](https://github.com/zelang-dev/c-raii/blob/main/include/coro.h) and [channel.h](https://github.com/zelang-dev/c-raii/blob/main/include/channel.h).
* string manipulation/handling [swar.h](https://github.com/zelang-dev/c-raii/blob/main/include/swar.h).
* access/manipulate an individual integer bits, with string representation [bitset.h](https://github.com/zelang-dev/c-raii/blob/main/include/bitset.h).
* runtime code [reflection.h](https://github.com/zelang-dev/c-raii/blob/main/include/reflection.h), behavior similar to [C++ Reflection - Back on Track](https://youtu.be/nBUgjFPkoto) **video**.
> See [tests](https://github.com/zelang-dev/c-raii/blob/main/tests) and [examples](https://github.com/zelang-dev/c-raii/blob/main/examples) **folder** for _usage_.
This library has come full circle from it's initial aim at being _decoupled_ from ~c-coroutine~ [c-asio](https://zelang-dev.github.io/c-asio) to _bridging_ it back with different startup strategy behavior. Must either **`#define USE_CORO`** or call **`coro_start(main_func, argc, argv, 0)`**. When active **C** become whatever _high level_ language you can imagine that offers _automatic memory management_, otherwise no coroutine support, program will **crash** calling any specific coroutine only functions.
> The one _essential_ thing that all languages must _control_ and _redefine_ doing the compiler creation process, _function_ **"creation/signature/entrance/exit"** _process_. The whole **"signature/entrance/exit"** process hereforth is under **C-RAII** control.
The threading model, _execution context_ provided here is nothing new, the concept has been around for **C** aka _C89_ for quite awhile. The behavior is the same, but called under different _terminology_:
* [Effect handlers](https://en.wikipedia.org/wiki/Effect_system) model with various implementations listed in [effect-handlers-bench](https://github.com/effect-handlers/effect-handlers-bench).
* Various high level languages have direct compiler support for [Async/Await](https://en.wikipedia.org/wiki/Async/await),
but don't have [work stealing](https://en.wikipedia.org/wiki/Work_stealing) in that _paradigm_.
The model here mimics [Go concurrency](https://en.wikipedia.org/wiki/Go_(programming_language)#Concurrency) aka [Green thread](https://en.wikipedia.org/wiki/Green_thread) in _execution_ with follows [Cilk](https://en.wikipedia.org/wiki/Cilk) behavior. There are also [Weave](https://github.com/mratsim/weave) and [Lace](https://github.com/trolando/lace) both compete with **Cilk** behavior. **Weave** using [Nim programming language](https://nim-lang.org/), in which it's origins is `C`, and still compiles down to. **Lace** is `C11` base. In fact **Go** initial origins started as `C`, it's just a _matter of time_ before a [turing-complete](https://en.wikipedia.org/wiki/Turing_completeness) _understanding_ take effect, build _self_ with _self_. **It's amazing how things always come full circle**.
**_Everything that follows at this point is from version `1.x`, noteing ~c-coroutine~ [c-asio repo](https://zelang-dev.github.io/c-asio) will be restructured/refactored for the sole purpose of integrating [libuv](https://github.com/libuv/libuv), the coroutine part to be removed._**
---
This library has been decoupled from ~c-coroutine~ [c-asio](https://zelang-dev.github.io/c-asio) to be independently developed.
In the effort to find uniform naming of terms, various other packages was discovered [Except](https://github.com/meaning-matters/Except), and [exceptions-and-raii-in-c](https://github.com/psevon/exceptions-and-raii-in-c). Choose to settle in on [A defer mechanism for C](https://gustedt.wordpress.com/2020/12/14/a-defer-mechanism-for-c/), an upcoming C standard compiler feature. It's exactly this library's working design and concepts addressed in [c-asio](https://github.com/zelang-dev/c-asio).
This library uses an custom version of [rpmalloc](https://github.com/mjansson/rpmalloc) for **malloc/heap** allocation, not **C11** compiler `thread local` dependant, nor **C++** focus, _removed_. The customization is merged with required [c-threads](https://github.com/zelang-dev/c-threads) for **C11** _thread like emulation_.
* The behavior here is as in other _languages_ **Go's** [defer](https://go.dev/ref/spec#Defer_statements), **Zig's** [defer](https://ziglang.org/documentation/master/#defer), **Swift's** [defer](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/statements/#Defer-Statement), even **Rust** has [multi defer crates](https://crates.io/keywords/defer) there are other **borrow checker** issues - [A defer discussion](https://internals.rust-lang.org/t/a-defer-discussion/20387).
> As a side benefit, just including a single `#include "raii.h"` will make your **Linux** only application **Windows** compatible, see `work-steal.c` in [examples](https://github.com/zelang-dev/c-raii/tree/main/examples) folder, it's from [Complementary Concurrency Programs for course "Linux Kernel Internals"](https://github.com/sysprog21/concurrent-programs), _2 minor changes_, using macro `make_atomic`.
> All aspect of this library as been incorporated into an **c++11** like _threading model_ as outlined in [std::async](https://cplusplus.com/reference/future/async/), any thread launched with signature `thrd_async(fn, num_of_args, ...)` has function executed within `guard` block as further described below, all allocations is automatically cleaned up and `defer` statements **run**.
The C++ version from
RAII C89
C++11
```c
#include "raii.h"
// a non-optimized way of checking for prime numbers:
void *is_prime(args_t arg) {
int i, x = arg[0].integer;
for (i = 2; i < x; ++i) if (x % i == 0) return $(false);
return $(true);
}
int main(int argc, char **argv) {
int prime = 194232491;
// call function asynchronously:
future fut = thrd_async(is_prime, 1, prime);
printf("checking...\n");
thrd_wait(fut, thrd_yield);
printf("\n194232491 ");
if (thrd_get(fut).boolean) // guaranteed to be ready (and not block) after wait returns
printf("is prime.\n");
else
printf("is not prime.\n");
return 0;
}
```
```c++
// future::wait
#include // std::cout
#include // std::async, std::future
#include // std::chrono::milliseconds
// a non-optimized way of checking for prime numbers:
bool is_prime (int x) {
for (int i=2; i fut = std::async (is_prime,194232491);
std::cout << "checking...\n";
fut.wait();
std::cout << "\n194232491 ";
if (fut.get()) // guaranteed to be ready (and not block) after wait returns
std::cout << "is prime.\n";
else
std::cout << "is not prime.\n";
return 0;
}
```
The planned implementation from [defer reference implementation for C](https://gustedt.gitlabpages.inria.fr/defer/):
RAII C89
Planned C11
```c
// includes "cthreads.h" emulated C11 threads
#include "raii.h"
int main(int argc, char **argv)
guard {
// Panic if p == NULL
// Automatically _defer(free, p)
void *p = _malloc(25);
void *q = _calloc(1, 25);
if (mtx_lock(&mut)==thrd_error) break;
_defer(mtx_unlock, &mut);
// all resources acquired
} unguarded(0);
```
```c
guard {
void * const p = malloc(25);
if (!p) break;
defer free(p);
void * const q = malloc(25);
if (!q) break;
defer free(q);
if (mtx_lock(&mut)==thrd_error) break;
defer mtx_unlock(&mut);
// all resources acquired
}
```
There C Standard implementation states: **The important interfaces of this tool are:**
* `guard` prefixes a guarded block
* `defer` prefixes a defer clause
* `break` ends a guarded block and executes all its defer clauses
* `return` unwinds all guarded blocks of the current function and returns to the caller
* `exit` unwinds all defer clauses of all active function calls of the thread and exits normally
* `panic` starts global unwinding of all guarded blocks
* `recover` inside a defer clause stops a panic and provides an error code
There example from [source](https://gitlab.inria.fr/gustedt/defer/-/blob/master/defer4.c?ref_type=heads) - **gitlab**, outlined in [C Standard WG14 meeting](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2542.pdf) - **pdf**
RAII C89
Planned C11
```c
#include "raii.h"
char number[20];
void g_print(void *ptr) {
int i = raii_value(ptr)->integer;
printf("Defer in g = %d.\n", i);
}
void g(int i) {
if (i > 3) {
puts("Panicking!");
snprintf(number, 20, "%ld", i);
_panic(number);
}
guard {
_defer(g_print, &i);
printf("Printing in g = %d.\n", i);
g(i + 1);
} guarded;
}
void f_print(void *na) {
puts("In defer in f");
fflush(stdout);
if (_recovered(_get_message())) {
printf("Recovered in f = %s\n", _get_message());
fflush(stdout);
}
}
void f()
guard {
_defer(f_print, NULL);
puts("Calling g.");
g(0);
puts("Returned normally from g.");
} guarded;
int main(int argc, char **argv) {
f();
puts("Returned normally from f.");
return EXIT_SUCCESS;
}
```
```c
#include
#include
#include
#include "stddefer.h"
void g(int i) {
if (i > 3) {
puts("Panicking!");
panic(i);
}
guard {
defer {
printf("Defer in g = %d.\n", i);
}
printf("Printing in g = %d.\n", i);
g(i+1);
}
}
void f() {
guard {
defer {
puts("In defer in f");
fflush(stdout);
int err = recover();
if (err != 0) {
printf("Recovered in f = %d\n", err);
fflush(stdout);
}
}
puts("Calling g.");
g(0);
puts("Returned normally from g.");
}
}
int main(int argc, char* argv[static argc+1]) {
f();
puts("Returned normally from f.");
return EXIT_SUCCESS;
}
```
_Function_ `f` containing a **defer** statement which contains a call to the **recover**
function. _Function_ `f` invokes _function_ `g` which recursively descends before _panicking_ when the
value of `i > 3`. Execution of `f` produces the following **output**:
Calling g.
Printing in g = 0.
Printing in g = 1.
Printing in g = 2.
Printing in g = 3.
Panicking!
Defer in g = 3.
Defer in g = 2.
Defer in g = 1.
Defer in g = 0.
In defer in f
Recovered in f = 4
Returned normally from f.
## Synopsis
**C++** has a concept called [unique_ptr)](https://en.cppreference.com/w/cpp/memory/unique_ptr) where **"a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope. "**
Here too the same process is in effect through an **new** _typedef_ `unique_t` aka `memory_t` _structure_.
```c
/* Calls fn (with args as arguments) in separate thread, returning without waiting
for the execution of fn to complete. The value returned by fn can be accessed
by calling `thrd_get()`. */
C_API future thrd_async(thrd_func_t fn, size_t num_of_args, ...);
/* Same as `thrd_async`, allows passing custom `context` scope for internal `promise`
for auto cleanup within caller's `scope`. */
C_API future thrd_async_ex(memory_t *scope, thrd_func_t fn, void_t args);
/* Returns the value of `future` ~promise~, a thread's shared object, If not ready, this
function blocks the calling thread and waits until it is ready. */
C_API values_type thrd_get(future);
/* This function blocks the calling thread and waits until `future` is ready,
will execute provided `yield` callback function continuously. */
C_API void thrd_wait(future, wait_func yield);
/* Check status of `future` object state, if `true` indicates thread execution has ended,
any call thereafter to `thrd_get` is guaranteed non-blocking. */
C_API bool thrd_is_done(future);
C_API void thrd_delete(future);
C_API uintptr_t thrd_self(void);
C_API size_t thrd_cpu_count(void);
/* Return/create an arbitrary `vector/array` set of `values`, only available within `thread/future` */
C_API vectors_t thrd_data(size_t, ...);
/* Return/create an single `vector/array` ~value~, only available within `thread/future` */
#define $(val) thrd_data(1, (val))
/* Return/create an pair `vector/array` ~values~, only available within `thread/future` */
#define $$(val1, val2) thrd_data(2, (val1), (val2))
C_API future_t thrd_scope(void);
C_API future_t thrd_sync(future_t);
C_API rid_t thrd_spawn(thrd_func_t fn, size_t num_of_args, ...);
C_API values_type thrd_result(rid_t id);
// C_API future_t thrd_for(for_func_t loop, intptr_t initial, intptr_t times);
C_API void thrd_then(result_func_t callback, future_t iter, void_t result);
C_API void thrd_destroy(future_t);
C_API bool thrd_is_finish(future_t);
/**
* Creates an scoped `vector/array/container` for arbitrary arguments passing
* into an single `parameter` function.
* - Use standard `array access` for retrieval of an `union` storage type.
*
* - MUST CALL `args_destructor_set()` to have memory auto released
* within ~callers~ scoped `context`, will happen either at return/exist or panics.
*
* - OTHERWISE `memory leak` will be shown in DEBUG build.
*
* NOTE: `vector_for` does auto memory cleanup.
*
* @param count numbers of parameters, `0` will create empty `vector/array`.
* @param arguments indexed in given order.
*/
C_API args_t args_for(size_t, ...);
C_API args_t args_ex(size_t, va_list);
C_API void args_destructor_set(args_t);
C_API bool is_args(void_t);
/**
* Creates an scoped `vector/array/container` for arbitrary item types.
* - Use standard `array access` for retrieval of an `union` storage type.
*
* - MUST CALL `array_deferred_set` to have memory auto released
* when given `scope` context return/exist or panics.
*
* - OTHERWISE `memory leak` will be shown in DEBUG build.
*
* @param count numbers of parameters, `0` will create empty `vector/array`.
* @param arguments indexed in given order.
*/
C_API arrays_t array_of(memory_t *, size_t, ...);
C_API void array_deferred_set(arrays_t, memory_t *);
C_API void array_append(arrays_t, void_t);
C_API void array_delete(arrays_t);
C_API void array_remove(arrays_t, int);
C_API bool is_array(void_t);
#define $append(arr, value) array_append((arrays_t)arr, (void_t)value)
#define $remove(arr, index) array_remove((arrays_t)arr, index)
#define vectorize(vec) vectors_t vec = vector_variant()
#define vector(vec, count, ...) vectors_t vec = vector_for(nil, count, __VA_ARGS__)
#define $push_back(vec, value) vector_push_back((vectors_t)vec, (void_t)value)
#define $insert(vec, index, value) vector_insert((vectors_t)vec, index, (void_t)value)
#define $clear(vec) vector_clear((vectors_t)vec)
#define $size(vec) vector_size((vectors_t)vec)
#define $capacity(vec) vector_capacity((vectors_t)vec)
#define $erase(vec, index) vector_erase((vectors_t)vec, index)
/* The `foreach(item in vector/array)` macro, similar to `C#`,
executes a statement or a block of statements for each element in
an instance of `vectors_t/args_t/arrays_t` */
#define foreach(...) foreach_xp(foreach_in, (__VA_ARGS__))
#define foreach_back(...) foreach_xp(foreach_in_back, (__VA_ARGS__))
```
### There is _1 way_ to create an smart memory pointer
```c
/* Creates smart memory pointer, this object binds any additional requests to it's lifetime.
for use with `malloc_*` `calloc_*` wrapper functions to request/return raw memory. */
C_API unique_t *unique_init(void);
```
> This system use macros `RAII_MALLOC`, `RAII_FREE`, `RAII_REALLOC`, and `RAII_CALLOC`.
> If not **defined**, the default **malloc/calloc/realloc/free** are used when expanded.
> The expansion thereto points to custom replacement allocation routines **rpmalloc**.
### The following _malloc/calloc_ wrapper functions are used to get an raw memory pointer
```c
/* Request/return raw memory of given `size`, using smart memory pointer's lifetime scope handle.
DO NOT `free`, will be freed with given `func`, when scope smart pointer panics/returns/exits. */
C_API void *malloc_full(memory_t *scope, size_t size, func_t func);
/* Request/return raw memory of given `size`, using smart memory pointer's lifetime scope handle.
DO NOT `free`, will be freed with given `func`, when scope smart pointer panics/returns/exits. */
C_API void *calloc_full(memory_t *scope, int count, size_t size, func_t func);
```
Note the above functions will **panic/throw** if request fails, is `NULL`,
and begin **unwinding**, executing _deferred_ statements.
### Thereafter, an smart pointer can be use with these _raii__* functions
```c
/* Defer execution `LIFO` of given function with argument,
to the given `scoped smart pointer` lifetime/destruction. */
C_API size_t raii_deferred(memory_t *, func_t, void *);
/* Same as `raii_deferred` but allows recover from an Error condition throw/panic,
you must call `raii_is_caught` inside function to mark Error condition handled. */
C_API void raii_recover_by(memory_t *, func_t, void *);
/* Compare `err` to scoped error condition, will mark exception handled, if `true`. */
C_API bool raii_is_caught(memory_t *scope, const char *err);
/* Get scoped error condition string. */
C_API const char *raii_message_by(memory_t *scope);
/* Begin `unwinding`, executing given scope smart pointer `raii_deferred` statements. */
C_API void raii_deferred_free(memory_t *);
/* Same as `raii_deferred_free`, but also destroy smart pointer. */
C_API void raii_delete(memory_t *ptr);
```
### Using `thread local storage` for an default smart pointer, the following functions always available
```c
/* Return current `context` ~scope~ smart pointer, in use! */
C_API memory_t *get_scope(void);
/* Returns protected raw memory pointer of given `size`,
DO NOT FREE, will `throw/panic` if memory request fails.
This uses current `context` smart pointer, being in `guard` blocks,
inside `thread/future`, or active `coroutine` call. */
C_API void *malloc_local(size_t size);
/* Returns protected raw memory pointer of given `size`,
DO NOT FREE, will `throw/panic` if memory request fails.
This uses current `context` smart pointer, being in `guard` blocks,
inside `thread/future`, or active `coroutine` call. */
C_API void *calloc_local(int count, size_t size);
/* Defer execution `LIFO` of given function with argument,
to current `thread` scope lifetime/destruction. */
C_API size_t raii_defer(func_t, void *);
/* Same as `raii_defer` but allows recover from an Error condition throw/panic,
you must call `raii_caught` inside function to mark Error condition handled. */
C_API void raii_recover(func_t, void *);
/* Compare `err` to current error condition, will mark exception handled, if `true`. */
C_API bool raii_caught(const char *err);
/* Get current error condition string. */
C_API const char *raii_message(void);
/* Begin `unwinding`, executing current `thread` scope `raii_defer` statements. */
C_API void raii_deferred_clean(void);
```
### Fully automatic memory safety, using `guard/unguarded/guarded` macro
The full potently of **RAII** is encapsulated in the `guard` macro.
Using `try/catch/catch_any/catch_if/finally/finality` exception system macros separately will be unnecessary, however see [examples](https://github.com/zelang-dev/c-raii/tree/main/examples) folder for usage.
Below is the **recommended pattern** for complete cross platform usage.
System uses _native_ [Windows SEH](https://learn.microsoft.com/en-us/cpp/cpp/try-except-statement), _multiple_ `catch()` blocks not possible.
> NOTE: Only in **debug builds** _uncaught exceptions_ **backtrace** info is auto displayed.
```c++
#include "raii.h"
static void pfree(void *p) {
printf("freeing protected memory pointed by '%s'\n", (char *)p);
free(p);
}
int main(int argc, char **argv) {
try {
int a = 1;
int b = 0.00000;
char *p = 0;
p = malloc_full(raii_init(), 3, pfree);
strcpy(p, "p");
*(int *)0 = 0;
printf("never reached\n");
printf("%d\n", (a / b));
raise(SIGINT);
} catch_if {
if (caught(bad_alloc))
printf("catch: exception %s (%s:%d) caught\n", err.name, err.file, err.line);
else if (caught(division_by_zero))
printf("catch: exception %s (%s:%d) caught\n", err.name, err.file, err.line);
else if (caught(sig_fpe))
printf("catch: exception %s (%s:%d) caught\n", err.name, err.file, err.line);
else if (caught(sig_ill))
printf("catch: exception %s (%s:%d) caught\n", err.name, err.file, err.line);
else if (caught(sig_int))
printf("catch: exception %s (%s:%d) caught\n", err.name, err.file, err.line);
else if (caught(sig_segv))
printf("catch: exception %s (%s:%d) caught\n", err.name, err.file, err.line);
} finally {
if (err.is_caught) {
printf("finally: try failed, but succeeded to catch -> %s (%s:%d)\n", err.name, err.file, err.line);
} else {
printf("finally: try failed to `catch()`\n");
ex_backtrace(err.backtrace);
}
}
return 0;
}
```
The Planned C11 implementation details still holds here, but `defer` not confined to `guard` block, actual function call.
```c
/* Creates an scoped guard section, it replaces `{`.
Usage of: `_defer`, `_malloc`, `_calloc`, `_assign_ptr` macros
are only valid between these sections.
- Use `_return(x);` macro, or `break;` to exit early.
- Use `_assign_ptr(var)` macro, to make assignment of block scoped smart pointer. */
#define guard
/* This ends an scoped guard section, it replaces `}`.
On exit will begin executing deferred functions,
return given `result` when done, use `NONE` for no return. */
#define unguarded(result)
/* This ends an scoped guard section, it replaces `}`.
On exit will begin executing deferred functions. */
#define guarded
#define _malloc(size) malloc_local(size)
#define _calloc(count, size) calloc_local(count, size)
/* Defer execution `LIFO` of given function with argument,
Only valid between `guard` blocks or inside `thread/future` call.
Execution begins when current `guard` scope exits or panic/throw. */
#define _defer(func, ptr)
/* Compare `err` to scoped error condition, will mark exception handled, if `true`.
Only valid between `guard` blocks or inside `thread/future` call. */
#define _recovered(err)
/* Get scoped error condition string.
Only valid between `guard` blocks or inside `thread/future` call. */
#define _get_message()
/* Stops the ordinary flow of control and begins panicking,
throws an exception of given message. */
#define _panic(err)
/* Makes a reference assignment of current scoped smart pointer. */
#define _assign_ptr(scope)
/* Exit `guarded` section, begin executing deferred functions,
return given `value` when done, use `NONE` for no return. */
#define _return(value)
```
The idea way of using this library, is to make a **new** _field_ for `unique_t` into your current _typedef_ **object**,
mainly one held throughout application, and setup your wrapper functions to above **raii_** functions.
There are also `2 global callback` functions that need to be setup for complete integration.
```c
// Currently an wrapper function that set ctx->data, scoped error, and protection state, working on removing need
typedef void (*ex_setup_func)(ex_context_t *ctx, const char *ex, const char *panic);
// Your wrapper to raii_deferred_free(ctx->data)
typedef void (*ex_unwind_func)(void *);
ex_setup_func exception_setup_func;
ex_unwind_func exception_unwind_func;
```
## Installation
Any **commit** with an **tag** is considered _stable_ for **release** at that _version_ point.
If there are no _binary_ available for your platform under **Releases** then build using **cmake**,
which produces **static** libraries by default.
You will need the _binary_ stored under `built`, and `*.h` headers, or complete `include` _folder_ if **Windows**.
### Linux
```shell
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug/Release -D BUILD_TESTS=OFF -D BUILD_EXAMPLES=OFF # use to not build tests and examples
cmake --build .
```
### Windows
```shell
mkdir build
cd build
cmake .. -D BUILD_TESTS=OFF -D BUILD_EXAMPLES=OFF # use to not build tests and examples
cmake --build . --config Debug/Release
```
### As cmake project dependency
> For **CMake** versions earlier than `3.14`, see
Add to **CMakeLists.txt**
```c
find_package(raii QUIET)
if(NOT raii_FOUND)
FetchContent_Declare(raii
URL https://github.com/zelang-dev/c-raii/archive/refs/tags/2.3.1.zip
URL_MD5 c1867e16749033765d10d440ed99c919
)
FetchContent_MakeAvailable(raii)
endif()
target_include_directories(your_project PUBLIC $