From d92ffe3da29b3352c90b2d356e395914ba5b3f52 Mon Sep 17 00:00:00 2001 From: Tyge Lovset Date: Sat, 8 Apr 2023 11:02:39 +0200 Subject: More docs updates, and a change in stc/extend.h. --- README.md | 51 +++++++++++++++++++++++++++-------------------- docs/ccommon_api.md | 33 +++++++++++++++--------------- include/stc/algo/crange.h | 4 ++-- include/stc/ccommon.h | 3 ++- include/stc/extend.h | 4 ++-- misc/examples/forfilter.c | 2 +- misc/examples/functor.c | 10 +++++----- misc/examples/prime.c | 2 +- 8 files changed, 59 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 6f07f8ff..220349b3 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,10 @@ STC - Smart Template Containers for C --- Description ----------- -STC is a *modern*, *fully typesafe*, *fast* and *compact* container and algorithm library for C99. +STC is a *modern*, *typesafe*, *fast* and *compact* container and algorithms library for C99. The API naming is similar to C++ STL, but it takes inspiration from Rust and Python as well. -The library let you store and manage trivial and highly complex data in a variety of -containers with ease. It is optimized for speed, usage ergonomics, consistency, -and it creates small binaries. +The library handles everything from trivial to highly complex data using *templates*, and it +supports a variety of containers. Containers ---------- @@ -80,18 +79,18 @@ List of contents ## STC is unique! 1. ***Centralized analysis of template parameters***. The analyser assigns values to all -non-specified template parameters (based on the specified ones) using meta-programming, -so that you don't have to! You may specify a set of "standard" template parameters for each container, -but as a minimum *only one is required*: `i_val` (+ `i_key` for maps). In this case STC assumes -that the elements are of basic types. For more complex types, additional template parameters must be given. -2. ***Alternative insert/lookup type***. You may specify an alternative type to use for lookup in -containers. E.g., containers with STC string elements (**cstr**) uses `const char*` as lookup type, -so construction of a `cstr` (which may allocate memory) for the lookup *is not needed*. Hence, the alt. lookup -key does not need to be destroyed after use as it is normally a POD type. Finally, the alternative -key does not need to be destroyed after use as it is normally a POD type. Finally, the alternative -lookup type may be passed to an ***emplace***-function. E.g. instead of calling -`cvec_str_push(&vec, cstr_from("Hello"))`, you may call `cvec_str_emplace(&vec, "Hello")`, -which is functionally identical, but more convenient. +non-specified template parameters (based on the specified ones) using meta-programming, so +that you don't have to! You may specify a set of "standard" template parameters for each +container, but as a minimum *only one is required*: `i_val` (+ `i_key` for maps). In this +case, STC assumes that the elements are of basic types. For non-trivial types, additional +template parameters must be given. +2. ***Alternative insert/lookup type***. You may specify an alternative type to use for +lookup in containers. E.g., containers with STC string elements (**cstr**) uses `const char*` +as lookup type, so constructing a `cstr` (which may allocate memory) for the lookup +*is not needed*. Hence, the alternative lookup key does not need to be destroyed after use, +as it is normally a POD type. Finally, the key may be passed to an ***emplace***-function. +So instead of calling e.g. `cvec_str_push(&vec, cstr_from("Hello"))`, you may call +`cvec_str_emplace(&vec, "Hello")`, which is functionally identical, but more convenient. 3. ***Standardized container iterators***. All containers can be iterated in the same manner, and all use the same element access syntax. E.g.: - `c_foreach (it, MyInts, myints) *it.ref += 42;` works for any container defined as @@ -146,9 +145,9 @@ Benchmark notes: --- ## Usage STC containers have similar functionality to C++ STL standard containers. All containers except for a few, -like **cstr** and **cbits** are generic/templated. No type casting is done, so containers are type-safe like -templated types in C++. However, the specification of template parameters are naturally different. In STC, -you specify template parameters by `#define` before including the container: +like **cstr** and **cbits** are generic/templated. No type casting is used, so containers are type-safe like +templated types in C++. However, to specify template parameters with STC, you define them as macros prior to +including the container: ```c #define i_type Floats // Container type name; unless defined name would be cvec_float #define i_val float // Container element type @@ -576,10 +575,10 @@ To use it, define both `i_type` and `i_con` (the container type) before includin #define i_con csmap #include "stcpgs.h" -// Note the wrapper struct type is IMapExt. IMap is accessed by .get +// Note the wrapper struct type is IMap_ext. IMap is accessed by .get void maptest() { - IMapExt map = {.memctx=CurrentMemoryContext}; + IMap_ext map = {.memctx=CurrentMemoryContext}; c_forrange (i, 1, 16) IMap_insert(&map.get, i*i, i); @@ -605,9 +604,17 @@ STC is generally very memory efficient. Memory usage for the different container --- # Version History +## Version 4.2 +- Much improved documentation +- Added Coroutines + documentation +- Added `c_const_cast()` typesafe macro. +- Renamed c_foreach_r => `c_foreach_rv` +- Renamed c_flt_count(i) => `c_flt_counter(i)` +- Renamed c_flt_last(i) => `c_flt_getcount(i)` +- Removed c_PAIR ## Version 4.1.1 -I am happy to finally announce a new release! Major changes: +Major changes: - A new exciting [**cspan**](docs/cspan_api.md) single/multi-dimensional array view (with numpy-like slicing). - Signed sizes and indices for all containers. See C++ Core Guidelines by Stroustrup/Sutter: [ES.100](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es100-dont-mix-signed-and-unsigned-arithmetic), [ES.102](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es102-use-signed-types-for-arithmetic), [ES.106](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es106-dont-try-to-avoid-negative-values-by-using-unsigned), and [ES.107](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es107-dont-use-unsigned-for-subscripts-prefer-gslindex). - Customizable allocator [per templated container type](https://github.com/tylov/STC/discussions/44#discussioncomment-4891925). diff --git a/docs/ccommon_api.md b/docs/ccommon_api.md index 21eaf884..90191e40 100644 --- a/docs/ccommon_api.md +++ b/docs/ccommon_api.md @@ -84,7 +84,7 @@ c_forrange (i, 30, 0, -5) printf(" %lld", i); ### crange A number sequence generator type, similar to [boost::irange](https://www.boost.org/doc/libs/release/libs/range/doc/html/range/reference/ranges/irange.html). The **crange_value** type is `long long`. Below *start*, *stop*, and *step* are of type *crange_value*: ```c -crange& crange_object(...) // create a compound literal crange object +crange& crange_obj(...) // create a compound literal crange object crange crange_make(stop); // will generate 0, 1, ..., stop-1 crange crange_make(start, stop); // will generate start, start+1, ... stop-1 crange crange_make(start, stop, step); // will generate start, start+step, ... upto-not-including stop @@ -102,7 +102,7 @@ c_forfilter (i, crange, r1, isPrime(*i.ref)) // 2. The first 11 primes: printf("2"); -c_forfilter (i, crange, crange_object(3, INT64_MAX, 2), +c_forfilter (i, crange, crange_obj(3, INT64_MAX, 2), isPrime(*i.ref) && c_flt_take(10) ){ @@ -260,22 +260,23 @@ void c_default_drop(Type* p); // does nothing --- ## Coroutines -This is an improved implementation of Simon Tatham's classic C code, which utilizes -the *Duff's device* trick. However, Tatham's implementation is not typesafe, -and it always allocates the coroutine's internal state dynamically. But most crucially, +This is a much improved implementation of +[Simon Tatham's coroutines](https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html), +which utilizes the *Duff's device* trick. Tatham's implementation is not typesafe, +and it always allocates the coroutine's internal state dynamically. But crucially, it does not let the coroutine do self-cleanup on early finish - i.e. it only frees the initial dynamically allocated memory. -In this implementation a coroutine may have any signature, but it should -take some struct pointer as parameter, which must contain the member `int cco_state;` +In this implementation, a coroutine may have any signature, but it should +take a struct pointer as parameter, which must contain the member `int cco_state;` The struct should normally store all the *local* variables to be used in the coroutine. It can also store input and output data if desired. -The coroutine example below generates Pythagorian triples, but the main user-loop -skips the triples which are upscaled version of smaller ones by checking -the gcd() function, and breaks when the diagonal size >= 100: +The coroutine example below generates Pythagorian triples, but the calling loop +skips the triples which are upscaled version of smaller ones, by checking +the gcd() function. It also ensures that it stops when the diagonal size >= 100: ```c -#include +#include struct triples { int n; // input: max number of triples to be generated. @@ -311,8 +312,7 @@ int gcd(int a, int b) { // greatest common denominator int main() { - puts("\nCoroutine triples:"); - struct triples t = {INT32_MAX}; + struct triples t = {.n=INT32_MAX}; int n = 0; while (triples_next(&t)) { @@ -324,12 +324,13 @@ int main() if (t.c < 100) printf("%d: {%d, %d, %d}\n", ++n, t.a, t.b, t.c); else - cco_stop(&t); // make sure coroutine cleanup is done + cco_stop(&t); // cleanup in next coroutine call/resume } } ``` ### Coroutine API -**Note**: `cco_yield()` may not be called inside a `switch` statement. Use `if-else-if` constructs instead. +**Note**: *cco_yield()* may not be called inside a `switch` statement. Use `if-else-if` constructs instead. +To resume the coroutine from where it was suspended with *cco_yield()*, simply call the coroutine again. | | Function / operator | Description | |:----------|:-------------------------------------|:----------------------------------------| @@ -340,7 +341,7 @@ int main() | `void` | `cco_begin(ctx)` | Begin coroutine block | | `rettype` | `cco_end(retval)` | End coroutine block with return value | | `rettype` | `cco_end()` | End coroutine block with (void) | -| `rettype` | `cco_yield(retval)` | Yield a value | +| `rettype` | `cco_yield(retval)` | Return a value and suspend execution | | `rettype` | `cco_yield(corocall2, ctx2, retval)` | Yield from another coroutine and return val | | `rettype` | `cco_yield(corocall2, ctx2)` | Yield from another coroutine (void) | | | From the caller side: | | diff --git a/include/stc/algo/crange.h b/include/stc/algo/crange.h index 91ffdd56..ca06c258 100644 --- a/include/stc/algo/crange.h +++ b/include/stc/algo/crange.h @@ -34,7 +34,7 @@ int main() // use a temporary crange object. int a = 100, b = INT32_MAX; - c_forfilter (i, crange, crange_object(a, b, 8), + c_forfilter (i, crange, crange_obj(a, b, 8), c_flt_skip(i, 10) && c_flt_take(i, 3)) printf(" %lld", *i.ref); @@ -46,7 +46,7 @@ int main() #include -#define crange_object(...) \ +#define crange_obj(...) \ (*(crange[]){crange_make(__VA_ARGS__)}) typedef long long crange_value; diff --git a/include/stc/ccommon.h b/include/stc/ccommon.h index 24ad59f9..1a2b3c3f 100644 --- a/include/stc/ccommon.h +++ b/include/stc/ccommon.h @@ -122,8 +122,9 @@ #define c_make(C, ...) \ C##_from_n((C##_raw[])__VA_ARGS__, c_sizeof((C##_raw[])__VA_ARGS__)/c_sizeof(C##_raw)) -#define c_arraylen(a) (intptr_t)(sizeof(a)/sizeof 0[a]) #define c_litstrlen(literal) (c_sizeof("" literal) - 1) +#define c_arraylen(a) (c_ARRAYLEN(a) + c_static_assert(sizeof(a) != sizeof(uintptr_t))) +#define c_ARRAYLEN(a) (intptr_t)(sizeof(a)/sizeof 0[a]) // Non-owning c-string typedef const char* crawstr; diff --git a/include/stc/extend.h b/include/stc/extend.h index cbfc4a12..66b3ebd1 100644 --- a/include/stc/extend.h +++ b/include/stc/extend.h @@ -52,9 +52,9 @@ typedef struct { i_extend i_type get; -} c_PASTE(i_type, Ext); +} c_PASTE(i_type, _ext); -#define c_getcon(cptr) c_container_of(cptr, _cx_memb(Ext), get) +#define c_getcon(cptr) c_container_of(cptr, _cx_memb(_ext), get) #define i_is_forward #define _i_inc diff --git a/misc/examples/forfilter.c b/misc/examples/forfilter.c index daea68b9..fbb7280f 100644 --- a/misc/examples/forfilter.c +++ b/misc/examples/forfilter.c @@ -54,7 +54,7 @@ fn main() { void demo2(void) { IVec vector = {0}; - c_forfilter (x, crange, crange_object(INT64_MAX), + c_forfilter (x, crange, crange_obj(INT64_MAX), c_flt_skipwhile(x, *x.ref != 11) && (*x.ref % 2) != 0 && c_flt_take(x, 5) diff --git a/misc/examples/functor.c b/misc/examples/functor.c index 7417080b..c0a4f8e8 100644 --- a/misc/examples/functor.c +++ b/misc/examples/functor.c @@ -14,10 +14,10 @@ #define i_con cpque #include -void print_queue(const char* name, IPQueExt q) { +void print_queue(const char* name, IPQue_ext q) { // NB: make a clone because there is no way to traverse // priority_queue's content without erasing the queue. - IPQueExt copy = {q.less, IPQue_clone(q.get)}; + IPQue_ext copy = {q.less, IPQue_clone(q.get)}; for (printf("%s: \t", name); !IPQue_empty(©.get); IPQue_pop(©.get)) printf("%d ", *IPQue_top(©.get)); @@ -38,9 +38,9 @@ int main() printf("%d ", data[i]); puts(""); - IPQueExt q1 = {int_less}; // Max priority queue - IPQueExt minq1 = {int_greater}; // Min priority queue - IPQueExt q5 = {int_lambda}; // Using lambda to compare elements. + IPQue_ext q1 = {int_less}; // Max priority queue + IPQue_ext minq1 = {int_greater}; // Min priority queue + IPQue_ext q5 = {int_lambda}; // Using lambda to compare elements. c_forrange (i, n) IPQue_push(&q1.get, data[i]); diff --git a/misc/examples/prime.c b/misc/examples/prime.c index a576a85c..d0887353 100644 --- a/misc/examples/prime.c +++ b/misc/examples/prime.c @@ -42,7 +42,7 @@ int main(void) puts("\n"); puts("Show the last 50 primes using a temporary crange generator:"); - c_forfilter (i, crange, crange_object(n - 1, 0, -2), + c_forfilter (i, crange, crange_obj(n - 1, 0, -2), cbits_test(&primes, *i.ref/2) && c_flt_take(i, 50) ){ -- cgit v1.2.3