Skip to content

Commit

Permalink
Update some docs. Removed some macros in coroutine, replaced with docs.
Browse files Browse the repository at this point in the history
  • Loading branch information
tylov committed Oct 24, 2024
1 parent 3c3b5be commit cae60f3
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 43 deletions.
36 changes: 23 additions & 13 deletions docs/algorithm_api.md
Original file line number Diff line number Diff line change
@@ -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
<details>
Expand All @@ -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
Expand Down Expand Up @@ -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 |
|:-------------------------------------|:-------------------------------------|
Expand Down Expand Up @@ -221,38 +223,46 @@ int main(void)
<summary><b>c_func</b> - Function with on-the-fly defined return type</summary>
### 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}),
.v2=c_init(Vec, {7, 8, 9, 10, 11})
};
}
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;
}
```
</details>
<details>
<summary><b>c_init, c_push, c_drop</b> - Generic container operations</summary>

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
Expand All @@ -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;
}
Expand All @@ -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);
}
```
Expand Down
50 changes: 31 additions & 19 deletions include/stc/coroutine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
*/
Expand Down
24 changes: 13 additions & 11 deletions misc/examples/coroutines/generator.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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) {
Expand All @@ -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))
);
}

0 comments on commit cae60f3

Please sign in to comment.