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