Skip to content

Commit

Permalink
Fairly large update before release 4.1, cleaning up docs and some min…
Browse files Browse the repository at this point in the history
…or additions.
  • Loading branch information
tylov committed Feb 12, 2023
1 parent 9904a7e commit 7dc6fdd
Show file tree
Hide file tree
Showing 18 changed files with 131 additions and 140 deletions.
37 changes: 22 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@ STC - Smart Template Containers for C

News: Version 4.1 Released (Feb 2023)
------------------------------------------------
Major changes:
I am happy to finally announce a new release! 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).
- Updates on cregex with several [new unicode character classes](docs/cregex_api.md#regex-cheatsheet).
- Uppercase flow-control macro names in ccommon.h [supported as alternatives](include/stc/priv/altnames.h).
- Updates on **cregex** with several [new unicode character classes](docs/cregex_api.md#regex-cheatsheet).
- Algorithms:
- [crange](docs/ccommon_api.md#crange) - similar to [boost::irange](https://www.boost.org/doc/libs/release/libs/range/doc/html/range/reference/ranges/irange.html) integer range generator.
- [c_forfilter](docs/ccommon_api.md#c_forfilter) - ranges-like filtering.
- [csort](misc/benchmarks/various/csort_bench.c) - fast quicksort with custom inline comparison.
- Support for [uppercase flow-control](include/stc/priv/altnames.h) macro names in ccommon.h.
- Create single header container versions with python script.
- Some API changes in cregex and cstr.
- [Previous changes for version 4](#version-4).

Expand Down Expand Up @@ -91,14 +96,14 @@ STC conventions
- Container names are prefixed by `c`, e.g. `cvec`, `cstr`.
- Public STC macros are prefixed by `c_`, e.g. `c_foreach`, `c_make`.
- Template parameter macros are prefixed by `i_`, e.g. `i_val`, `i_type`.
- All containers can be initialized with `{0}`, i.e. no heap allocation used for empty init.
- Common types for container type Con:
- All containers can be initialized with `{0}`, i.e. no heap allocation used by default init.
- Common types for a container type Con:
- Con
- Con_value
- Con_raw
- Con_iter
- Con_ssize
- Common function names for container type Con:
- Common function names for a container type Con:
- Con_init()
- Con_reserve(&con, capacity)
- Con_drop(&con)
Expand All @@ -116,8 +121,8 @@ STC conventions
- Con_next(&iter)
- Con_advance(iter, n)

Standout features of STC
------------------------
Some standout features of STC
-----------------------------
1. ***Centralized analysis of template arguments***. Assigns good defaults to non-specified templates.
You may specify a number of "standard" template arguments for each container, but as minimum only one is
required (two for maps). In the latter case, STC assumes the elements are basic types. For more complex types,
Expand All @@ -130,8 +135,10 @@ Finally, destruction of the lookup key (i.e. string literal) after usage is not
is convenient in C. A great ergonomic feature is that the alternative lookup type can also be used for adding
entries into containers through using the *emplace*-functions. E.g. `MyCStrVec_emplace_back(&vec, "Hello")`.
3. ***Standardized container iterators***. All container can be iterated the same way, and uses the
same element access syntax. E.g. `c_foreach (it, IntContainer, container) printf(" %d", *it.ref);` will work for
every type of container defined as `IntContainer` with `int` elements. Also the form `c_foreach (it, IntContainer, it1, it2)`
same element access syntax. E.g.:
- `c_foreach (it, IntContainer, container) printf(" %d", *it.ref);` will work for
every type of container defined as `IntContainer` with `int` elements. Also the form:
- `c_foreach (it, IntContainer, it1, it2)`
may be used to iterate from `it1` up to `it2`.

Usage
Expand Down Expand Up @@ -172,12 +179,12 @@ int main()
}
```
For user defined struct elements, `i_cmp` compare function should be defined, as the default `<` and `==`
only works for integral types. Alternatively, `#define i_opt c_no_cmp` to disable sorting and searching.
only works for integral types. *Alternatively, `#define i_opt c_no_cmp` to disable sorting and searching*.

Similarily, if an element destructor `i_valdrop` is defined, `i_valclone` function is required.
Alternatively `#define i_opt c_no_clone` to disable container cloning.
*Alternatively `#define i_opt c_no_clone` to disable container cloning.*

In order to include two **cvec**'s with different element types, include <stc/cvec.h> twice, e.g.:
In order to include two **cvec**'s with different element types, include <stc/cvec.h> twice:
```c
#define i_val struct One
#define i_opt c_no_cmp
Expand Down Expand Up @@ -326,7 +333,7 @@ As a special case, there may be non-templated functions in templated containers
once and if needed. Currently, define `i_extern` before including **clist** for its sorting function, and before
**cregex** or **utf8** to implement them (global `STC_EXTERN` can alternatively be defined).
It is possible to generate single headers by executing the python script in src/singleheader.py <header>.
It is possible to generate single headers by executing the python script `src/singleheader.py header-file > single`.
Conveniently, `src\libstc.c` implements non-templated functions as shared symbols for **cstr**, **csview**,
**cbits** and **crandom**. When building in shared mode (-DSTC_HEADER), you may include this file in your project,
Expand Down Expand Up @@ -386,7 +393,7 @@ Val:
- `i_valfrom` - Convertion func *i_val* <- *i_valraw*.
- `i_valto` - Convertion func *i_val*\* -> *i_valraw*.

Specials:
Specials (meta-template parameters):
- `i_keyclass TYPE` - Auto-binds to standard named functions: *TYPE_clone()*, *TYPE_drop()*, *TYPE_cmp()*, *TYPE_eq()*, *TYPE_hash()*. If `i_keyraw` is defined, function *TYPE_toraw()* is bound to `i_keyto`, and *TYPE_from()* binds to `i_keyfrom`. Only functions required by the container type needs to be defined. E.g.:
- *TYPE_hash()* and *TYPE_eq()* are only required by **cmap**, **cset** and smart pointers.
- *TYPE_cmp()* is not used by **cstack** and **cmap/cset**, or if *#define i_opt c_no_cmp* is specified.
Expand Down
48 changes: 25 additions & 23 deletions docs/ccommon_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,57 +218,57 @@ c_forrange (i, 30, 0, -5) printf(" %lld", i);
// 30 25 20 15 10 5
```

### c_forwhile, c_forfilter
### c_forfilter
Iterate containers with stop-criteria and chained range filtering.

| Usage | Description |
|:----------------------------------------------------|:---------------------------------------|
| `c_forwhile (it, ctype, start, pred)` | Iterate until pred is false |
| `c_forfilter (it, ctype, container, filter)` | Filter out items in chain with && |
| `c_forfilter (it, ctype, container, filter, pred)` | Filter and iterate until pred is false |
| `c_forwhile (it, ctype, start, pred)` | Iterate until pred is false |

| Built-in filter | Description |
|:----------------------------------|:-------------------------------------|
| `c_flt_skip(it, numItems)` | Skip numItems |
| `c_flt_take(it, numItems)` | Take numItems |
| `c_flt_skipwhile(it, predicate)` | Skip items until predicate is false |
| `c_flt_takewhile(it, predicate)` | Take items until predicate is false |
| `c_flt_last(it)` | Get count of last filter successes |
| `c_flt_lastwhile(it)` | Get value of last while-filter |

`it.index` holds the index of the source item, and `it.count` the current number of items taken.
`it.index` holds the index of the source item.
```c
#define i_type IVec
#define i_val int
#include <stc/cstack.h>
// Example:
#include <stc/algo/crange.h>
#include <stc/algo/filter.h>
#include <stdio.h>

bool isPrime(int i) {
for (int j=2; j*j <= i; ++j) if (i % j == 0) return false;
return true;
}
#define isOdd(i) ((i) & 1)
// Get 10 prime numbers after 1 million, but only every 25th of them.

int main() {
c_auto (IVec, vec) {
c_forrange (i, 1000) IVec_push(&vec, 1000000 + i);

c_forfilter (i, IVec, vec,
isOdd(*i.ref)
&& c_flt_skip(i, 100) // built-in
&& isPrime(*i.ref)
, c_flt_take(i, 10)) { // breaks loop on false.
printf(" %d", *i.ref);
}
puts("");
crange R = crange_make(1000001, INT32_MAX, 2);

c_forfilter (i, crange, R,
isPrime(*i.ref)
&& (c_flt_skip(i, INT32_MAX) ||
c_flt_last(i) % 25 == 0)
, c_flt_take(i, 10)) // breaks loop on false.
{
printf(" %d", *i.ref);
}
}
// Out: 1000211 1000213 1000231 1000249 1000253 1000273 1000289 1000291 1000303 1000313
// Out: 1000303 1000639 1000999 1001311 1001593 1001981 1002299 1002583 1002887 1003241
```
Note that `c_flt_take()` is given as an optional argument, which makes the loop stop when it becomes false (for efficiency). Chaining it after `isPrime()` instead will give same result, but the full input is processed.
Note that `c_flt_take()` is given as an optional argument, which breaks the loop on false
(for efficiency). Without the comma, it will give same result, but the full input is processed first.
### c_make, c_new, c_delete
- **c_make**: Make a container from a literal initializer list. Example:
- **c_make**: Make any container from an initializer list. Example:
```c
#define i_val_str // cstr value type
#include <stc/cset.h>
Expand All @@ -277,7 +277,9 @@ Note that `c_flt_take()` is given as an optional argument, which makes the loop
#define i_val int
#include <stc/cmap.h>
...
cset_str myset = c_make(cset_str, {"This", "is", "the", "story"}); // note: const char* values given!
// Initializes with const char*, internally converted to cstr!
cset_str myset = c_make(cset_str, {"This", "is", "the", "story"});
int x = 7, y = 8;
cmap_int mymap = c_make(cmap_int, { {1, 2}, {3, 4}, {5, 6}, {x, y} });
```
Expand All @@ -293,7 +295,7 @@ c_delete(cstr, stringptr);
```
### crange
- **crange** is 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*:
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_obj(...) // create a compound literal crange object
crange crange_make(stop); // will generate 0, 1, ..., stop-1
Expand Down
36 changes: 10 additions & 26 deletions include/stc/algo/csort.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stc/priv/template.h>
#include <stc/ccommon.h>
#include <stc/priv/template.h>

/* Generic Quicksort in C, performs as fast as c++ std::sort().
template params:
Expand All @@ -31,37 +31,21 @@ template params:
// test:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define i_val int
#include <stc/algo/csort.h>
#include <stc/crandom.h>
#ifdef __cplusplus
#include <algorithm>
#endif
void testsort(csort_int_value *a, size_t size, const char *desc) {
clock_t t = clock();
csort_int(a, size);
t = clock() - t;
int main() {
int arr[] = {23, 321, 5434, 25, 245, 1, 654, 33, 543, 21};
csort_int(arr, c_arraylen(arr));
printf("%s: %zu elements sorted in %.3fms\n",
desc, size, t*1000.0/CLOCKS_PER_SEC);
for (int i = 0; i < c_arraylen(arr); i++)
printf(" %d", arr[i]);
puts("");
}
*/

int main() {
size_t i, size = 10000000;
csort_int_value *a = (csort_int_value*)malloc(sizeof(*a) * size);
if (a != NULL) {
for (i = 0; i < size; i++)
a[i] = crandom() & (1U << 28) - 1;
testsort(a, size, "random");
for (i = 0; i < 20; i++) printf(" %d", a[i]);
puts("");
free(a);
}
}*/
typedef i_val c_PASTE(c_PASTE(csort_, i_tag), _value);
typedef i_val c_PASTE(c_CONCAT(csort_, i_tag), _value);

static inline void c_PASTE(cisort_, i_tag)(i_val arr[], intptr_t lo, intptr_t hi) {
for (intptr_t j = lo, i = lo + 1; i <= hi; j = i, ++i) {
Expand Down
6 changes: 4 additions & 2 deletions include/stc/algo/filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ int main()
#define c_flt_skip(i, n) (++(i).s1[(i).s1top++] > (n))
#define c_flt_skipwhile(i, pred) ((i).s2[(i).s2top++] |= !(pred))
#define c_flt_takewhile(i, pred) !c_flt_skipwhile(i, pred)
#define c_flt_last(i) (i).s1[(i).s1top-1]
#define c_flt_lastwhile(i) (i).s2[(i).s2top-1]

#define c_forfilter(...) c_MACRO_OVERLOAD(c_forfilter, __VA_ARGS__)

Expand All @@ -68,10 +70,10 @@ int main()

#define c_forfilter_B(i, C, start, filter) \
for (struct {C##_iter it; C##_value *ref; \
uint32_t s1[c_NFILTERS], index, count; \
uint32_t s1[c_NFILTERS], index; \
bool s2[c_NFILTERS]; uint8_t s1top, s2top;} \
i = {.it=start, .ref=i.it.ref}; i.it.ref \
; C##_next(&i.it), i.ref = i.it.ref, ++i.index, i.s1top=0, i.s2top=0) \
if (!((filter) && ++i.count)) ; else
if (!(filter)) ; else

#endif
6 changes: 3 additions & 3 deletions include/stc/coption.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ static void coption_permute_(char *argv[], int j, int n) {
*/
static int coption_get(coption *opt, int argc, char *argv[],
const char *shortopts, const coption_long *longopts) {
int optc = -1, i0, j, posixly_correct = (shortopts[0] == '+');
int optc = -1, i0, j, posixly_correct = (shortopts && shortopts[0] == '+');
if (!posixly_correct) {
while (opt->_i < argc && (argv[opt->_i][0] != '-' || argv[opt->_i][1] == '\0'))
++opt->_i, ++opt->_nargs;
Expand Down Expand Up @@ -149,12 +149,12 @@ static int coption_get(coption *opt, int argc, char *argv[],
}
}
}
} else { /* a short option */
} else if (shortopts) { /* a short option */
const char *p;
if (opt->_pos == 0) opt->_pos = 1;
optc = opt->opt = argv[opt->_i][opt->_pos++];
opt->_optstr[1] = optc, opt->optstr = opt->_optstr;
p = strchr((char *) shortopts, optc);
p = strchr(shortopts, optc);
if (p == 0) {
optc = '?'; /* unknown option */
} else if (p[1] == ':') {
Expand Down
4 changes: 2 additions & 2 deletions misc/benchmarks/build_all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ if [ ! -z "$1" ] ; then
cc=$@
fi
if [ $run = 0 ] ; then
for i in *.cpp misc/*.c* picobench/*.cpp plotbench/*.cpp ; do
for i in *.cpp various/*.c* picobench/*.cpp plotbench/*.cpp ; do
echo $cc -I../include $i -o $(basename -s .cpp $i).exe
$cc -I../include $i -o $(basename -s .cpp $i).exe
done
else
for i in misc/*.c* picobench/*.cpp ; do
for i in various/*.c* picobench/*.cpp ; do
echo $cc -O3 -I../include $i
$cc -O3 -I../include $i
if [ -f $(basename -s .c $i).exe ]; then ./$(basename -s .c $i).exe; fi
Expand Down
File renamed without changes.
61 changes: 61 additions & 0 deletions misc/benchmarks/various/csort_bench.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Generic Quicksort in C, performs as fast as c++ std::sort().
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#ifdef __cplusplus
#include <algorithm>
#endif
#define i_val int
#include <stc/algo/csort.h>

#define ROTL(d,bits) ((d<<(bits)) | (d>>(8*sizeof(d)-(bits))))
uint64_t random(uint64_t s[3]) {
uint64_t xp = s[0], yp = s[1], zp = s[2];
s[0] = 15241094284759029579u * zp;
s[1] = yp - xp; s[1] = ROTL(s[1], 12);
s[2] = zp - yp; s[2] = ROTL(s[2], 44);
return xp;
}

void testsort(int *a, int size, const char *desc) {
clock_t t = clock();
#ifdef __cplusplus
{ printf("std::sort: "); std::sort(a, a + size); }
#else
{ printf("stc_sort: "); csort_int(a, size); }
#endif
t = clock() - t;

printf("time: %.1fms, n: %d, %s\n",
(double)t*1000.0/CLOCKS_PER_SEC, size, desc);
}


int main(int argc, char *argv[]) {
size_t i, size = argc > 1 ? strtoull(argv[1], NULL, 0) : 10000000;
uint64_t s[3] = {123456789, 3456789123, 789123456};

int32_t *a = (int32_t*)malloc(sizeof(*a) * size);
if (!a) return -1;

for (i = 0; i < size; i++)
a[i] = random(s) & (1U << 30) - 1;
testsort(a, size, "random");
for (i = 0; i < 20; i++)
printf(" %d", (int)a[i]);
puts("");
for (i = 0; i < size; i++)
a[i] = i;
testsort(a, size, "sorted");
for (i = 0; i < size; i++)
a[i] = size - i;
testsort(a, size, "reverse sorted");
for (i = 0; i < size; i++)
a[i] = 126735;
testsort(a, size, "constant");
for (i = 0; i < size; i++)
a[i] = i + 1;
a[size - 1] = 0;
testsort(a, size, "rotated");
free(a);
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 7dc6fdd

Please sign in to comment.