]> git.tdb.fi Git - libs/core.git/blob - source/core/refptr.h
1a164c1a90eafe69d3eab755b971400ff1f3e275
[libs/core.git] / source / core / refptr.h
1 #ifndef MSP_CORE_REFPTR_H_
2 #define MSP_CORE_REFPTR_H_
3
4 namespace Msp {
5
6 struct RefCounts
7 {
8         enum
9         {
10                 KEEP = 1U<<(sizeof(unsigned)*8-1)
11         };
12
13         unsigned count = 0;
14         unsigned weak_count = 0;
15 };
16
17 template<typename T>
18 class WeakPtr;
19
20
21 /**
22 A reference counting smart pointer.  When the last RefPtr for the data gets
23 destroyed, the data is deleted as well.
24 */
25 template<typename T>
26 class RefPtr
27 {
28         template<typename U> friend class RefPtr;
29         template<typename U> friend class WeakPtr;
30
31 private:
32         T *data = nullptr;
33         RefCounts *counts = nullptr;
34
35 public:
36         RefPtr() = default;
37         RefPtr(T *d): data(d), counts(data ? new RefCounts : nullptr) { incref(); }
38 private:
39         RefPtr(T *d, RefCounts *c): data(d), counts(d ? c : nullptr) { incref(); }
40
41 public:
42         /* Must have this or the compiler will generate a default copy-c'tor despite
43         the template version */
44         RefPtr(const RefPtr &p): data(p.data), counts(p.counts) { incref(); }
45
46         template<typename U>
47         RefPtr(const RefPtr<U> &p): data(p.data), counts(p.counts) { incref(); }
48
49         template<typename U>
50         RefPtr(const WeakPtr<U> &p): data(p.get()), counts(data ? p.counts : nullptr) { incref(); }
51
52         ~RefPtr() { decref(); }
53
54         RefPtr &operator=(T *);
55
56         // Likewise for the assignment operator
57         RefPtr &operator=(const RefPtr &p) { return assign(p); }
58
59         template<typename U>
60         RefPtr &operator=(const RefPtr<U> &p) { return assign(p); }
61
62         template<typename U>
63         RefPtr &operator=(const WeakPtr<U> &p) { return assign(RefPtr(p)); }
64
65 private:
66         template<typename U>
67         RefPtr &assign(const RefPtr<U> &);
68
69 public:
70         /** Makes the RefPtr release its reference of the data without deleting it.
71         Note that if there are other RefPtrs left with the same data, it might
72         still get deleted automatically. */
73         T *release();
74
75         /** Marks the data to not be deleted.  This affects all RefPtrs with the
76         same data. */
77         void keep() { if(counts) counts->count |= RefCounts::KEEP; }
78
79         T *get() const { return data; }
80         T &operator*() const { return *data; }
81         T *operator->() const { return data; }
82         explicit operator bool() const { return data; }
83
84         unsigned refcount() const { return (data ? counts->count : 0); }
85
86         template<typename U>
87         static RefPtr<T> cast_dynamic(const RefPtr<U> &p)
88         { return RefPtr<T>(dynamic_cast<T *>(p.data), p.counts); }
89
90 private:
91         void incref() { if(counts) ++counts->count; }
92         void decref();
93 };
94
95
96 template<typename T>
97 class WeakPtr
98 {
99         template<typename U> friend class RefPtr;
100         template<typename U> friend class WeakPtr;
101
102 private:
103         T *data = nullptr;
104         RefCounts *counts = nullptr;
105
106 public:
107         WeakPtr() = default;
108 private:
109         WeakPtr(T *d, RefCounts *c): data(d), counts(d ? c : nullptr) { incref(); }
110
111 public:
112         WeakPtr(const WeakPtr &p): data(p.get()), counts(data ? p.counts : nullptr) { incref(); }
113
114         template<typename U>
115         WeakPtr(const WeakPtr<U> &p): data(p.get()), counts(data ? p.counts : nullptr) { incref(); }
116
117         template<typename U>
118         WeakPtr(const RefPtr<U> &p): data(p.data), counts(p.counts) { incref(); }
119
120         WeakPtr &operator=(const WeakPtr &p) { return assign(p); }
121
122         template<typename U>
123         WeakPtr &operator=(const WeakPtr<U> &p) { return assign(p); }
124
125         template<typename U>
126         WeakPtr &operator=(const RefPtr<U> &p) { return assign(WeakPtr(p)); }
127
128 private:
129         template<typename U>
130         WeakPtr &assign(const WeakPtr<U> &);
131
132         T *get() const { return (counts && counts->count ? data : nullptr); }
133         void incref() { if(counts) ++counts->weak_count; }
134         void decref();
135 };
136
137
138 template<typename T>
139 RefPtr<T> &RefPtr<T>::operator=(T *d)
140 {
141         decref();
142         data = d;
143         counts = (d ? new RefCounts : nullptr);
144         incref();
145         return *this;
146 }
147
148 template<typename T>
149 template<typename U>
150 RefPtr<T> &RefPtr<T>::assign(const RefPtr<U> &p)
151 {
152         if(static_cast<const void *>(&p)==this)
153                 return *this;
154
155         decref();
156         data = p.data;
157         counts = p.counts;
158         incref();
159         return *this;
160 }
161
162 template<typename T>
163 T *RefPtr<T>::release()
164 {
165         T *d = data;
166         data = nullptr;
167         decref();
168         counts = nullptr;
169         return d;
170 }
171
172 template<typename T>
173 void RefPtr<T>::decref()
174 {
175         if(!counts) return;
176         --counts->count;
177         if(!counts->count)
178         {
179                 delete data;
180                 data = nullptr;
181         }
182         else if(counts->count==RefCounts::KEEP)
183                 data = nullptr;
184         else
185                 return;
186
187         if(!counts->weak_count)
188         {
189                 delete counts;
190                 counts = nullptr;
191         }
192 }
193
194
195 template<typename T>
196 template<typename U>
197 WeakPtr<T> &WeakPtr<T>::assign(const WeakPtr<U> &p)
198 {
199         if(&p==this)
200                 return *this;
201
202         decref();
203         data = p.get();
204         counts = (data ? p.counts : nullptr);
205         incref();
206         return *this;
207 }
208
209 template<typename T>
210 void WeakPtr<T>::decref()
211 {
212         if(!counts) return;
213         --counts->weak_count;
214         if(!counts->weak_count && (!counts->count || counts->count==RefCounts::KEEP))
215         {
216                 delete counts;
217                 data = nullptr;
218                 counts = nullptr;
219         }
220 }
221
222 } // namespace Msp
223
224 #endif