/[svn]/linuxsampler/trunk/src/common/Ref.h
ViewVC logotype

Contents of /linuxsampler/trunk/src/common/Ref.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3767 - (show annotations) (download) (as text)
Fri May 15 19:28:13 2020 UTC (3 years, 11 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 14004 byte(s)
* Ref<> class: prevent compiler error if T == T_Base
  (due to method signature duplicate).

* Bumped version (2.1.1.svn55).

1 /*
2 * Copyright (c) 2014 - 2020 Christian Schoenebeck
3 *
4 * http://www.linuxsampler.org
5 *
6 * This file is part of LinuxSampler and released under the same terms.
7 * See README file for details.
8 */
9
10 #ifndef LS_REF_H
11 #define LS_REF_H
12
13 #include <set>
14 #include <stdio.h>
15 #if __cplusplus >= 201103L && !CONFIG_NO_CPP11STL
16 # include <type_traits> // for std::enable_if and std::is_same
17 #endif
18
19 // You may enable this while developing or at least when you encounter any kind
20 // of crashes or other misbehaviors in conjunction with Ref class guarded code.
21 // Enabling the following macro will add a bunch of sanity checks for easing
22 // debugging of such issues, however it comes with the cost that everything will
23 // be much slower.
24 #define LS_REF_ASSERT_MODE 0
25
26 #if LS_REF_ASSERT_MODE
27 # warning LS_REF_ASSERT_MODE is enabled which will decrease runtime efficiency!
28 #endif
29
30 // Enable this for VERY verbose debug messages for debbugging deep issues with
31 // Ref class.
32 #define LS_REF_VERBOSE_DEBUG_MSG 0
33
34 #if LS_REF_ASSERT_MODE
35 # include <assert.h>
36 #endif
37
38 namespace LinuxSampler {
39
40 //TODO: make reference count increment/decrement thread safe
41
42 template<typename T, typename T_BASE> class Ref;
43
44 extern std::set<void*> _allRefPtrs;
45
46 /**
47 * Exists just for implementation detail purpose, you cannot use it
48 * directly. Use its derived template class Ref instead.
49 *
50 * @see Ref
51 */
52 template<typename T_BASE>
53 class RefBase {
54 public:
55 template<typename T_BASE1>
56 class _RefCounter {
57 public:
58 _RefCounter(T_BASE1* p, int refs) :
59 references(refs), ptr(p)
60 {
61 #if LS_REF_VERBOSE_DEBUG_MSG
62 printf("Ref 0x%lx: new counter (refs=%d)\n", (long long)ptr, references);
63 #endif
64 #if LS_REF_ASSERT_MODE
65 assert(p);
66 assert(refs > 0);
67 assert(!_allRefPtrs.count(p));
68 _allRefPtrs.insert(p);
69 #endif
70 }
71
72 virtual ~_RefCounter() {
73 #if LS_REF_VERBOSE_DEBUG_MSG
74 printf("Ref 0x%lx: counter destructor (refs=%d)\n", (long long)ptr, references);
75 #endif
76 fflush(stdout);
77 }
78
79 void retain() {
80 references++;
81 #if LS_REF_VERBOSE_DEBUG_MSG
82 printf("Ref 0x%lx: retain (refs=%d)\n", (long long)ptr, references);
83 #endif
84 }
85
86 void release() {
87 if (!references) return;
88 references--;
89 #if LS_REF_VERBOSE_DEBUG_MSG
90 printf("Ref 0x%lx: release (refs=%d)\n", (long long)ptr, references);
91 #endif
92 if (!references) deletePtr();
93 }
94 //protected:
95 void deletePtr() {
96 #if LS_REF_VERBOSE_DEBUG_MSG
97 printf("RefCounter 0x%lx: deletePtr() (refs=%d)\n", (long long)ptr, references);
98 #endif
99 #if LS_REF_ASSERT_MODE
100 assert(!references);
101 _allRefPtrs.erase(ptr);
102 #endif
103 delete ptr;
104 delete this;
105 }
106
107 int references;
108 T_BASE1* ptr;
109 //friend class ... todo
110 };
111 typedef _RefCounter<T_BASE> RefCounter;
112
113 virtual ~RefBase() {
114 if (refCounter) refCounter->release();
115 refCounter = NULL;
116 }
117
118 //protected:
119 RefCounter* refCounter;
120 //friend class Ref<T_BASE, T_BASE>;
121
122 protected:
123 RefBase() : refCounter(NULL) {
124 #if LS_REF_VERBOSE_DEBUG_MSG
125 printf("(RefBase empty ctor)\n");
126 #endif
127 }
128 /*
129 RefBase(RefCounter* rc) {
130 refCounter = rc;
131 }
132
133 RefBase(const RefBase& r) {
134 refCounter = r.refCounter;
135 if (refCounter) refCounter->retain();
136 }
137 */
138 };
139
140 /**
141 * Replicates a std::shared_ptr template class, to avoid a build requirement
142 * of having a C++11 compliant compiler (std::shared_ptr was not part of the
143 * C++03 standard).
144 *
145 * In contrast to the STL implementation though this implementation here
146 * also supports copying references of derived, different types (in a type
147 * safe manner). You can achieve that by providing a second template
148 * argument (which is optional), for declaring a common subtype. For example
149 * the following code would not compile:
150 * @code
151 * void example(UILabel* pLabel) {
152 * Ref<UILabel> lbl = pLabel;
153 * Ref<UIWidget> w = lbl; // compile error, incompatible Ref types
154 * w->resize(16,300);
155 * }
156 * @endcode
157 * Whereas the following would work:
158 * @code
159 * void example(UILabel* pLabel) {
160 * Ref<UILabel,UIWidget> lbl = pLabel;
161 * Ref<UIWidget> w = lbl; // works (assuming that UILabel is a subclass of UIWidget)
162 * w->resize(16,300);
163 * }
164 * @endcode
165 * Like the STL's std::shared_ptr, this class also emulates raw pointer
166 * access and operators. With one addition: if used in the derived common
167 * subtype manner as shown above, access to the actual data and boolean
168 * operator will also check whether the underlying pointer (of the common
169 * subclass) can actually be casted safely to the objects main type (first
170 * template argument of this class). For example:
171 * @code
172 * void example(UILabel* pLabel) { // assuming pLabel is not NULL ...
173 * Ref<UILabel,UIWidget> lbl = pLabel;
174 * Ref<UIDialog,UIWidget> dlg = lbl;
175 * bool b1 = lbl; // will be true (assuming pLabel was not NULL)
176 * bool b2 = dlg; // will be false (assuming that UIDialog is not derived from UILabel)
177 * lbl->setText("foo"); // works
178 * dlg->showModal(); // would crash with -> operator providing a NULL pointer
179 * }
180 * @endcode
181 * Like with std::shared_ptr you must be @b very cautious that you
182 * initialize only one Ref class object directly with the same raw pointer.
183 * If you forget this fundamental rule somewhere, your code will crash!
184 * @code
185 * UIWidget* ptr = new UIWidget();
186 * Ref<UIWidget> w1 = ptr;
187 * Ref<UIWidget> w2 = w1; // this is OK, copy from a Ref object
188 * Ref<UIWidget> w3 = ptr; // illegal! 2nd direct init from same raw pointer. This will crash!
189 * @endcode
190 * It would be possible to write an implementation of the Ref class that
191 * could handle the case above as well without crashing, however it would be
192 * too slow for practice. Because it would require a global lookup table
193 * maintaining all memory pointers which are currently already guarded by
194 * this class. Plus it would need an expensive synchronization to prevent
195 * concurrent access on that global lookup table.
196 */
197 template<typename T, typename T_BASE = T>
198 class Ref : public RefBase<T_BASE> {
199 public:
200 typedef RefBase<T_BASE> RefBaseT;
201 typedef typename RefBase<T_BASE>::RefCounter RefCounter;
202
203 Ref() : RefBaseT() {
204 #if LS_REF_VERBOSE_DEBUG_MSG
205 printf("Ref empty ctor Ref:0x%lx\n", (long long)this);
206 #endif
207 }
208
209 #if __cplusplus >= 201103L && !CONFIG_NO_CPP11STL
210 template<std::enable_if<!std::is_same<T_BASE, T>::value>* = NULL> // prevent compiler error if T == T_Base (due to method signature duplicate)
211 #endif
212 Ref(const T_BASE* p) : RefBaseT() {
213 #if LS_REF_VERBOSE_DEBUG_MSG
214 printf("Ref base ptr ctor Ref:0x%lx <- p:0x%lx\n", (long long)this, (long long)p);
215 #endif
216 RefBaseT::refCounter = p ? new RefCounter((T_BASE*)p, 1) : NULL;
217 }
218
219 Ref(const T* p) : RefBaseT() {
220 #if LS_REF_VERBOSE_DEBUG_MSG
221 printf("Ref main ptr ctor Ref:0x%lx <- p:0x%lx\n", (long long)this, (long long)p);
222 #endif
223 RefBaseT::refCounter = p ? new RefCounter((T*)p, 1) : NULL;
224 }
225
226 Ref(const RefBaseT& r) : RefBaseT() {
227 #if LS_REF_VERBOSE_DEBUG_MSG
228 printf("Ref base ref ctor Ref:0x%lx <- Ref:0x%lx\n", (long long)this, (long long)&r);
229 #endif
230 RefBaseT::refCounter = r.refCounter;
231 if (RefBaseT::refCounter)
232 RefBaseT::refCounter->retain();
233 }
234
235 Ref(const Ref& r) : RefBaseT() {
236 #if LS_REF_VERBOSE_DEBUG_MSG
237 printf("Ref main ref ctor Ref:0x%lx <- Ref:0x%lx\n", (long long)this, (long long)&r);
238 #endif
239 RefBaseT::refCounter = r.refCounter;
240 if (RefBaseT::refCounter)
241 RefBaseT::refCounter->retain();
242 }
243
244 inline T* operator->() {
245 return dynamic_cast<T*>( RefBaseT::refCounter->ptr );
246 }
247
248 inline const T* operator->() const {
249 return dynamic_cast<const T*>( RefBaseT::refCounter->ptr );
250 }
251
252 inline T& operator*() {
253 return *dynamic_cast<T*>( RefBaseT::refCounter->ptr );
254 }
255
256 inline const T& operator*() const {
257 return *dynamic_cast<const T*>( RefBaseT::refCounter->ptr );
258 }
259
260 inline bool operator==(const RefBaseT& other) const {
261 return RefBaseT::refCounter == other.refCounter;
262 }
263
264 inline bool operator!=(const RefBaseT& other) const {
265 return RefBaseT::refCounter != other.refCounter;
266 }
267
268 inline operator bool() const {
269 return RefBaseT::refCounter && RefBaseT::refCounter->ptr &&
270 dynamic_cast<const T*>( RefBaseT::refCounter->ptr );
271 }
272
273 inline bool operator!() const {
274 return !( RefBaseT::refCounter && RefBaseT::refCounter->ptr &&
275 dynamic_cast<const T*>( RefBaseT::refCounter->ptr ) );
276 }
277
278 /*
279 inline operator RefBaseT&() {
280 return *this;
281 }
282
283 inline operator const RefBaseT&() const {
284 return *this;
285 }
286 */
287 inline bool isEquivalent(const RefBaseT& other) const {
288 if (static_cast<const RefBaseT*>(this) == &other)
289 return true;
290 return (RefBaseT::refCounter == other.refCounter);
291 }
292
293 inline bool isEquivalent(const T_BASE* const other) const {
294 if (!other) return !RefBaseT::refCounter;
295 if (!RefBaseT::refCounter) return false;
296 return other == RefBaseT::refCounter->ptr;
297 }
298
299 Ref<T,T_BASE>& operator=(const RefBaseT& other) {
300 #if LS_REF_VERBOSE_DEBUG_MSG
301 printf("Ref base ref assignment Ref:0x%lx <- Ref:0x%lx\n", (long long)this, (long long)&other);
302 #endif
303 if (isEquivalent(other)) {
304 #if LS_REF_VERBOSE_DEBUG_MSG
305 printf("Ref 0x%lx WRN: equivalent ref assignment ignored.\n", (long long)this);
306 #endif
307 return *this;
308 }
309 if (RefBaseT::refCounter) {
310 RefBaseT::refCounter->release();
311 RefBaseT::refCounter = NULL;
312 }
313 RefBaseT::refCounter = other.refCounter;
314 if (RefBaseT::refCounter)
315 RefBaseT::refCounter->retain();
316 return *this;
317 }
318
319 Ref<T,T_BASE>& operator=(const Ref<T,T_BASE>& other) {
320 #if LS_REF_VERBOSE_DEBUG_MSG
321 printf("Ref main ref assignment Ref:0x%lx <- Ref:0x%lx\n", (long long)this, (long long)&other);
322 #endif
323 if (isEquivalent(other)) {
324 #if LS_REF_VERBOSE_DEBUG_MSG
325 printf("Ref 0x%lx WRN: equivalent ref assignment ignored.\n", (long long)this);
326 #endif
327 return *this;
328 }
329 if (RefBaseT::refCounter) {
330 RefBaseT::refCounter->release();
331 RefBaseT::refCounter = NULL;
332 }
333 RefBaseT::refCounter = other.refCounter;
334 if (RefBaseT::refCounter)
335 RefBaseT::refCounter->retain();
336 return *this;
337 }
338
339 Ref<T,T_BASE>& operator=(const T* p) {
340 #if LS_REF_VERBOSE_DEBUG_MSG
341 printf("Ref main ptr assignment Ref:0x%lx <- p:0x%lx\n", (long long)this, p);
342 #endif
343 if (isEquivalent(p)) {
344 #if LS_REF_VERBOSE_DEBUG_MSG
345 printf("Ref 0x%lx WRN: equivalent ptr assignment ignored.\n", (long long)this);
346 #endif
347 return *this;
348 }
349 if (RefBaseT::refCounter) {
350 RefBaseT::refCounter->release();
351 RefBaseT::refCounter = NULL;
352 }
353 RefBaseT::refCounter = p ? new RefCounter((T*)p, 1) : NULL;
354 #if LS_REF_VERBOSE_DEBUG_MSG
355 printf("Ref main ptr assignment done\n");
356 #endif
357 return *this;
358 }
359
360 #if __cplusplus >= 201103L && !CONFIG_NO_CPP11STL
361 template<std::enable_if<!std::is_same<T_BASE, T>::value>* = NULL> // prevent compiler error if T == T_Base (due to method signature duplicate)
362 #endif
363 Ref<T,T_BASE>& operator=(const T_BASE* p) {
364 #if LS_REF_VERBOSE_DEBUG_MSG
365 printf("Ref base ptr assignment Ref:0x%lx <- p:0x%lx\n", (long long)this, p);
366 #endif
367 if (isEquivalent(p)) {
368 #if LS_REF_VERBOSE_DEBUG_MSG
369 printf("Ref 0x%lx WRN: equivalent ptr assignment ignored.\n", (long long)this);
370 #endif
371 return *this;
372 }
373 if (RefBaseT::refCounter) {
374 RefBaseT::refCounter->release();
375 RefBaseT::refCounter = NULL;
376 }
377 RefBaseT::refCounter = p ? new RefCounter((T*)p, 1) : NULL;
378 return *this;
379 }
380 };
381
382 } // namespace LinuxSampler
383
384 #endif // LS_REF_H

  ViewVC Help
Powered by ViewVC