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
183
184
185
186
187
188
189
190
191
|
# STC [csptr](../include/stc/csptr.h): Shared Pointers
**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 either of the following happens:
- the last remaining **csptr** owning the object is destroyed with *csptr_X_del()*;
- the last remaining **csptr** owning the object is assigned another pointer via *csptr_X_clone()*, *csptr_X_move()* or by *csptr_X_reset()*.
The object is destroyed using *csptr_X_del()* or a custom deleter that is supplied to **csptr**
in the using-statement.
A **csptr** may also own no objects, in which case it is called empty.
All **csptr** functions can be called by multiple threads on different instances of **csptr** 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_del()* methods.
See the c++ classes [std::shared_ptr](https://en.cppreference.com/w/cpp/memory/shared_ptr) for a functional reference.
## Header file and declaration
```c
#include <stc/csptr.h>
using_csptr(X, Value);
using_csptr(X, Value, valueCompare);
using_csptr(X, Value, valueCompare, valueDel);
```
The macro `using_csptr()` must be instantiated in the global scope. `X` is a type tag name and will affect the names of all csptr types and methods. E.g. declaring `using_csptr(v4, Vec4)`, `X` should be replaced by `v4` in all of the following documentation.
## Methods
Use *csptr_X_clone(p)* when sharing ownership of the pointed-to object. See examples below.
The *csptr_X_compare()*, *csptr_X_equals()* and *csptr_X_del()* methods are defined based on the *valeCompare* and *valueDel* arguments passed to the **using**-macro.
```c
csptr_X csptr_X_from(csptr_X_value_t* ptr); // constructor
csptr_X csptr_X_make(csptr_X_value_t val); // make_shared
void csptr_X_reset(csptr_X* self);
void csptr_X_reset_with(csptr_X* self, csptr_X_value_t* ptr);
csptr_X csptr_X_clone(csptr_X sptr); // share the pointer (increase use count)
void csptr_X_move(csptr_X* self); // transfer ownership instead of sharing.
void csptr_X_del(csptr_X* self); // destructor: decrease use count, destroy at 0
int csptr_X_compare(csptr_X* x, csptr_X* y);
bool csptr_X_equals(csptr_X* x, csptr_X* y);
```
## Types and constants
| Type name | Type definition | Used to represent... |
|:--------------------|:--------------------------------------------------------------|:-------------------------|
| `csptr_null` | `{NULL, NULL}` | Init nullptr const |
| `csptr_X` | `struct { csptr_X_value_t* get; atomic_count_t* use_count; }` | The csptr type |
| `csptr_X_value_t` | `Value` | The csptr element type |
| `atomic_count_t` | `long` | The reference counter |
## Example
```c
#include <stc/csptr.h>
#include <stc/cstr.h>
typedef struct { cstr name, last; } Person;
Person* Person_make(Person* p, const char* name, const char* last) {
p->name = cstr_from(name), p->last = cstr_from(last);
return p;
}
void Person_del(Person* p) {
printf("Destroy: %s %s\n", p->name.str, p->last.str);
c_del(cstr, &p->name, &p->last);
}
using_csptr(pe, Person, c_no_compare, Person_del);
int main() {
csptr_pe p = csptr_pe_from(Person_make(c_new(Person), "John", "Smiths"));
csptr_pe q = csptr_pe_clone(p); // means: share the pointer
printf("Person: %s %s. uses: %zu\n", p.get->name.str, p.get->last.str, *p.use_count);
csptr_pe_del(&p);
printf("Last man standing: %s %s. uses: %zu\n", q.get->name.str, q.get->last.str, *q.use_count);
csptr_pe_del(&q);
}
```
Output:
```
Person: John Smiths. uses: 2
Last man standing: John Smiths. uses: 1
Destroy: John Smiths
```
### Example 2
Advanced: Two different ways to store Person in vectors: 1) `cvec<Person>`, 2) `cvec< csptr<Person> >`.
```c
#include <stc/csptr.h>
#include <stc/cstr.h>
#include <stc/cvec.h>
typedef struct { cstr name, last; } Person;
Person* Person_make(Person* p, const char* name, const char* last) {
p->name = cstr_from(name), p->last = cstr_from(last);
return p;
}
int Person_compare(const Person* p, const Person* q) {
int cmp = strcmp(p->name.str, q->name.str);
return cmp == 0 ? strcmp(p->last.str, q->last.str) : cmp;
}
void Person_del(Person* p) {
printf("del: %s\n", p->name.str);
c_del(cstr, &p->name, &p->last);
}
// 1. cvec of Person struct; emplace and cloning disabled.
using_cvec(pe, Person, Person_compare, Person_del, c_no_clone);
// 2. cvec of shared-ptr to Person - with emplace_back() and cloning cvec ENABLED.
using_csptr(pe, Person, Person_compare, Person_del);
using_cvec(ps, csptr_pe, csptr_pe_compare, csptr_pe_del, csptr_pe_clone);
const char* names[] = {
"Joe", "Jordan",
"Annie", "Aniston",
"Jane", "Jacobs"
};
int main() {
cvec_pe vec1 = cvec_pe_init();
cvec_ps vec2 = cvec_ps_init();
for (int i = 0; i < 6; i += 2) {
Person tmp;
cvec_pe_push_back(&vec1, *Person_make(&tmp, names[i], names[i+1]));
cvec_ps_push_back(&vec2, csptr_pe_from(Person_make(c_new(Person), names[i], names[i+1])));
}
puts("1. Sorted vec1 of Person:");
cvec_pe_sort(&vec1);
c_foreach (i, cvec_pe, vec1)
printf(" %s %s\n", i.ref->name.str, i.ref->last.str);
// Append a shared copy of vec2.data[0]. Will only be destructed once!
cvec_ps_emplace_back(&vec2, vec2.data[0]); // emplace will internally call csptr_ps_clone()!
puts("\n2. Sorted vec2 of shared-pointer to Person:");
cvec_ps_sort(&vec2);
c_foreach (i, cvec_ps, vec2)
printf(" %s %s\n", i.ref->get->name.str, i.ref->get->last.str);
// Share vec2.data[1] with elem1 variable.
csptr_pe elem1 = csptr_pe_clone(vec2.data[1]);
puts("\nDestroy vec1:");
cvec_pe_del(&vec1);
puts("\nDestroy vec2:");
cvec_ps_del(&vec2);
puts("\nDestroy elem1:");
csptr_pe_del(&elem1);
}
```
Output:
```
1. Sorted vec1 of Person:
Annie Aniston
Jane Jacobs
Joe Jordan
2. Sorted vec2 of shared-pointer to Person:
Annie Aniston
Jane Jacobs
Joe Jordan
Joe Jordan
Destroy vec1:
del: Annie
del: Jane
del: Joe
Destroy vec2:
del: Annie
del: Joe
Destroy elem1:
del: Jane
```
|