From cae60f36aafac52908f40f763338617c9c746942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tyge=20L=C3=B8vset?= Date: Thu, 24 Oct 2024 19:25:16 +0200 Subject: [PATCH] Update some docs. Removed some macros in coroutine, replaced with docs. --- docs/algorithm_api.md | 36 ++++++++++++-------- include/stc/coroutine.h | 50 +++++++++++++++++----------- misc/examples/coroutines/generator.c | 24 +++++++------ 3 files changed, 67 insertions(+), 43 deletions(-) diff --git a/docs/algorithm_api.md b/docs/algorithm_api.md index 8109aafe..e2bf4239 100644 --- a/docs/algorithm_api.md +++ b/docs/algorithm_api.md @@ -1,6 +1,8 @@ # STC Algorithms -STC contains many generic algorithms and flow control abstactions. +STC contains many generic algorithms and loop abstactions. Raw loops are one of the most prominent +sources for errors in C and C++ code. By using the loop abstractions below, your code becomes more +descriptive and reduces chances of making mistakes. It is often easier to read and write too. ## Ranged for-loops
@@ -19,7 +21,7 @@ STC contains many generic algorithms and flow control abstactions. | `c_foreach_reverse (it, ctype, container)`| Iteratate elements in reverse: *vec, deque, queue, stack* | | `c_foreach_reverse (it, ctype, it1, it2)`| Iteratate range [it1, it2) elements in reverse. | | `c_foreach_n (it, ctype, container, n)`| Iteratate `n` first elements. Index variable is `{it}_index`. | -| `c_foreach_kv (key, val, ctype, container)` | Iterate with structured binding | +| `c_foreach_kv (key, val, ctype, container)` | Iterate maps with "structured binding" | ```c #define i_type IMap, int, int @@ -79,7 +81,7 @@ c_foritems (i, const char*, {"Hello", "crazy", "world"}) ### c_forrange, c_forrange32, c_forrange_t - `c_forrange`: abstraction for iterating sequence of integers. Like python's **for** *i* **in** *range()* loop. Uses `isize` (*ptrdiff_t*) as control variable. - `c_forrange32` is like *c_forrange*, but uses `int32` as control variable. -- `c_forrange_t` is like *c_forrange*, but takes an additional ***type*** as first argument for the control variable. +- `c_forrange_t` is like *c_forrange*, but takes an additional ***type*** for the control variable as first argument. | Usage | Python equivalent | |:-------------------------------------|:-------------------------------------| @@ -221,16 +223,21 @@ int main(void) c_func - Function with on-the-fly defined return type ### c_func -A convenient macro for defining functions with one or multiple return values, e.g. for errors. + +A macro for conveniently defining functions with multiple return values. This is for encouraging +to write functions that returns extra error context when error occurs, or just multiple return values. + ```c Vec get_data(void) { return c_init(Vec, {1, 2, 3, 4, 5, 6}); } -// same as get_data(): + +// same as get_data(), but with the c_func macro "syntax". c_func (get_data1,(void), ->, Vec) { return c_init(Vec, {1, 2, 3, 4, 5, 6}); } +// return two Vec types "on-the-fly". c_func (get_data2,(void), ->, struct {Vec v1, v2;}) { return (get_data2_result){ .v1=c_init(Vec, {1, 2, 3, 4, 5, 6}), @@ -238,21 +245,24 @@ c_func (get_data2,(void), ->, struct {Vec v1, v2;}) { }; } -c_func (load_data,(const char* fname), ->, struct {Vec out; int err;}) { +// return a Vec, and an err code which is 0 if OK. +c_func (load_data,(const char* fname), ->, struct {Vec vec; int err;}) { FILE* fp = fopen(fname, "rb"); if (fp == 0) return (load_data_result){.err=1}; - load_data_result vec = {Vec_with_size(1024, '\0')}; - fread(vec.out.data, sizeof(vec.out.data[0]), 1024, fp); + load_data_result out = {Vec_with_size(1024, '\0')}; + fread(out.vec.data, sizeof(out.vec.data[0]), 1024, fp); fclose(fp); - return vec; + return out; } ```
c_init, c_push, c_drop - Generic container operations +These work on any container. *c_init()* may also be used for **cspan** views. + ### c_init, c_push, c_drop - **c_init** - construct any container from an initializer list @@ -268,11 +278,11 @@ c_func (load_data,(const char* fname), ->, struct {Vec out; int err;}) { #define i_type Map, int, int #include "stc/hmap.h" -c_func (split_map,(Map map), ->, struct {Vec keys, vals;}) { +c_func (split_map,(Map map), ->, struct {Vec keys, values;}) { split_map_result out = {0}; c_foreach_kv (k, v, Map, map) { Vec_push(&out.keys, *k); - Vec_push(&out.vals, *v); + Vec_push(&out.values, *v); } return out; } @@ -294,11 +304,11 @@ int main(void) { split_map_result res = split_map(map); - c_foreach (i, Vec, res.vals) + c_foreach (i, Vec, res.values) printf("%d ", *i.ref); puts(""); - c_drop(Vec, &vec, &res.keys, &res.vals); + c_drop(Vec, &vec, &res.keys, &res.values); c_drop(Map, &map); } ``` diff --git a/include/stc/coroutine.h b/include/stc/coroutine.h index e05dc4aa..aaa9bc10 100644 --- a/include/stc/coroutine.h +++ b/include/stc/coroutine.h @@ -145,29 +145,41 @@ typedef struct { /* ============ ADVANCED, OPTIONAL ============= */ /* - * Iterators (for generators) - * Gen must be an existing typedef struct, i.e., these must be defined: - * Gen_iter Gen_begin(Gen* g); // return a coroutine object, advanced to the first yield - * int Gen_next(Gen_iter* it); // resume the coroutine + * Iterators for coroutine generators + * A type Gen must be an existing generator typedef struct. Then: * - * cco_default_begin(Gen); // implements basic Gen_begin() function - * .... + * typedef Gen Gen_value; + * typedef struct { + * Gen* ref; + * cco_state cco; + * ... + * } Gen_iter; + * + * // the generator coroutine, get the next value: + * int Gen_next(Gen_iter* it) { + * Gen* g = it->ref; + * cco_scope (it) { + * ... + * cco_yield; // suspend exec, gen with value ready + * ... + * cco_cleanup: + * it->ref = NULL; // stops the iteration + * } + * } + * + * // create coroutine/iter, advance to the first yield: + * Gen_iter Gen_begin(Gen* g) { + * Gen_iter it = {.ref = g}; + * ... + * Gen_next(&it); + * return it; + * } + * + * ... * c_foreach (i, Gen, gen) - printf("%d ", *i.ref); + * printf("%d ", *i.ref); */ -#define cco_iter_struct(Gen) \ - typedef Gen Gen##_value; \ - typedef struct Gen##_iter Gen##_iter; \ - struct Gen##_iter - -#define cco_default_begin(Gen) \ -Gen##_iter Gen##_begin(Gen* g) { \ - Gen##_iter it = {.ref = g}; \ - Gen##_next(&it); \ - return it; \ -} struct Gen##_iter - /* Using c_filter with generators: */ diff --git a/misc/examples/coroutines/generator.c b/misc/examples/coroutines/generator.c index eb109f24..f2b7cb93 100644 --- a/misc/examples/coroutines/generator.c +++ b/misc/examples/coroutines/generator.c @@ -4,21 +4,22 @@ #include "stc/coroutine.h" #include "stc/algorithm.h" +// Create an iterable generator Triple with max_triples items. +// Requires coroutine Triple_next() and function Triple_begin() to be defined. + typedef struct { int max_triples; int a, b, c; -} Triple; +} Triple, Triple_value; -// Create an iterable generator on an existing Triple type with count items. -// Requires coroutine Triple_next() and function Triple_begin() to be defined. -cco_iter_struct (Triple) { - Triple_value* ref; // required by iterator +typedef struct { + Triple* ref; // required by iterator + cco_state cco; // required by coroutine int count; - cco_state cco; // required by coroutine -}; +} Triple_iter; int Triple_next(Triple_iter* it) { - Triple* g = it->ref; // note: before cco_scope + Triple* g = it->ref; // get generator before cco_scope starts! cco_scope(it) { for (g->c = 5;; ++g->c) { @@ -40,14 +41,14 @@ int Triple_next(Triple_iter* it) { } Triple_iter Triple_begin(Triple* g) { - Triple_iter it = {.ref=g}; + Triple_iter it = {.ref = g}; Triple_next(&it); return it; } int main(void) { - Triple triple = {.max_triples=INT32_MAX}; + Triple triple = {.max_triples = INT32_MAX}; puts("Pythagorean triples.\nGet all triples with c < 40, using c_foreach:"); c_foreach (i, Triple, triple) { @@ -61,7 +62,8 @@ int main(void) c_filter(Triple, triple, true && (value->c < 40) && (cco_flt_take(10), // NB! use cco_flt_take(n) instead of c_flt_take(n) - // to ensure coroutine/iter cleanup if needed + // to ensure coroutine/iter cleanup. + // Also applies to cco_flt_takewhile(pred) printf("%d: (%d, %d, %d)\n", c_flt_getcount(), value->a, value->b, value->c)) ); }