From 79c83eba7933f49e766dbeb85f9c40ee17e06edf Mon Sep 17 00:00:00 2001 From: Tyge Løvset Date: Wed, 22 Dec 2021 12:35:36 +0100 Subject: Renamed csptr to carc. i_key/val_ref renamed to i_key/val_sptr. Change inspired by Rust Arc/Rc. cbox name is taken from Rust Box type. --- README.md | 73 ++++++++++-------- docs/cbox_api.md | 10 +-- docs/csptr_api.md | 96 ++++++++++++----------- examples/box.c | 3 +- examples/box2.c | 8 +- examples/new_sptr.c | 16 ++-- examples/sptr.c | 4 +- examples/sptr_demo.c | 10 +-- examples/sptr_erase.c | 8 +- examples/sptr_music.c | 4 +- examples/sptr_pthread.c | 20 ++--- examples/sptr_to_maps.c | 8 +- include/stc/carc.h | 201 ++++++++++++++++++++++++++++++++++++++++++++++++ include/stc/ccommon.h | 2 +- include/stc/csptr.h | 201 ------------------------------------------------ include/stc/forward.h | 4 +- include/stc/template.h | 16 ++-- 17 files changed, 350 insertions(+), 334 deletions(-) create mode 100644 include/stc/carc.h delete mode 100644 include/stc/csptr.h diff --git a/README.md b/README.md index e8d9d34b..624796bb 100644 --- a/README.md +++ b/README.md @@ -5,19 +5,20 @@ STC - Smart Template Containers for C News ---- -### Version 3.0 released -There are new general `i_key_bind` / `i_val_bind` template parameters which auto-binds a set of functions -to the type specified, and can be used in place of `i_key` / `i_val`. Use the `_bind` variant for elements +### Version 3 released +A lot of enhancements, additions and bugfixes. There are also a number of breaking changes (mostly renamings), +see migrate guide below. + +Added are new general `i_key_bind`/`i_val_bind` template parameters which auto-binds a set of functions +to the type specified, and may be used in place of `i_key`/`i_val`. Use the `_bind` variant for elements of Type which have following functions defined: *Type_cmp*, *Type_clone*, *Type_drop*, *Type_hash*, and *Type_eq*. Only the functions required by the particular container needs to be defined. e.g. **cmap** -and **cset** are the only types that requires *Type_hash* and *Type_eq* to be defined. -You may override these by defining `i_cmp`, `i_drop`, etc. Template parameters with `i_val` / `i_key` may -still be defined as before, which is often easier for simple element types. +and **cset** are the only types that require *Type_hash* and *Type_eq* to be defined. -Migration guide from version 2 to 3. Replace (regular expresion) in VS Code: +### Migration guide from version 2 to 3. +Replace (regular expression) in VS Code: - `_del\b` → `_drop` - `_compare\b` → `_cmp` -- `csptr` → `cref` - `_rawvalue\b` → `_raw` - `_equ\b` → `_eq` @@ -26,24 +27,25 @@ Replace (whole word + match case): - `i_valdel` → `i_valdrop` - `i_cnt` → `i_type` - `cstr_lit` → `cstr_new` -- `csptr_X_make` → `csptr_X_new` +- `csptr_X_make` → `carc_X_from` - `i_key_csptr` → `i_key_sptr` - `i_val_csptr` → `i_val_sptr` - `c_apply` → `c_apply_OLD` // replaced by new `c_apply` - `c_apply_pair` → `c_apply_pair_OLD` // replaced by new `c_apply` -### Final version 2.1 +Non-regex, global match case replace: +- `csptr` → `carc` + +### Brief summary of changes - Strings: Renamed constructor *cstr_lit()* to `cstr_new(lit)`. Renamed *cstr_assign_fmt()* to `cstr_printf()`. -- Added [**cbox**](docs/cbox_api.md) type: container of one element, similar to [std::unique_ptr](https://en.cppreference.com/w/cpp/memory/unique_ptr) -- Added [example for **csptr**](examples/sptr_to_maps.c). +- Added [**cbox**](docs/cbox_api.md) type: smart pointer, similar to [std::unique_ptr](https://en.cppreference.com/w/cpp/memory/unique_ptr). Also renamed `csptr` to `carc` (atomic reference counted) smart pointer. +- Added [example for **carc**](examples/sptr_to_maps.c). - Added [**c_forpair**](docs/ccommon_api.md) macro: for-loop with "structured binding". -- Renamed: *csptr_X_make()* to *csptr_X_new()*. Corresponding **cbox** method is *cbox_X_new()*. +- Renamed: *csptr_X_make()* to *carc_X_from()*. Corresponding **cbox** method is *cbox_X_from()*. - Renamed: *c_default_fromraw(raw)* to *c_default_clone(raw)*. -- Renamed: `i_key_csptr` / `i_val_csptr`. Use `i_key_sptr` / `i_val_sptr` for **csptr** and **cbox**. +- Renamed: `i_key_csptr`/`i_val_csptr` to `i_key_sptr`/`i_val_sptr` for both **carc** and **cbox** values in containers. - Renamed: `i_cnt` to `i_type` for defining the complete container type name. - Added `i_opt` template parameter: compile-time options: `c_no_cmp`, `c_no_clone`, `c_no_atomic`, `c_is_fwd`; may be combined with `|` - -### Version 2.0. Two main breaking changes from V1.X. - Uses a different way to instantiate templated containers, which is incompatible with v1.X. Introduction @@ -67,7 +69,7 @@ which by the compiler is seen as different code because of macro name substituti - [***clist*** - **std::forward_list** alike type](docs/clist_api.md) - [***cmap*** - **std::unordered_map** alike type](docs/cmap_api.md) - [***cpque*** - **std::priority_queue** alike type](docs/cpque_api.md) -- [***csptr*** - **std::shared_ptr** alike support](docs/csptr_api.md) +- [***carc*** - **std::shared_ptr** alike support](docs/carc_api.md) - [***cqueue*** - **std::queue** alike type](docs/cqueue_api.md) - [***cset*** - **std::unordered_set** alike type](docs/cset_api.md) - [***csmap*** - **std::map** sorted map alike type](docs/csmap_api.md) @@ -87,7 +89,7 @@ Highlights - **User friendly** - Just include the headers and you are good. The API and functionality is very close to c++ STL, and is fully listed in the docs. - **Templates** - Use `#define i_{arg}` to specify container template arguments. There are templates for element-*type*, -*comparison*, -*destruction*, -*cloning*, -*conversion types*, and more. - **Unparalleled performance** - Some containers are much faster than the c++ STL containers, the rest are about equal in speed. -- **Fully memory managed** - All containers will destruct keys/values via destructor defined as macro parameters before including the container header. Also, shared pointers are supported and can be stored in containers, see ***csptr***. +- **Fully memory managed** - All containers will destruct keys/values via destructor defined as macro parameters before including the container header. Also, shared pointers are supported and can be stored in containers, see ***carc***. - **Fully type safe** - Because of templating, it avoids error-prone casting of container types and elements back and forth from the containers. - **Uniform, easy-to-learn API** - Methods to ***construct***, ***initialize***, ***iterate*** and ***destruct*** have uniform and intuitive usage across the various containers. - **Small footprint** - Small source code and generated executables. The executable from the example below with six different containers is *22 kb in size* compiled with gcc -Os on linux. @@ -114,19 +116,30 @@ The usage of the containers is similar to the c++ standard containers in STL, so are familiar with them. All containers are generic/templated, except for **cstr** and **cbits**. No casting is used, so containers are type-safe like templates in c++. A basic usage example: ```c -#define i_val float -#include +#define i_type FVec // if not defined, vector type would be cvec_float +#define i_val float // element type +#include // defines the FVec type int main(void) { - cvec_float vec = cvec_float_init(); - cvec_float_push_back(&vec, 10.f); - cvec_float_push_back(&vec, 20.f); - cvec_float_push_back(&vec, 30.f); - - c_foreach (i, cvec_float, vec) - printf(" %g", *i.ref); + FVec vec = FVec_init(); + FVec_push_back(&vec, 10.f); + FVec_push_back(&vec, 20.f); + FVec_push_back(&vec, 30.f); + + for (size_t i = 0; i < FVec_size(vec); ++i) + printf(" %g", vec.data[i]); + FVec_drop(&vec); // free memory +} +``` +However, a "better" way to write the same is: +```c +int main(void) { + c_auto (FVec, vec) { // RAII + c_apply(v, FVec_push_back(&vec, v), float, {10.f, 20.f, 30.f}); - cvec_float_drop(&vec); + c_foreach (i, FVec, vec) // generic iteration and element access + printf(" %g", *i.ref); + } } ``` In order to include two **cvec**s with different element types, include cvec.h twice. For struct, a `i_cmp` @@ -195,7 +208,7 @@ int main(void) { { // add some elements to each container c_apply(v, cset_int_insert(&set, v), int, {10, 20, 30}); - c_apply(v, cvec_pnt_push_back(&vec, v), int, { {10, 1}, {20, 2}, {30, 3} }); + c_apply(v, cvec_pnt_push_back(&vec, v), cvec_pnt_raw, { {10, 1}, {20, 2}, {30, 3} }); c_apply(v, cdeq_int_push_back(&deq, v), int, {10, 20, 30}); c_apply(v, clist_int_push_back(&lst, v), int, {10, 20, 30}); c_apply(v, cstack_int_push(&stk, v), int, {10, 20, 30}); @@ -440,4 +453,4 @@ Memory efficiency - **cmap**: Type size: 4 pointers. *cmap* uses one table of keys+value, and one table of precomputed hash-value/used bucket, which occupies only one byte per bucket. The closed hashing has a default max load factor of 85%, and hash table scales by 1.6x when reaching that. - **csmap**: Type size: 1 pointer. *csmap* manages its own array of tree-nodes for allocation efficiency. Each node uses only two 32-bit ints for child nodes, and one byte for `level`. - **carr2**, **carr3**: Type size: 1 pointer plus dimension variables. Arrays are allocated as one contiguous block of heap memory, and one allocation for pointers of indices to the array. -- **csptr**: Type size: 2 pointers, one for the data and one for the reference counter. +- **carc**: Type size: 2 pointers, one for the data and one for the reference counter. diff --git a/docs/cbox_api.md b/docs/cbox_api.md index 011beea2..e05e69f0 100644 --- a/docs/cbox_api.md +++ b/docs/cbox_api.md @@ -1,4 +1,4 @@ -# STC [cbox](../include/stc/cbox.h): (Boxed) Heap Allocated Objects +# STC [cbox](../include/stc/cbox.h): Smart Pointer (Boxed object) **cbox** is a A box is a smart pointer to a heap allocated value of type X. A **cbox** can be empty. The *cbox_X_cmp()*, *cbox_X_drop()* methods are defined based on the `i_cmp` @@ -21,7 +21,7 @@ See similar c++ class [std::unique_ptr](https://en.cppreference.com/w/cpp/memory #define i_val // value: REQUIRED #define i_cmp // three-way compare two i_val* : REQUIRED IF i_val is a non-integral type #define i_drop // destroy value func - defaults to empty destruct -#define i_from // create from raw/clone func - REQUIRED if i_drop is defined, +#define i_from // create from raw/clone func - REQUIRED if i_drop is defined, // unless 'i_opt c_no_clone' is defined. #define i_tag // type name tag, defaults to i_val #include @@ -76,7 +76,7 @@ void int_drop(int* x) { // both for the cbox type and the container of cbox elements. It will also // disable emplace container functions. // -// This applies to all container types, except those with csptr elements, as they +// This applies to all container types, except those with carc elements, as they // define cloning internally. #define i_val int @@ -84,11 +84,11 @@ void int_drop(int* x) { #define i_from c_default_clone #include // cbox_int -#define i_key_bind cbox_int // note: use i_key_bind instead of i_key +#define i_key_sptr cbox_int // note: use i_key_sptr instead of i_key #define i_tag int // tag otherwise defaults to 'ref' #include // csset_int (like: std::set>) -#define i_val_bind cbox_int // note: use i_val_bind instead of i_val +#define i_val_sptr cbox_int // note: use i_val_sptr instead of i_val #define i_tag int // tag otherwise defaults to 'ref' #include // cvec_int (like: std::vector>) diff --git a/docs/csptr_api.md b/docs/csptr_api.md index 95575417..e33bf410 100644 --- a/docs/csptr_api.md +++ b/docs/csptr_api.md @@ -1,23 +1,24 @@ -# STC [csptr](../include/stc/csptr.h): Atomic Reference Counted +# STC [carc](../include/stc/carc.h): Atomic Reference Counted Smart Pointer -**csptr** is a smart pointer that retains shared ownership of an object through a pointer. -Several **csptr** objects may own the same object. The object is destroyed and its memory -deallocated when the last remaining **csptr** owning the object is destroyed with *csptr_X_drop()*; +**carc** is a smart pointer that retains shared ownership of an object through a pointer. +Several **carc** objects may own the same object. The object is destroyed and its memory +deallocated when the last remaining **carc** owning the object is destroyed with *carc_X_drop()*; -The object is destroyed using *csptr_X_drop()*. A **csptr** may also own no objects, in which -case it is called empty. The *csptr_X_cmp()*, *csptr_X_drop()* methods are defined based on -the `i_cmp` and `i_valdrop` macros specified. Use *csptr_X_clone(p)* when sharing ownership of +The object is destroyed using *carc_X_drop()*. A **carc** may also own no objects, in which +case it is called empty. The *carc_X_cmp()*, *carc_X_drop()* methods are defined based on +the `i_cmp` and `i_valdrop` macros specified. Use *carc_X_clone(p)* when sharing ownership of the pointed-to object. -All **csptr** functions can be called by multiple threads on different instances of **csptr** without +All **carc** functions can be called by multiple threads on different instances of **carc** without additional synchronization even if these instances are copies and share ownership of the same object. -**csptr** uses thread-safe atomic reference counting, through the *csptr_X_clone()* and *csptr_X_drop()* methods. +**carc** uses thread-safe atomic reference counting, through the *carc_X_clone()* and *carc_X_drop()* methods. -When declaring a container with shared pointers, define `i_val_bind` as the csptr type, see example. +When declaring a container with shared pointers, define `i_val_bind` as the carc type, see example. -For containers, make sure to pass the result of create functions *csptr_X_new()* **only** to *insert()*, -*push_back()*, and *push()* functions. Use *emplace()* method for sharing existing **csptr**s between -containers or other existing shared pointers, as they internally clone/share the input. +Make sure to pass the result of create functions like *carc_X_from()* **only** to *insert()*, +*push_back()*, and *push()* functions, as it is *moved* into the container. Use *emplace()* +method for sharing existing **carc**s between containers or other existing shared pointers, +as they clone/share the input internally. See similar c++ class [std::shared_ptr](https://en.cppreference.com/w/cpp/memory/shared_ptr) for a functional reference, or Rust [std::sync::Arc](https://doc.rust-lang.org/std/sync/struct.Arc.html) / [std::rc::Rc](https://doc.rust-lang.org/std/rc/struct.Rc.html). @@ -29,41 +30,42 @@ See similar c++ class [std::shared_ptr](https://en.cppreference.com/w/cpp/memory #define i_drop // destroy value func - defaults to empty destruct #define i_tag // defaults to i_val #define i_opt c_no_atomic // Non-atomic reference counting, like Rust Rc. -#include +#include ``` `X` should be replaced by the value of `i_tag` in all of the following documentation. ## Methods ```c -csptr_X csptr_X_init(); // empty shared pointer -csptr_X csptr_X_new(i_valraw raw); // like csptr_X_from(), but construct owned value from raw. -csptr_X csptr_X_from(i_val val); // create new heap allocated object. Take ownership of val. -csptr_X csptr_X_from_ptr(i_val* p); // create a csptr from raw pointer. Takes ownership of p. - -csptr_X csptr_X_clone(csptr_X other); // return other with increased use count -csptr_X csptr_X_move(csptr_X* self); // transfer ownership to another csptr. -void csptr_X_take(csptr_X* self, csptr_X other); // take ownership of other. -void csptr_X_copy(csptr_X* self, csptr_X other); // copy shared (increase use count) - -void csptr_X_drop(csptr_X* self); // destruct (decrease use count, free at 0) -long csptr_X_use_count(csptr_X ptr); - -void csptr_X_reset(csptr_X* self); -void csptr_X_reset_from(csptr_X* self, i_val val); // assign new csptr with value. Takes ownership of val. - -uint64_t csptr_X_value_hash(const i_val* x, size_t n); // hash value -int csptr_X_value_cmp(const i_val* x, const i_val* y); // compares pointer addresses if 'i_opt c_no_cmp' - // is defined. Otherwise uses 'i_cmp' or default compare. -bool csptr_X_value_eq(const i_val* x, const i_val* y); // cbox_X_value_cmp == 0 +carc_X carc_X_init(); // empty shared pointer +carc_X carc_X_new(i_valraw raw); // like carc_X_from(), but construct owned value from raw. +carc_X carc_X_from(i_val val); // create new heap allocated object. Take ownership of val. +carc_X carc_X_from_ptr(i_val* p); // create a carc from raw pointer. Takes ownership of p. + +carc_X carc_X_clone(carc_X other); // return other with increased use count +carc_X carc_X_move(carc_X* self); // transfer ownership to another carc. +void carc_X_take(carc_X* self, carc_X other); // take ownership of other. +void carc_X_copy(carc_X* self, carc_X other); // copy shared (increase use count) + +void carc_X_drop(carc_X* self); // destruct (decrease use count, free at 0) +long carc_X_use_count(carc_X ptr); + +void carc_X_reset(carc_X* self); +void carc_X_reset_from(carc_X* self, i_val val); // assign new carc with value. Takes ownership of val. + +uint64_t carc_X_value_hash(const i_val* x, size_t n); // hash value +int carc_X_value_cmp(const i_val* x, const i_val* y); // compares pointer addresses if 'i_opt c_no_cmp' + // is defined. Otherwise uses 'i_cmp' or default compare. +bool carc_X_value_eq(const i_val* x, const i_val* y); // cbox_X_value_cmp == 0 ``` ## Types and constants -| Type name | Type definition | Used to represent... | -|:-------------------|:---------------------------------------------------|:------------------------| -| `csptr_null` | `{NULL, NULL}` | Init nullptr const | -| `csptr_X` | `struct { csptr_X_value* get; long* use_count; }` | The csptr type | -| `csptr_X_value` | `i_val` | The csptr element type | +| Type name | Type definition | Used to represent... | +|:------------------|:--------------------------------------------------|:-----------------------| +| `carc_null` | `{NULL, NULL}` | Init nullptr const | +| `carc_X` | `struct { carc_X_value* get; long* use_count; }` | The carc type | +| `carc_X_value` | `i_val` | The carc element type | +| `carc_X_raw` | `i_valraw` | Convertion type | ## Example @@ -83,14 +85,14 @@ bool csptr_X_value_eq(const i_val* x, const i_val* y); // cbox_X_value // no comparison of Maps needed (or available), and // no need for atomic ref. count in single thread: #define i_opt c_no_cmp|c_no_atomic -#include +#include #define i_type Stack -#define i_val_bind Arc // define i_val_bind for csptr / cbox value, not i_val +#define i_val_sptr Arc // define i_val_sptr for carc/cbox value, not i_val or i_val_bind #include #define i_type List -#define i_val_bind Arc // as above +#define i_val_sptr Arc // as above #include int main() @@ -100,28 +102,28 @@ int main() { // POPULATE the stack with shared pointers to Map: Map *map; - map = Stack_push(&stack, Arc_new(Map_init()))->get; + map = Stack_push(&stack, Arc_from(Map_init()))->get; c_apply(v, Map_emplace(map, c_pair(v)), Map_raw, { {"Joey", 1990}, {"Mary", 1995}, {"Joanna", 1992} }); - map = Stack_push(&stack, Arc_new(Map_init()))->get; + map = Stack_push(&stack, Arc_from(Map_init()))->get; c_apply(v, Map_emplace(map, c_pair(v)), Map_raw, { {"Rosanna", 2001}, {"Brad", 1999}, {"Jack", 1980} }); // POPULATE the list: - map = List_push_back(&list, Arc_new(Map_init()))->get; + map = List_push_back(&list, Arc_from(Map_init()))->get; c_apply(v, Map_emplace(map, c_pair(v)), Map_raw, { {"Steve", 1979}, {"Rick", 1974}, {"Tracy", 2003} }); - // Share two Maps from the stack with the list using emplace (clones the csptr): + // Share two Maps from the stack with the list using emplace (clones the carc): List_emplace_back(&list, stack.data[0]); List_emplace_back(&list, stack.data[1]); // Clone (deep copy) a Map from the stack to the list // List will contain two shared and two unshared maps. - map = List_push_back(&list, Arc_new(Map_clone(*stack.data[1].get)))->get; + map = List_push_back(&list, Arc_from(Map_clone(*stack.data[1].get)))->get; // Add one more element to the cloned map: Map_emplace_or_assign(map, "CLONED", 2021); diff --git a/examples/box.c b/examples/box.c index ae10a89f..d97545b6 100644 --- a/examples/box.c +++ b/examples/box.c @@ -28,10 +28,9 @@ void Person_drop(Person* p) { #include #define i_type Persons -#define i_val_ref PBox // binds PBox_cmp, ... +#define i_val_sptr PBox // informs that PBox is a smart pointer. #include - int main() { c_auto (Persons, vec) diff --git a/examples/box2.c b/examples/box2.c index 5549dade..d79ea32d 100644 --- a/examples/box2.c +++ b/examples/box2.c @@ -26,8 +26,8 @@ struct { #include // cbox_Rectangle // Box in box: -#define i_val_bind cbox_Point // NB: adviced to use i_val_arc when value is a cbox or csptr! - // it will auto-set i_drop, i_from, i_cmp for you. +#define i_val_sptr cbox_Point // NB: adviced to use i_val_sptr when value is a cbox or carc! + // it will auto-set i_drop, i_from, i_cmp for you. #define i_opt c_no_cmp #define i_tag BoxPoint #include // cbox_BoxPoint @@ -62,10 +62,10 @@ int main(void) { }); // The output of functions can be boxed - boxed_point = cbox_Point_new(origin()); + boxed_point = cbox_Point_from(origin()); // Double indirection - box_in_a_box = cbox_BoxPoint_new(boxed_origin()); + box_in_a_box = cbox_BoxPoint_from(boxed_origin()); printf("Point occupies %zu bytes on the stack\n", sizeof(point)); diff --git a/examples/new_sptr.c b/examples/new_sptr.c index ce31478e..045eef77 100644 --- a/examples/new_sptr.c +++ b/examples/new_sptr.c @@ -14,29 +14,29 @@ void Person_drop(Person* p) { #define i_drop Person_drop #define i_opt c_no_cmp #define i_tag person -#include +#include // ... #define i_val int #define i_drop(x) printf("drop: %d\n", *(x)) -#include +#include -#define i_val_bind csptr_int +#define i_val_bind carc_int #define i_tag iptr #include int main(void) { - c_autovar (csptr_person p = csptr_person_from(Person_new("John", "Smiths")), csptr_person_drop(&p)) - c_autovar (csptr_person q = csptr_person_clone(p), csptr_person_drop(&q)) // share the pointer + c_autovar (carc_person p = carc_person_from(Person_new("John", "Smiths")), carc_person_drop(&p)) + c_autovar (carc_person q = carc_person_clone(p), carc_person_drop(&q)) // share the pointer { printf("%s %s. uses: %lu\n", q.get->name.str, q.get->last.str, *q.use_count); } c_auto (cstack_iptr, stk) { - cstack_iptr_push(&stk, csptr_int_from(10)); - cstack_iptr_push(&stk, csptr_int_from(20)); - cstack_iptr_push(&stk, csptr_int_from(30)); + cstack_iptr_push(&stk, carc_int_from(10)); + cstack_iptr_push(&stk, carc_int_from(20)); + cstack_iptr_push(&stk, carc_int_from(30)); cstack_iptr_emplace(&stk, *cstack_iptr_top(&stk)); cstack_iptr_emplace(&stk, *cstack_iptr_begin(&stk).ref); diff --git a/examples/sptr.c b/examples/sptr.c index e1d734e0..0c22bd68 100644 --- a/examples/sptr.c +++ b/examples/sptr.c @@ -25,10 +25,10 @@ void Person_drop(Person* p) { #define i_type PSPtr #define i_val_bind Person // binds Person_cmp, ... -#include +#include #define i_type Persons -#define i_val_ref PSPtr // binds PSPtr_cmp, ... +#define i_val_sptr PSPtr // binds PSPtr_cmp, ... #include diff --git a/examples/sptr_demo.c b/examples/sptr_demo.c index a5c642b0..8626e2d4 100644 --- a/examples/sptr_demo.c +++ b/examples/sptr_demo.c @@ -5,18 +5,18 @@ void int_drop(int* x) { printf("drop: %d\n", *x); } -// csptr implements its own clone method using reference counting, +// carc implements its own clone method using reference counting, // so 'i_valfrom' need not be defined (will be ignored). -#define i_type iref // set type name to be defined (instead of 'csptr_int') +#define i_type iref // set type name to be defined (instead of 'carc_int') #define i_val int #define i_drop int_drop // optional, just to display the elements destroyed -#include // iref +#include // iref -#define i_key_ref iref // note: use i_key_bind instead of i_key for csptr/cbox elements +#define i_key_sptr iref // note: use i_key_bind instead of i_key for carc/cbox elements #include // csset_iref (like: std::set>) -#define i_val_ref iref // note: as above. +#define i_val_sptr iref // note: as above. #include // cvec_iref (like: std::vector>) int main() diff --git a/examples/sptr_erase.c b/examples/sptr_erase.c index fbb141b4..d40d8fe7 100644 --- a/examples/sptr_erase.c +++ b/examples/sptr_erase.c @@ -5,14 +5,14 @@ void show_drop(int* x) { printf("drop: %d\n", *x); } #define i_type Arc #define i_val int #define i_drop show_drop -// csptr/cbox will use pointer address comparison of i_val +// carc/cbox will use pointer address comparison of i_val // if 'i_opt c_no_cmp' is defined, otherwise i_cmp is used -// to compare object values. See the twodifferences by +// to compare object values. See the two differences by // commenting out the next line. -#include // Shared pointer to int +#include // Shared pointer to int #define i_type Vec -#define i_val_ref Arc +#define i_val_sptr Arc #include // Vec: cvec diff --git a/examples/sptr_music.c b/examples/sptr_music.c index 1d58cc46..42bb3455 100644 --- a/examples/sptr_music.c +++ b/examples/sptr_music.c @@ -21,10 +21,10 @@ void Song_drop(Song* s) { #define i_val Song #define i_drop Song_drop #define i_opt c_no_cmp -#include +#include #define i_type SongVec -#define i_val_ref SongPtr +#define i_val_sptr SongPtr #include void example3() diff --git a/examples/sptr_pthread.c b/examples/sptr_pthread.c index d71ce97d..d81d9810 100644 --- a/examples/sptr_pthread.c +++ b/examples/sptr_pthread.c @@ -11,13 +11,13 @@ struct Base int value; } typedef Base; +#define i_type BaseRc #define i_val Base -#define i_drop(x) printf("Base::~Base()\n") -#define i_tag base +#define i_valdrop(x) printf("Drop Base: %d\n", (x)->value) #define i_opt c_no_cmp -#include +#include -void* thr(csptr_base* lp) +void* thr(BaseRc* lp) { sleep(1); static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; @@ -25,30 +25,32 @@ void* thr(csptr_base* lp) { printf("local pointer in a thread:\n" " p.get() = %p, p.use_count() = %ld\n", (void*)lp->get, *lp->use_count); + /* safe to modify base here */ + lp->get->value += 1; } /* atomically decrease ref. */ - csptr_base_drop(lp); + BaseRc_drop(lp); return NULL; } int main() { - csptr_base p = csptr_base_from((Base){42}); + BaseRc p = BaseRc_from((Base){0}); printf("Created a Base\n" " p.get() = %p, p.use_count() = %ld\n", (void*)p.get, *p.use_count); enum {N = 3}; pthread_t t[N]; - csptr_base c[N]; + BaseRc c[N]; c_forrange (i, N) { - c[i] = csptr_base_clone(p); + c[i] = BaseRc_clone(p); pthread_create(&t[i], NULL, (void*(*)(void*))thr, &c[i]); } printf("Shared ownership between %d threads and released\n" "ownership from main:\n" " p.get() = %p, p.use_count() = %ld\n", N, (void*)p.get, *p.use_count); - csptr_base_reset(&p); + BaseRc_reset(&p); c_forrange (i, N) pthread_join(t[i], NULL); printf("All threads completed, the last one deleted Base\n"); diff --git a/examples/sptr_to_maps.c b/examples/sptr_to_maps.c index 64866ba3..0873d297 100644 --- a/examples/sptr_to_maps.c +++ b/examples/sptr_to_maps.c @@ -12,14 +12,14 @@ // no need for atomic ref. count in single thread: // no compare function available for csmap: #define i_opt c_no_atomic|c_no_cmp -#include +#include #define i_type Stack -#define i_val_ref Arc // define i_val_bind for csptr/cbox value (not i_val) +#define i_val_sptr Arc // define i_val_bind for carc/cbox value (not i_val) #include #define i_type List -#define i_val_ref Arc // as above +#define i_val_sptr Arc // as above #include int main() @@ -44,7 +44,7 @@ int main() {"Steve", 1979}, {"Rick", 1974}, {"Tracy", 2003} }); - // Share two Maps from the stack with the list using emplace (clone the csptr): + // Share two Maps from the stack with the list using emplace (clone the carc): List_push_back(&list, Arc_clone(stack.data[0])); List_push_back(&list, Arc_clone(stack.data[1])); diff --git a/include/stc/carc.h b/include/stc/carc.h new file mode 100644 index 00000000..505b47d9 --- /dev/null +++ b/include/stc/carc.h @@ -0,0 +1,201 @@ +/* MIT License + * + * Copyright (c) 2021 Tyge Løvset, NORCE, www.norceresearch.no + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* carc: atomic reference counted shared_ptr +#include + +typedef struct { cstr name, last; } Person; + +Person Person_new(const char* name, const char* last) { + return (Person){.name = cstr_from(name), .last = cstr_from(last)}; +} +void Person_drop(Person* p) { + printf("drop: %s %s\n", p->name.str, p->last.str); + c_drop(cstr, &p->name, &p->last); +} + +#define i_tag person +#define i_val Person +#define i_valdrop Person_drop +#include + +int main() { + carc_person p = carc_person_from(Person_new("John", "Smiths")); + carc_person q = carc_person_clone(p); // share the pointer + + printf("%s %s. uses: %zu\n", q.get->name.str, q.get->last.str, *q.use_count); + c_drop(carc_person, &p, &q); +} +*/ + +#ifndef CARC_H_INCLUDED +#define CARC_H_INCLUDED +#include "ccommon.h" +#include "forward.h" +#include + +#if defined(__GNUC__) || defined(__clang__) + #define c_atomic_inc(v) (void)__atomic_add_fetch(v, 1, __ATOMIC_SEQ_CST) + #define c_atomic_dec_and_test(v) !__atomic_sub_fetch(v, 1, __ATOMIC_SEQ_CST) +#elif defined(_MSC_VER) + #include + #define c_atomic_inc(v) (void)_InterlockedIncrement(v) + #define c_atomic_dec_and_test(v) !_InterlockedDecrement(v) +#else + #include + #define c_atomic_inc(v) (void)atomic_fetch_add(v, 1) + #define c_atomic_dec_and_test(v) (atomic_fetch_sub(v, 1) == 1) +#endif + +#define carc_null {NULL, NULL} +#define _cx_carc_rep struct _cx_memb(_rep_) +#endif // CARC_H_INCLUDED + +#ifndef _i_prefix +#define _i_prefix carc_ +#endif +#define _i_has_internal_clone +#include "template.h" +typedef i_valraw _cx_raw; + +#if !c_option(c_no_atomic) + #define _i_atomic_inc(v) c_atomic_inc(v) + #define _i_atomic_dec_and_test(v) c_atomic_dec_and_test(v) +#else + #define _i_atomic_inc(v) (void)(++*(v)) + #define _i_atomic_dec_and_test(v) !(--*(v)) +#endif +#if !c_option(c_is_fwd) +_cx_deftypes(_c_carc_types, _cx_self, i_val); +#endif +_cx_carc_rep { long counter; i_val value; }; + +STC_INLINE _cx_self +_cx_memb(_init)(void) { return c_make(_cx_self){NULL, NULL}; } + +STC_INLINE long +_cx_memb(_use_count)(_cx_self ptr) { return ptr.use_count ? *ptr.use_count : 0; } + +STC_INLINE _cx_self +_cx_memb(_from_ptr)(_cx_value* p) { + _cx_self ptr = {p}; + if (p) *(ptr.use_count = c_alloc(long)) = 1; + return ptr; +} + +STC_INLINE _cx_self +_cx_memb(_from)(i_val val) { + _cx_self ptr; _cx_carc_rep *rep = c_alloc(_cx_carc_rep); + *(ptr.use_count = &rep->counter) = 1; + *(ptr.get = &rep->value) = val; + return ptr; +} + +STC_INLINE i_val _cx_memb(_toraw)(const _cx_self* self) { + return *self->get; +} + +STC_INLINE _cx_self +_cx_memb(_move)(_cx_self* self) { + _cx_self ptr = *self; + self->get = NULL, self->use_count = NULL; + return ptr; +} + +STC_INLINE void +_cx_memb(_drop)(_cx_self* self) { + if (self->use_count && _i_atomic_dec_and_test(self->use_count)) { + i_valdrop(self->get); + if (self->get != &((_cx_carc_rep *)self->use_count)->value) + c_free(self->get); + c_free(self->use_count); + } +} + +STC_INLINE void +_cx_memb(_reset)(_cx_self* self) { + _cx_memb(_drop)(self); + self->use_count = NULL, self->get = NULL; +} + +STC_INLINE void +_cx_memb(_reset_from)(_cx_self* self, i_val val) { + _cx_memb(_drop)(self); + *self = _cx_memb(_from)(val); +} + +#if !c_option(c_no_clone) && !defined _i_valraw_default + STC_INLINE _cx_self _cx_memb(_new)(i_valraw raw) { + return _cx_memb(_from)(i_valfrom(raw)); + } +#endif + +// does not use i_valfrom, so we can bypass c_no_clone +STC_INLINE _cx_self +_cx_memb(_clone)(_cx_self ptr) { + if (ptr.use_count) _i_atomic_inc(ptr.use_count); + return ptr; +} + +STC_INLINE void +_cx_memb(_copy)(_cx_self* self, _cx_self ptr) { + if (ptr.use_count) _i_atomic_inc(ptr.use_count); + _cx_memb(_drop)(self); + *self = ptr; +} + +STC_INLINE void +_cx_memb(_take)(_cx_self* self, _cx_self ptr) { + if (self->get != ptr.get) _cx_memb(_drop)(self); + *self = ptr; +} + +STC_INLINE uint64_t +_cx_memb(_value_hash)(const _cx_value* x, size_t n) { + #if c_option(c_no_cmp) && UINTPTR_MAX == UINT64_MAX + return c_hash64(&x, 8); + #elif c_option(c_no_cmp) + return c_hash32(&x, 4); + #else + i_valraw rx = i_valto(x); + return i_hash(&rx, sizeof rx); + #endif +} + +STC_INLINE int +_cx_memb(_value_cmp)(const _cx_value* x, const _cx_value* y) { + #if c_option(c_no_cmp) + return c_default_cmp(&x, &y); + #else + i_valraw rx = i_valto(x), ry = i_valto(x); + return i_cmp(&rx, &ry); + #endif +} + +STC_INLINE bool +_cx_memb(_value_eq)(const _cx_value* x, const _cx_value* y) { + return !_cx_memb(_value_cmp)(x, y); +} +#undef _i_atomic_inc +#undef _i_atomic_dec_and_test +#include "template.h" diff --git a/include/stc/ccommon.h b/include/stc/ccommon.h index ac6e9d31..4e2ee97d 100644 --- a/include/stc/ccommon.h +++ b/include/stc/ccommon.h @@ -199,7 +199,7 @@ STC_INLINE uint64_t c_default_hash(const void* key, size_t len) { for (size_t index = 0; index < _n; ++index) \ { const T v = _c_arr[index]; method; } \ } while (0) -#define c_apply_it(v, method, C, ...) do { \ +#define c_apply_cnt(v, method, C, ...) do { \ size_t index = 0; \ c_foreach (_it, C, __VA_ARGS__) \ { const C##_value v = *_it.ref; method; ++index; } \ diff --git a/include/stc/csptr.h b/include/stc/csptr.h deleted file mode 100644 index 0718a4cc..00000000 --- a/include/stc/csptr.h +++ /dev/null @@ -1,201 +0,0 @@ -/* MIT License - * - * Copyright (c) 2021 Tyge Løvset, NORCE, www.norceresearch.no - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* csptr: shared_ptr type -#include - -typedef struct { cstr name, last; } Person; - -Person Person_new(const char* name, const char* last) { - return (Person){.name = cstr_from(name), .last = cstr_from(last)}; -} -void Person_drop(Person* p) { - printf("drop: %s %s\n", p->name.str, p->last.str); - c_drop(cstr, &p->name, &p->last); -} - -#define i_tag person -#define i_val Person -#define i_valdrop Person_drop -#include - -int main() { - csptr_person p = csptr_person_new(Person_new("John", "Smiths")); - csptr_person q = csptr_person_clone(p); // share the pointer - - printf("%s %s. uses: %zu\n", q.get->name.str, q.get->last.str, *q.use_count); - c_drop(csptr_person, &p, &q); -} -*/ - -#ifndef CSPTR_H_INCLUDED -#define CSPTR_H_INCLUDED -#include "ccommon.h" -#include "forward.h" -#include - -#if defined(__GNUC__) || defined(__clang__) - #define c_atomic_inc(v) (void)__atomic_add_fetch(v, 1, __ATOMIC_SEQ_CST) - #define c_atomic_dec_and_test(v) !__atomic_sub_fetch(v, 1, __ATOMIC_SEQ_CST) -#elif defined(_MSC_VER) - #include - #define c_atomic_inc(v) (void)_InterlockedIncrement(v) - #define c_atomic_dec_and_test(v) !_InterlockedDecrement(v) -#else - #include - #define c_atomic_inc(v) (void)atomic_fetch_add(v, 1) - #define c_atomic_dec_and_test(v) (atomic_fetch_sub(v, 1) == 1) -#endif - -#define csptr_null {NULL, NULL} -#define _cx_csptr_rep struct _cx_memb(_rep_) -#endif // CSPTR_H_INCLUDED - -#ifndef _i_prefix -#define _i_prefix csptr_ -#endif -#define _i_has_internal_clone -#include "template.h" -typedef i_valraw _cx_raw; - -#if !c_option(c_no_atomic) - #define _i_atomic_inc(v) c_atomic_inc(v) - #define _i_atomic_dec_and_test(v) c_atomic_dec_and_test(v) -#else - #define _i_atomic_inc(v) (void)(++*(v)) - #define _i_atomic_dec_and_test(v) !(--*(v)) -#endif -#if !c_option(c_is_fwd) -_cx_deftypes(_c_csptr_types, _cx_self, i_val); -#endif -_cx_csptr_rep { long counter; i_val value; }; - -STC_INLINE _cx_self -_cx_memb(_init)(void) { return c_make(_cx_self){NULL, NULL}; } - -STC_INLINE long -_cx_memb(_use_count)(_cx_self ptr) { return ptr.use_count ? *ptr.use_count : 0; } - -STC_INLINE _cx_self -_cx_memb(_from_ptr)(_cx_value* p) { - _cx_self ptr = {p}; - if (p) *(ptr.use_count = c_alloc(long)) = 1; - return ptr; -} - -STC_INLINE _cx_self -_cx_memb(_from)(i_val val) { - _cx_self ptr; _cx_csptr_rep *rep = c_alloc(_cx_csptr_rep); - *(ptr.use_count = &rep->counter) = 1; - *(ptr.get = &rep->value) = val; - return ptr; -} - -STC_INLINE i_val _cx_memb(_toraw)(const _cx_self* self) { - return *self->get; -} - -STC_INLINE _cx_self -_cx_memb(_move)(_cx_self* self) { - _cx_self ptr = *self; - self->get = NULL, self->use_count = NULL; - return ptr; -} - -STC_INLINE void -_cx_memb(_drop)(_cx_self* self) { - if (self->use_count && _i_atomic_dec_and_test(self->use_count)) { - i_valdrop(self->get); - if (self->get != &((_cx_csptr_rep *)self->use_count)->value) - c_free(self->get); - c_free(self->use_count); - } -} - -STC_INLINE void -_cx_memb(_reset)(_cx_self* self) { - _cx_memb(_drop)(self); - self->use_count = NULL, self->get = NULL; -} - -STC_INLINE void -_cx_memb(_reset_from)(_cx_self* self, i_val val) { - _cx_memb(_drop)(self); - *self = _cx_memb(_from)(val); -} - -#if !c_option(c_no_clone) && !defined _i_valraw_default - STC_INLINE _cx_self _cx_memb(_new)(i_valraw raw) { - return _cx_memb(_from)(i_valfrom(raw)); - } -#endif - -// does not use i_valfrom, so we can bypass c_no_clone -STC_INLINE _cx_self -_cx_memb(_clone)(_cx_self ptr) { - if (ptr.use_count) _i_atomic_inc(ptr.use_count); - return ptr; -} - -STC_INLINE void -_cx_memb(_copy)(_cx_self* self, _cx_self ptr) { - if (ptr.use_count) _i_atomic_inc(ptr.use_count); - _cx_memb(_drop)(self); - *self = ptr; -} - -STC_INLINE void -_cx_memb(_take)(_cx_self* self, _cx_self ptr) { - if (self->get != ptr.get) _cx_memb(_drop)(self); - *self = ptr; -} - -STC_INLINE uint64_t -_cx_memb(_value_hash)(const _cx_value* x, size_t n) { - #if c_option(c_no_cmp) && UINTPTR_MAX == UINT64_MAX - return c_hash64(&x, 8); - #elif c_option(c_no_cmp) - return c_hash32(&x, 4); - #else - i_valraw rx = i_valto(x); - return i_hash(&rx, sizeof rx); - #endif -} - -STC_INLINE int -_cx_memb(_value_cmp)(const _cx_value* x, const _cx_value* y) { - #if c_option(c_no_cmp) - return c_default_cmp(&x, &y); - #else - i_valraw rx = i_valto(x), ry = i_valto(x); - return i_cmp(&rx, &ry); - #endif -} - -STC_INLINE bool -_cx_memb(_value_eq)(const _cx_value* x, const _cx_value* y) { - return !_cx_memb(_value_cmp)(x, y); -} -#undef _i_atomic_inc -#undef _i_atomic_dec_and_test -#include "template.h" diff --git a/include/stc/forward.h b/include/stc/forward.h index bf2ba698..9a37297e 100644 --- a/include/stc/forward.h +++ b/include/stc/forward.h @@ -34,7 +34,7 @@ #define forward_cset(CX, KEY) _c_chash_types(CX, cset, KEY, KEY, c_false, c_true) #define forward_csset(CX, KEY) _c_aatree_types(CX, KEY, KEY, c_false, c_true) #define forward_cbox(CX, VAL) _c_cbox_types(CX, VAL) -#define forward_csptr(CX, VAL) _c_csptr_types(CX, VAL) +#define forward_carc(CX, VAL) _c_carc_types(CX, VAL) #define forward_cpque(CX, VAL) _c_cpque_types(CX, VAL) #define forward_cstack(CX, VAL) _c_cstack_types(CX, VAL) #define forward_cqueue(CX, VAL) _c_cdeq_types(CX, VAL) @@ -132,7 +132,7 @@ SELF##_value* get; \ } SELF -#define _c_csptr_types(SELF, VAL) \ +#define _c_carc_types(SELF, VAL) \ typedef VAL SELF##_value; \ \ typedef struct { \ diff --git a/include/stc/template.h b/include/stc/template.h index 1d1551fa..5efe3772 100644 --- a/include/stc/template.h +++ b/include/stc/template.h @@ -61,9 +61,9 @@ #ifndef i_tag #define i_tag str #endif -#elif defined i_key_ref - #define i_key_bind i_key_ref - #define i_keyraw c_PASTE(i_key_ref, _value) +#elif defined i_key_sptr + #define i_key_bind i_key_sptr + #define i_keyraw c_PASTE(i_key_sptr, _value) #endif #ifdef i_key_bind @@ -120,9 +120,9 @@ #if !defined i_tag && !defined i_key #define i_tag str #endif -#elif defined i_val_ref - #define i_val_bind i_val_ref - #define i_valraw c_PASTE(i_val_ref, _value) +#elif defined i_val_sptr + #define i_val_bind i_val_sptr + #define i_valraw c_PASTE(i_val_sptr, _value) #endif #ifdef i_val_bind @@ -219,7 +219,7 @@ #undef i_val #undef i_val_str -#undef i_val_ref +#undef i_val_sptr #undef i_val_bind #undef i_valraw #undef i_valfrom @@ -228,7 +228,7 @@ #undef i_key #undef i_key_str -#undef i_key_ref +#undef i_key_sptr #undef i_key_bind #undef i_keyraw #undef i_keyfrom -- cgit v1.2.3