1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
|
# STC [carc](../include/stc/carc.h): Atomic Reference Counted Smart Pointer
**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 *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 **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.
**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 carc type, see example.
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).
## Header file and declaration
```c
#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_valdrop // 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 <stc/carc.h>
```
`X` should be replaced by the value of `i_tag` in all of the following documentation.
## Methods
```c
carc_X carc_X_init(); // empty shared pointer
carc_X carc_X_from(i_valraw raw); // construct a new value in an carc from raw type.
carc_X carc_X_make(i_val val); // make a carc from constructed val object. Faster than from_ptr().
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_to(carc_X* self, i_val* p); // assign new carc from ptr. Takes ownership of p.
uint64_t carc_X_value_hash(const i_val* x); // 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); // carc_X_value_cmp == 0
```
## Types and constants
| 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
```c
// Create a stack and a list of shared pointers to maps,
// and demonstrate sharing and cloning of maps.
#define i_implement
#include <stc/cstr.h>
#define i_type Map
#define i_key_str // strings
#define i_val int
#define i_keydrop(p) (printf(" drop name: %s\n", cstr_str(p)), cstr_drop(p))
#include <stc/csmap.h>
#define i_type Arc // (atomic) ref. counted type
#define i_val Map
#define i_valclone Map_clone
#define i_valdrop(p) (printf("drop Arc:\n"), Map_drop(p))
// 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 <stc/carc.h>
#define i_type Stack
#define i_val_arcbox Arc // note: define i_val_arcbox for carc/cbox value
#include <stc/cstack.h>
#define i_type List
#define i_val_arcbox Arc // as above
#include <stc/clist.h>
int main()
{
c_auto (Stack, stack)
c_auto (List, list)
{
// POPULATE the stack with shared pointers to Map:
Map *map;
map = Stack_push(&stack, Arc_make(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_make(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_make(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 by cloning(=sharing) the carc:
List_push_back(&list, Arc_clone(stack.data[0]));
List_push_back(&list, Arc_clone(stack.data[1]));
// Deep-copy (not share) a Map from the stack to the list
// List will contain two shared and two unshared maps.
map = List_push_back(&list, Arc_make(Map_clone(*stack.data[1].get)))->get;
// Add one more element to the cloned map:
Map_emplace_or_assign(map, "Cloned", 2022);
// Add one more element to the shared map:
Map_emplace_or_assign(stack.data[1].get, "Shared", 2022);
puts("STACKS");
c_foreach (i, Stack, stack) {
c_forpair (name, year, Map, *i.ref->get)
printf(" %s:%d", cstr_str(_.name), *_.year);
puts("");
}
puts("LIST");
c_foreach (i, List, list) {
c_forpair (name, year, Map, *i.ref->get)
printf(" %s:%d", cstr_str(_.name), *_.year);
puts("");
}
}
}
```
Output:
```
STACKS
Joanna:1992 Joey:1990 Mary:1995
Brad:1999 Jack:1980 Rosanna:2001 Shared:2022
LIST
Rick:1974 Steve:1979 Tracy:2003
Joanna:1992 Joey:1990 Mary:1995
Brad:1999 Jack:1980 Rosanna:2001 Shared:2022
Brad:1999 Cloned:2022 Jack:1980 Rosanna:2001
drop Arc:
drop name: Rick
drop name: Tracy
drop name: Steve
drop Arc:
drop name: Cloned
drop name: Brad
drop name: Rosanna
drop name: Jack
drop Arc:
drop name: Brad
drop name: Shared
drop name: Rosanna
drop name: Jack
drop Arc:
drop name: Joanna
drop name: Mary
drop name: Joey
```
|