summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--README.md51
-rw-r--r--docs/ccommon_api.md33
-rw-r--r--include/stc/algo/crange.h4
-rw-r--r--include/stc/ccommon.h3
-rw-r--r--include/stc/extend.h4
-rw-r--r--misc/examples/forfilter.c2
-rw-r--r--misc/examples/functor.c10
-rw-r--r--misc/examples/prime.c2
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 <stc/algo/coroutine.h>
+#include <stc/calgo.h>
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 <stc/ccommon.h>
-#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 <stc/i_con.h>
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 <stc/extend.h>
-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(&copy.get); IPQue_pop(&copy.get))
printf("%d ", *IPQue_top(&copy.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)
){