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 generally easier to read and write too.
"No raw loops" - Sean Parent
c_each - Ranged sequence iteration
#include "stc/common.h"
Usage | Description |
---|---|
for (c_each (it, CntType, container)) |
Iteratate all elements |
for (c_each (it, CntType, it1, it2)) |
Iterate the range [it1, it2) |
for (c_each_reverse (it, CntType, container)) |
Iteratate elements in reverse: vec, deque, queue, stack |
for (c_each_reverse (it, CntType, it1, it2))` |
Iteratate range [it1, it2) elements in reverse. |
for (c_each_n (it, CntType, container, n)) |
Iteratate n first elements. Index variable is {it}_index . |
for (c_each_kv (key, val, CntType, container)) |
Iterate maps with "structured binding" |
[ Run this code ]
#define i_type IMap, int, int
#include "stc/smap.h"
// ...
IMap map = c_make(IMap, {{23,1}, {3,2}, {7,3}, {5,4}, {12,5}});
for (c_each(i, IMap, map))
printf(" %d", i.ref->first);
// 3 5 7 12 23
// same, with raw for loop:
for (IMap_iter i = IMap_begin(&map); i.ref; IMap_next(&i))
printf(" %d", i.ref->first);
// iterate from iter to end
IMap_iter iter = IMap_find(&map, 7);
for (c_each(i, IMap, iter, IMap_end(&map)))
printf(" %d", i.ref->first);
// 7 12 23
// iterate first 3 with an index count enumeration
for (c_each_n(i, IMap, map, 3))
printf(" %zd:(%d %d)", i_index, i.ref->first, i.ref->second);
// 0:(3 2) 1:(5 4) 2:(7 3)
// iterate a map using "structured binding" of the key and val pair:
for (c_each_kv(id, count, IMap, map))
printf(" (%d %d)", *id, *count);
c_items - Literal list element iteration
Iterate compound literal array elements. In addition to i.ref
, you can access i.index
and i.size
.
// apply multiple push_backs
for (c_items(i, int, {4, 5, 6, 7}))
list_i_push_back(&lst, *i.ref);
// insert in existing map
for (c_items(i, hmap_ii_value, {{4, 5}, {6, 7}}))
hmap_ii_insert(&map, i.ref->first, i.ref->second);
// string literals pushed to a stack of cstr elements:
for (c_items(i, const char*, {"Hello", "crazy", "world"}))
stack_cstr_emplace(&stk, *i.ref);
c_range - Integer range iteration
c_range
: abstraction for iterating sequence of integers. Like python's for i in range() loop. Usesisize
(ptrdiff_t) as control variable.c_range32
is like c_range, but usesint32_t
as control variable.c_range_t
is like c_range, but takes an additional type for the control variable as first argument.
Usage | Python equivalent |
---|---|
for (c_range (stop)) |
for _ in range (stop): |
for (c_range (i, stop)) |
for i in range (stop): |
for (c_range (i, start, stop)) |
for i in range (start, stop): |
for (c_range (i, start, stop, step)) |
for i in range (start, stop, step): |
Usage |
---|
for (c_range_t (IntType, i, stop)) |
for (c_range_t (IntType, i, start, stop)) |
for (c_range_t (IntType, i, start, stop, step)) |
for (c_range(5)) printf("x");
// xxxxx
for (c_range(i, 5)) printf(" %lld", i);
// 0 1 2 3 4
for (c_range(i, -3, 3)) printf(" %lld", i);
// -3 -2 -1 0 1 2
for (c_range(i, 30, 0, -5)) printf(" %lld", i);
// 30 25 20 15 10 5
c_ffilter - Filtered range iteration
For-loop variant of c_filter
in generic algorithms.
#include <stdio.h>
#include "stc/algorithm.h"
#define i_type IVec,int
#include "stc/stack.h"
int main(void) {
IVec vec = c_make(IVec, {0, 1, 2, 3, 4, 5, 80, 6, 7, 80, 8, 9, 80,
10, 11, 12, 13, 14, 15, 80, 16, 17});
#define ff_skipValue(i, x) (*i.ref != (x))
#define ff_isEven(i) ((*i.ref & 1) == 0)
#define ff_square(i) (*i.ref * *i.ref)
int sum = 0;
for (c_ffilter(i, IVec, vec, true
&& c_fflt_skipwhile(i, *i.ref != 80)
&& c_fflt_skip(i, 1)
&& ff_isEven(i)
&& ff_skipValue(i, 80)
&& c_fflt_map(i, ff_square(i))
&& c_fflt_take(i, 5)
)){
sum += *i.ref;
}
printf("sum: %d\n", sum);
IVec_drop(&vec);
}
This is a tiny, robust and fully typesafe implementation of sum types. They work similarly as in Zig, Odin and Rust, and is just as easy and safe to use. Each tuple/parentesized field is an enum (tag) with an associated data type (payload), called a variant of the sum type. The sum type itself is a union type.
Sum types - aka variants or tagged unions
Synopsis:
// Define a sum type
c_sumtype (SumType,
(VariantEnum1, VariantType1),
...
(VariantEnumN, VariantTypeN)
);
SumType c_variant(VariantEnum tag, VariantType value); // Sum type constructor
bool c_holds(const SumType* obj, VariantEnum tag); // does obj hold VariantType?
int c_tag_index(SumType* obj); // 1-based index (mostly for debug)
// Use a sum type (1)
c_when (SumType* obj) {
c_is(VariantEnum1, VariantType1* x) <body>;
c_is(VariantEnum2) c_or_is(VariantEnum3) <body>;
...
c_otherwise <body>;
}
// Use a sum type (2)
if (c_is(SumType* obj, VariantEnum1, VariantType1* x))
<body>;
The c_when statement is exhaustive. The compiler will give a warning if not all variants are
covered by c_is (requires -Wall
or -Wswitch
gcc/clang compiler flag). The first enum value
is deliberately set to 1 in order to easier detect non/zero-initialized variants.
- Note: The
x
variables in the synopsis are "auto" type declared/defined - see examples. - Caveat 1: The use of
continue
in ac_when
orif (c_is())
block, whilec_when
is inside a loop will not work as expected. It will only break out of thec_when
-block. Instead, usegoto
to jump to the end of the loop.break
will break out ofc_when
, i.e. it behaves likeswitch
. - Caveat 2: Sum types will generally not work in coroutines because the
x
variable is local and therefore will not be preserved acrosscco_yield..
/cco_await..
. - Caveat 3: In the second (2) usage,
c_is(obj,VE,x)
combined with&&
or||
will not compile.
[ Run this code ]
#include <stdio.h>
#include "stc/algorithm.h"
c_sumtype (Tree,
(Empty, _Bool),
(Leaf, int),
(Node, struct {int value; Tree *left, *right;})
);
int tree_sum(Tree* t) {
c_when (t) {
c_is(Empty) return 0;
c_is(Leaf, v) return *v;
c_is(Node, n) return n->value + tree_sum(n->left) + tree_sum(n->right);
}
return -1;
}
int main(void) {
Tree* tree =
&c_variant(Node, {1,
&c_variant(Node, {2,
&c_variant(Leaf, 3),
&c_variant(Leaf, 4)
}),
&c_variant(Leaf, 5)
});
printf("sum = %d\n", tree_sum(tree));
}
This example has two sum types. The MessageChangeColor
variant uses the Color
sum type as
its data type (payload). Because C does not have namespaces, it is recommended to prefix the variant names with the sum type name, as with regular enums.
[ Run this code ]
// https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html#destructuring-enums
#include <stdio.h>
#include <stc/algorithm.h>
#include <stc/cstr.h>
c_sumtype (Color,
(ColorRgb, struct {int32 r, g, b;}),
(ColorHsv, struct {int32 h, s, v;})
);
c_sumtype (Message,
(MessageQuit, _Bool),
(MessageMove, struct {int32 x, y;}),
(MessageWrite, cstr),
(MessageChangeColor, Color)
);
void Message_drop(Message* m) {
if (c_is(m, MessageWrite, s)) cstr_drop(s);
}
int main(void) {
Message msg[] = {
c_variant(MessageMove, {42, 314}),
c_variant(MessageWrite, cstr_from("Attack!")),
c_variant(MessageChangeColor, c_variant(ColorHsv, {0, 160, 255})),
};
for (c_range(i, c_arraylen(msg)))
c_when (&msg[i]) {
c_is(MessageQuit) {
printf("The Quit variant has no data to destructure.\n");
}
c_is(MessageMove, p) {
printf("Move %d in the x direction and %d in the y direction\n", p->x, p->y);
}
c_is(MessageWrite, text) {
printf("Text message: %s\n", cstr_str(text));
}
c_is(MessageChangeColor, cc) c_when (cc) {
c_is(ColorRgb, c)
printf("Change color to red %d, green %d, and blue %d\n", c->r, c->g, c->b);
c_is(ColorHsv, c)
printf("Change color to hue %d, saturation %d, value %d\n", c->h, c->s, c->v);
}
Message_drop(&msg[i]);
}
}
c_make, c_push_items, c_drop - Generic container operations
These work on any container. c_make() may also be used for cspan views.
- c_make - construct any container from an initializer list
- c_push_items - push values onto any container from an initializer list
- c_drop - drop (destroy) multiple containers of the same type
[ Run this code ]
#include <stdio.h>
#define i_type Vec, int
#include "stc/vec.h"
#define i_type Map, int, int
#include "stc/hmap.h"
c_func (split_map,(Map map), ->, struct {Vec keys, values;}) {
split_map_result out = {0};
for (c_each_kv(k, v, Map, map)) {
Vec_push(&out.keys, *k);
Vec_push(&out.values, *v);
}
return out;
}
int main(void) {
Vec vec = c_make(Vec, {1, 2, 3, 4, 5, 6});
Map map = c_make(Map, {{1, 2}, {3, 4}, {5, 6}});
c_push_items(Vec, &vec, {7, 8, 9, 10, 11, 12});
c_push_items(Map, &map, {{7, 8}, {9, 10}, {11, 12}});
for (c_each(i, Vec, vec))
printf("%d ", *i.ref);
puts("");
for (c_each_kv(k, v, Map, map))
printf("[%d %d] ", *k, *v);
puts("");
split_map_result res = split_map(map);
for (c_each(i, Vec, res.values))
printf("%d ", *i.ref);
puts("");
c_drop(Vec, &vec, &res.keys, &res.values);
c_drop(Map, &map);
}
c_find, c_reverse, c_append, c_erase - Container/array operations
Find linearily in containers using a predicate. value
is a pointer to each element in predicate.
outiter_ptr must be defined prior to call.
- void
c_find_if
(CntType, cnt, outiter_ptr, pred). - void
c_find_if
(CntType, startiter, enditer, outiter_ptr, pred) - void
c_find_reverse_if
(CntType, cnt, outiter_ptr, pred) - void
c_find_reverse_if
(CntType, startiter, enditer, outiter_ptr, pred)
- void
c_reverse
(CntType, cnt); // reverse a cspan, vec, stack, queue or deque type. - void
c_reverse_array
(array, len); // reverse an array of elements.
Clones any container onto an arbitrary container type, optionally using a predicate to filter out elements.
Requires only that the element types are equal for the two containers.
value
is the pointer to each element in predicate. See example below for usage.
- void
c_append
(CntType, outcnt_ptr, cnt) - void
c_append
(OutCnt, outcnt_ptr, CntType, cnt) - void
c_append_if
(CntType, outcnt_ptr, cnt, pred) - void
c_append_if
(OutCnt, outcnt_ptr, CntType, cnt, pred)
Erase linearily in containers using a predicate. value
is a pointer to each element in predicate.
- void
c_erase_if
(CntType, cnt_ptr, pred)`. Use with *list, hmap, hset, smap, and sset. - void
c_eraseremove_if
(CntType, cnt_ptr, pred)`. Use with stack, vec, deque, and queue only.
[ Run this code ]
#include <stdio.h>
#include "stc/cstr.h"
#include "stc/algorithm.h"
#define i_type Vec, int, c_use_eq
#include "stc/stack.h"
#define i_type List, int, c_use_eq
#include "stc/list.h"
#define i_type Map
#define i_keypro cstr
#define i_val int
#include "stc/smap.h"
int main(void)
{
Vec vec = c_make(Vec, {2, 30, 21, 5, 9, 11});
Vec outvec = {0};
// Clone all *value > 10 to outvec. Note: `value` is a pointer to current element
c_append_if(Vec, &outvec, vec, *value > 10);
for (c_each(i, Vec, outvec)) printf(" %d", *i.ref);
puts("");
// Search vec for first value > 20.
Vec_iter result;
c_find_if(Vec, vec, &result, *value > 20);
if (result.ref) printf("found %d\n", *result.ref);
// Erase values between 20 and 25 in vec:
c_eraseremove_if(Vec, &vec, 20 < *value && *value < 25);
for (c_each(i, Vec, vec)) printf(" %d", *i.ref);
puts("");
// Erase all values > 20 in a linked list:
List list = c_make(List, {2, 30, 21, 5, 9, 11});
c_erase_if(List, &list, *value > 20);
for (c_each(i, List, list)) printf(" %d", *i.ref);
puts("");
// Search a sorted map from it1, for the first string containing "hello" and erase it:
Map map = c_make(Map, {{"yes",1}, {"no",2}, {"say hello from me",3}, {"goodbye",4}});
Map_iter res, it1 = Map_begin(&map);
c_find_if(Map, it1, Map_end(&map), &res, cstr_contains(&value->first, "hello"));
if (res.ref) Map_erase_at(&map, res);
// Erase all strings containing "good" in the sorted map:
c_erase_if(Map, &map, cstr_contains(&value->first, "good"));
for (c_each(i, Map, map)) printf("%s, ", cstr_str(&i.ref->first));
c_drop(Vec, &vec, &outvec);
List_drop(&list);
Map_drop(&map);
}
c_filter, c_filter_zip, c_filter_pairwise - Ranged filtering
Functional programming with chained &&
filtering. value
is the pointer to current value.
It enables a subset of functional programming like in other popular languages.
- Note 1: The _reverse variants only works with vec, deque, stack, queue containers.
- Note 2: There is also a
c_ffilter
loop variant ofc_filter
. It uses the filter namingsc_fflt_skip(it, numItems)
, etc.
Usage | Description |
---|---|
void c_filter (CntType, container, filters) |
Filter items in chain with the && operator |
void c_filter_from (CntType, start, filters) |
Filter from start iterator |
void c_filter_reverse (CntType, cnt, filters) |
Filter items in reverse order |
void c_filter_reverse_from (CntType, rstart, filters) |
Filter reverse from rstart iterator |
c_filter_zip, c_filter_pairwise: | |
void c_filter_zip (CntType, cnt1, cnt2, filters)` |
Filter (cnt1, cnt2) items |
void c_filter_zip (CntType1, cnt1, CntType2, cnt2, filters)` |
May use different types for cnt1, cnt2 |
void c_filter_reverse_zip (CntType, cnt1, cnt2, filters)` |
Filter (cnt1, cnt2) items in reverse order |
void c_filter_reverse_zip (CntType1, cnt1, CntType2, cnt2, filters)` |
May use different types for cnt1, cnt2 |
void c_filter_pairwise (CntType, cnt, filters)` |
Filter items pairwise as value1, value2 |
Built-in filter | Description |
---|---|
void c_flt_skip (numItems) |
Skip numItems (increments count) |
void c_flt_take (numItems) |
Take numItems only (increments count) |
void c_flt_skipwhile (predicate) |
Skip items until predicate is false |
void c_flt_takewhile (predicate) |
Take items until predicate is false |
int c_flt_counter () |
Increment count and return it |
int c_flt_getcount () |
Number of items passed skip/take/counter |
Type c_flt_map (expr) |
Map expr to current value. Input unchanged |
Type c_flt_src |
Pointer variable to current unmapped source value |
Type value |
Pointer variable to (possible mapped) value |
For c_filter_zip, c_filter_pairwise: | |
Type c_flt_map1 (expr) |
Map expr to value1. Input unchanged |
Type c_flt_map2 (expr) |
Map expr to value2. Input unchanged |
Type c_flt_src1 , c_flt_src2 |
Pointer variables to current unmapped source values |
Type value1 , value2 |
Pointer variables to (possible mapped) values |
[ Run this example ]
#include <stdio.h>
#define i_type Vec, int
#include "stc/stack.h"
#include "stc/algorithm.h"
int main(void)
{
Vec vec = c_make(Vec, {1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10, 11, 12, 5});
c_filter(Vec, vec, true
&& c_flt_skipwhile(*value < 3) // skip leading values < 3
&& (*value & 1) == 1 // then use odd values only
&& c_flt_map(*value * 2) // multiply by 2
&& c_flt_takewhile(*value < 20) // stop if mapped *value >= 20
&& printf(" %d", *value) // print value
);
// 6 10 14 2 6 18
puts("");
Vec_drop(&vec);
}
c_all_of, c_any_of, c_none_of - Boolean container operations
Test a container/range using a predicate. result is output and must be declared prior to call.
- void
c_all_of
(CntType, cnt, bool* result, pred) - void
c_any_of
(CntType, cnt, bool* result, pred) - void
c_none_of
(CntType, cnt, bool* result, pred)
#define DivisibleBy(n) (*value % (n) == 0) // `value` refers to the current element
bool result;
c_any_of(vec_int, vec, &result, DivisibleBy(7));
if (result)
puts("At least one number is divisible by 7");
sort, lower_bound, binary_search - Faster quicksort and binary search
X
refers to the template name specified byi_type
ori_key
.- All containers with random access may be sorted, including regular C-arrays, i.e. stack, vec
and deque when either
i_use_cmp
,i_cmp
ori_less
is defined. - Linked list may also be sorted, i.e. only X_sort() is available.
// Sort c-arrays by defining i_type and include "stc/sort.h":
void X_sort(const X array[], isize len);
isize X_lower_bound(const X array[], i_key key, isize len);
isize X_binary_search(const X array[], i_key key, isize len);
// or random access containers when `i_less`, `i_cmp` is defined:
void X_sort(X* self);
isize X_lower_bound(const X* self, i_key key);
isize X_binary_search(const X* self, i_key key);
// functions for sub ranges:
void X_sort_lowhigh(X* self, isize low, isize high);
isize X_lower_bound_range(const X* self, i_key key, isize start, isize end);
isize X_binary_search_range(const X* self, i_key key, isize start, isize end);
i_type
may be customized in the normal way, along with comparison function i_cmp
or i_less
.
The X_sort(), X_sort_lowhigh() functions are about twice as fast as qsort() and comparable in speed with *std::sort()**. Both X_binary_seach() and X_lower_bound() are about 30% faster than c++ std::lower_bound().
[ Run this code ]
#define i_key int // sort a regular c-array of ints
#include "stc/sort.h"
#include <stdio.h>
int main(void) {
int arr[] = {5, 3, 5, 9, 7, 4, 7, 2, 4, 9, 3, 1, 2, 6, 4};
ints_sort(arr, c_arraylen(arr)); // `ints` derived from the `i_key` name
for (c_range(i, c_arraylen(arr)))
printf(" %d", arr[i]);
}
#define i_type MyDeq, int, c_use_cmp // int elements, enable sorting
#include "stc/deque.h"
#include <stdio.h>
int main(void) {
MyDeq deq = c_make(MyDeq, {5, 3, 5, 9, 7, 4, 7});
MyDeq_sort(&deq);
for (c_each(i, MyDeq, deq))
printf(" %d", *i.ref);
puts("");
MyDeq_drop(&deq);
}
c_defer, c_with - RAII macros
Usage | Description |
---|---|
c_defer (deinit, ...) {} |
Defers execution of deinit s to end of scope |
c_with (init, deinit) {} |
Declare and/or initialize a variable. Defers execution of deinit to end of scope |
c_with (init, condition, deinit) {} |
Adds a predicate in order to exit early if init fails |
continue |
Break out of a c_with scope without resource leakage |
Note: Regular return , break and continue must not be used |
|
anywhere inside a defer scope. |
// declare and init a new scoped variable and specify the deinitialize call:
c_with (cstr str = cstr_lit("Hello"), cstr_drop(&str))
{
cstr_append(&str, " world");
printf("%s\n", cstr_str(&str));
}
pthread_mutex_t lock;
...
// use c_with without variable declaration:
c_with (pthread_mutex_lock(&lock), pthread_mutex_unlock(&lock))
{
// syncronized code
}
Example 2: Load each line of a text file into a vector of strings:
#include <errno.h>
#include "stc/cstr.h"
#define i_keypro cstr
#include "stc/vec.h"
// receiver should check errno variable
vec_cstr readFile(const char* name)
{
vec_cstr vec = {0}; // returned
c_with (FILE* fp = fopen(name, "r"), fp != NULL, fclose(fp))
c_with (cstr line = {0}, cstr_drop(&line))
while (cstr_getline(&line, fp))
vec_cstr_emplace(&vec, cstr_str(&line));
return vec;
}
int main(void)
{
c_with (vec_cstr vec = readFile(__FILE__), vec_cstr_drop(&vec))
for (c_each(i, vec_cstr, vec))
printf("| %s\n", cstr_str(i.ref));
}
crange, c_iota - Integer range objects
An integer sequence generator type, similar to boost::irange.
crange
usesisize
(ptrdiff_t) as control variablecrange32
is like crange, but usesint32_t
as control variable, which may be faster.
crange crange_make(stop); // 0, 1, ... stop-1
crange crange_make(start, stop); // start, start+1, ... stop-1
crange crange_make(start, stop, step); // start, start+step, ... upto-not-including stop,
// step may be negative.
crange_iter crange_begin(crange* self);
void crange_next(crange_iter* it);
crange& c_iota(start); // l-value; NB! otherwise like crange_make(start, INTPTR_MAX)
crange& c_iota(start, stop); // l-value; otherwise like crange_make(start, stop)
crange& c_iota(start, stop, step); // l-value; otherwise like crange_make(start, stop, step)
The crange_value type is isize. Variables start, stop, and step are of type crange_value.
[ Run this code ]
// 1. All primes less than 32: See below for c_filter() and is_prime()
crange r1 = crange_make(3, 32, 2);
printf("2"); // first prime
c_filter(crange, r1, true
&& is_prime(*value)
&& printf(" %zi", *value)
);
// 2 3 5 7 11 13 17 19 23 29 31
// 2. The first 11 primes:
// c_iota() can be used as argument to c_filter.
printf("2"); // first prime
c_filter(crange, c_iota(3), true
&& is_prime(*value)
&& (c_flt_take(10), printf(" %zi", *value))
);
// 2 3 5 7 11 13 17 19 23 29 31
c_func - Function with on-the-fly defined return type
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.
[ Run this code ]
Vec get_data(void) {
return c_make(Vec, {1, 2, 3, 4, 5, 6});
}
// same as get_data(), but with the c_func macro "syntax".
c_func (get_data1,(void), ->, Vec) {
return c_make(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_make(Vec, {1, 2, 3, 4, 5, 6}),
.v2 = c_make(Vec, {7, 8, 9, 10, 11})
};
}
// 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 out = {Vec_with_size(1024, '\0')};
fread(out.vec.data, sizeof(out.vec.data[0]), 1024, fp);
fclose(fp);
return out;
}
c_new, c_delete, c_malloc, etc. - Allocation helpers
- Type*
c_new
(Type, value) - Allocate and initialize a new object on the heap with value. - Type*
c_new_n
(Type, n) - Allocate an array of n new objects on the heap, initialized to zero. - void
c_delete
(Type, ptr) - Type_drop(ptr) and c_free(ptr, ..) allocated on the heap. NULL is OK. - void
c_delete_n
(Type, arr, n) - Type_drop(&arr[i]) and c_free(arr, ..) of n objects allocated on the heap. (NULL, 0) is OK.
#include "stc/cstr.h"
cstr* stringptr = c_new (cstr, cstr_from("Hello"));
printf("%s\n", cstr_str(stringp));
c_delete(cstr, stringptr);
Memory allocator wrappers which uses signed sizes. Note that the signatures for
c_realloc() and c_free() have an extra size parameter. These will be used as
default in containers unless i_malloc
, i_calloc
, i_realloc
, and i_free
are user defined. See
Per container-instance customization
- void*
c_malloc
(isize sz) - void*
c_calloc
(isize n, isize sz) - void*
c_realloc
(void* old_p, isize old_sz, isize new_sz) - void
c_free
(void* p, isize sz)
c_swap, c_arraylen, c_const_cast, c_safe_case
Side effect- and typesafe macro for swapping internals of two objects of same type:
double x = 1.0, y = 2.0;
c_swap(&x, &y);
Return number of elements in an array. array must not be a pointer!
int array[] = {1, 2, 3, 4};
isize n = c_arraylen(array);
Type-safe casting a from const (pointer):
const char cs[] = "Hello";
char* s = c_const_cast(char*, cs); // OK
int* ip = c_const_cast(int*, cs); // issues a warning!
// Type safe casting:
#define tofloat(d) c_safe_cast(float, double, d)