-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathsharedptr.hpp
198 lines (186 loc) · 5.19 KB
/
sharedptr.hpp
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
192
193
194
195
196
197
198
/** \file sharedptr.hpp
* \copyright GPLv3
* \details Defines smart pointers with reference counting. No spin lock is
* applied.
* \author jingkaimori
* \date 2024
*/
#pragma once
#include "fast_alloc.hpp"
#include <utility>
struct ref_counter_base {
virtual void inc_count ()= 0;
virtual void dec_count ()= 0;
virtual void* get () = 0;
};
/**
* @brief Structure representing an object with a reference count.
* @tparam T Actual type of inner object, must be a complete type
*/
template <typename T> struct ref_counter : ref_counter_base {
/// @brief the reference count of the object
int ref_count;
T content;
/**
* @brief in-place construct an object, and set its reference count to 1.
* @tparam Params types of parameters required by constructor of object
*/
template <typename... Params>
explicit ref_counter (Params&&... p)
: ref_count (1), content (std::forward<Params> (p)...){};
void inc_count () { ref_count++; }
/**
* @brief decrement the reference count of the object and delete it if the
* count reaches 0.
*/
void dec_count () {
ref_count--;
if (ref_count == 0) {
tm_delete (this);
}
}
void* get () { return &content; }
};
/**
* @brief Smart pointer with reference counting.
* @details
* To use smart pointer, counted_ptr class should be inherited publicly, and the
* type exploited to user of the smart pointer should be provided. If null
* pointer may be held inside the smart pointer, parameter nullable should be
* set to true explictly.
*
* The class derived from pointer should provide proper constructor so that user
* can instantiate pointer from arguments of other type. Operator should be
* overloaded at the derived class, rather than the type of underlying object.
* @tparam T The type which user of pointer get from indirection of the smart
* pointer.
*/
template <typename T, bool nullable= false> class counted_ptr {
protected:
using counter_t= ref_counter_base;
/**
* @brief short-hand name of the class, avoiding duplicated type name of
* underlying object inside template. If the derived class is also template
* class, this declaration should be imported explictly with `using` clause.
*/
using base= counted_ptr<T, nullable>;
explicit counted_ptr (counter_t* c)
: counter (c), rep (static_cast<T*> (c->get ())) {}
/**
* @tparam Stored the real type of underlying object, may differ from types
* exploits to user. This type must be complete type.
*/
template <typename Stored= T, typename... Params>
static counter_t* make (Params&&... p) {
return tm_new<ref_counter<Stored>> (std::forward<Params> (p)...);
}
private:
/**
* @brief Opaque pointer to the counter that holds the instance of the
* underlying object
*/
counter_t* counter;
public:
/**
* cached pointer to the underlying object
*/
T* rep;
counted_ptr () : counter (nullptr), rep (nullptr) {
static_assert (nullable,
"null pointer is not allowed in non-null smart pointer.");
}
/**
* copy constructor of the smart pointer
*/
counted_ptr (const counted_ptr<T, nullable>& x)
: counter (x.counter), rep (x.rep) {
if constexpr (nullable) {
if (counter != nullptr) {
counter->inc_count ();
}
}
else {
counter->inc_count ();
}
}
~counted_ptr () {
if constexpr (nullable) {
if (counter != nullptr) {
counter->dec_count ();
}
}
else {
counter->dec_count ();
}
}
/**
* @brief Decrement the reference count for the old object (`*this`) and
* increments the reference count for the new object.
* @param x the new object
*/
counted_ptr<T, nullable>& operator= (counted_ptr<T, nullable>& x) {
if constexpr (nullable) {
if (x.counter != nullptr) {
x.counter->inc_count ();
}
if (this->counter != nullptr) {
this->counter->dec_count ();
}
}
else {
x.counter->inc_count ();
this->counter->dec_count ();
}
this->counter= x.counter;
this->rep = x.rep;
return *this;
}
/**
* @brief Steal underlying reference counter from rvalue temprary object, then
* leave the old counter (`this->counter`) in the temprary object. Finally the
* temprary object will be destroyed along with old object held by `*this`
* @param x the new object
*/
counted_ptr<T, nullable>& operator= (counted_ptr<T, nullable>&& x) {
std::swap (this->counter, x.counter);
std::swap (this->rep, x.rep);
return *this;
}
/**
* @brief pointer dereference operator
*/
T* operator->() {
if constexpr (nullable) {
if (counter != nullptr) {
return rep;
}
else {
return nullptr;
}
}
else {
return rep;
}
}
/**
* @brief pointer dereference operator, call from const pointer to get const
* object
*/
const T* operator->() const { return operator->(); }
bool is_nil () const {
if constexpr (nullable) {
return counter == nullptr;
}
else {
return false;
}
}
};
/**
* @brief adapter of legacy is_nil function.
*/
template <typename T>
inline bool
is_nil (const counted_ptr<T, true> x) {
return x.is_nil ();
}