1 | // LAF Base Library |
2 | // Copyright (c) 2020 Igara Studio S.A. |
3 | // |
4 | // This file is released under the terms of the MIT license. |
5 | // Read LICENSE.txt for more information. |
6 | |
7 | #ifndef BASE_REF_H_INCLUDED |
8 | #define BASE_REF_H_INCLUDED |
9 | #pragma once |
10 | |
11 | #include "base/debug.h" |
12 | #include "base/ints.h" |
13 | |
14 | #include <atomic> |
15 | |
16 | namespace base { |
17 | |
18 | template<typename T> |
19 | class RefCountT { |
20 | public: |
21 | RefCountT() : m_ref(1) { } |
22 | |
23 | ~RefCountT() { |
24 | // m_ref can be 1 in case that RefCountT() was created in the |
25 | // stack, and 0 when it's deleted from unref(). |
26 | ASSERT(m_ref == 0 || m_ref == 1); |
27 | } |
28 | |
29 | RefCountT(const RefCountT&) : m_ref(1) { } |
30 | RefCountT& operator=(const RefCountT&) { return *this; } |
31 | |
32 | void ref() { |
33 | ASSERT(m_ref > 0); |
34 | m_ref.fetch_add(1, std::memory_order_relaxed); |
35 | } |
36 | |
37 | void unref() { |
38 | ASSERT(m_ref > 0); |
39 | if (m_ref.fetch_sub(1, std::memory_order_acq_rel) == 1) |
40 | delete (T*)this; |
41 | } |
42 | |
43 | #ifdef _DEBUG // For debugging purposes only (TRACE, TRACEARGS, etc.) |
44 | uint32_t ref_count() const { return m_ref; } |
45 | #endif |
46 | |
47 | private: |
48 | std::atomic<uint32_t> m_ref; |
49 | }; |
50 | |
51 | class RefCount : public RefCountT<RefCount> { |
52 | public: |
53 | RefCount() { } |
54 | virtual ~RefCount() { } |
55 | |
56 | // Copy and move disabled for classes with virtual destruction |
57 | RefCount(const RefCount&) = delete; |
58 | RefCount(RefCount&&) = delete; |
59 | RefCount& operator=(const RefCount&) = delete; |
60 | RefCount& operator=(RefCount&&) = delete; |
61 | }; |
62 | |
63 | // Smart pointer for RefCountT objects |
64 | template<typename T> |
65 | class Ref { |
66 | public: |
67 | Ref() noexcept : m_ptr(nullptr) { } |
68 | Ref(std::nullptr_t) noexcept : m_ptr(nullptr) { } |
69 | |
70 | explicit Ref(T* ptr) noexcept : m_ptr(ptr) { } |
71 | template<typename U> |
72 | explicit Ref(U* ptr) noexcept : m_ptr(static_cast<T*>(ptr)) { } |
73 | |
74 | Ref(Ref<T>&& ref) noexcept : m_ptr(ref.release()) { } |
75 | template<typename U> |
76 | Ref(Ref<U>&& ref) noexcept : m_ptr(static_cast<T*>(ref.release())) { } |
77 | |
78 | Ref(const Ref<T>& ref) : m_ptr(ref.m_ptr) { |
79 | if (m_ptr) m_ptr->ref(); |
80 | } |
81 | template<typename U> |
82 | Ref(const Ref<U>& ref) : m_ptr(static_cast<T*>(ref.m_ptr)) { |
83 | if (m_ptr) m_ptr->ref(); |
84 | } |
85 | |
86 | ~Ref() { if (m_ptr) m_ptr->unref(); } |
87 | |
88 | void reset(T* ptr = nullptr) { |
89 | if (m_ptr) m_ptr->unref(); |
90 | m_ptr = ptr; |
91 | if (m_ptr) m_ptr->ref(); |
92 | } |
93 | |
94 | T* release() { |
95 | T* ptr = m_ptr; |
96 | m_ptr = nullptr; |
97 | return ptr; |
98 | } |
99 | |
100 | T* get() const { return m_ptr; } |
101 | T* operator->() const { return m_ptr; } |
102 | T& operator*() const { return *m_ptr; } |
103 | |
104 | // Do not define operator=(T*) because stealing references from |
105 | // raw pointers is like an implicit copy ctor from a raw pointer. |
106 | // Use AddRef() for these cases, e.g.: |
107 | // |
108 | // void function(T* refCountedObj) { |
109 | // Ref<T> p; |
110 | // p = AddRef(refCountedObj); |
111 | // ... |
112 | // } |
113 | Ref<T>& operator=(T* ptr) = delete; |
114 | |
115 | Ref<T>& operator=(std::nullptr_t) { |
116 | reset(); |
117 | return *this; |
118 | } |
119 | |
120 | Ref<T>& operator=(Ref<T>&& ref) { |
121 | if (m_ptr) m_ptr->unref(); |
122 | m_ptr = ref.release(); |
123 | return *this; |
124 | } |
125 | |
126 | Ref<T>& operator=(const Ref<T>& ref) { |
127 | if (m_ptr) { |
128 | if (m_ptr == ref.m_ptr) |
129 | return *this; |
130 | m_ptr->unref(); |
131 | } |
132 | m_ptr = ref.m_ptr; |
133 | if (m_ptr) m_ptr->ref(); |
134 | return *this; |
135 | } |
136 | |
137 | // Comparison between other Ref, raw pointers and nullpointers |
138 | bool operator==(const Ref<T>& r) const { return m_ptr == r.m_ptr; } |
139 | bool operator!=(const Ref<T>& r) const { return m_ptr != r.m_ptr; } |
140 | bool operator==(const T* p) const { return m_ptr == p; } |
141 | bool operator!=(const T* p) const { return m_ptr != p; } |
142 | bool operator==(std::nullptr_t) const { return m_ptr == nullptr; } |
143 | bool operator!=(std::nullptr_t) const { return m_ptr != nullptr; } |
144 | |
145 | explicit operator bool() const { |
146 | return m_ptr != nullptr; |
147 | } |
148 | |
149 | void swap(Ref<T>& r) noexcept { |
150 | std::swap(m_ptr, r.m_ptr); |
151 | } |
152 | |
153 | private: |
154 | T* m_ptr; |
155 | }; |
156 | |
157 | template<typename T> |
158 | inline bool operator==(T* ptr, const Ref<T>& r) { |
159 | return r.get() == ptr; |
160 | } |
161 | |
162 | template<typename T> |
163 | inline bool operator!=(T* ptr, const Ref<T>& r) { |
164 | return r.get() != ptr; |
165 | } |
166 | |
167 | template<typename T> |
168 | inline bool operator==(std::nullptr_t, const Ref<T>& r) { |
169 | return r.get() == nullptr; |
170 | } |
171 | |
172 | template<typename T> |
173 | inline bool operator!=(std::nullptr_t, const Ref<T>& r) { |
174 | return r.get() != nullptr; |
175 | } |
176 | |
177 | template<typename T, |
178 | typename ...Args> |
179 | Ref<T> make_ref(Args&&...args) { |
180 | return Ref<T>(new T(std::forward<Args>(args)...)); |
181 | } |
182 | |
183 | // AddRef() is like Ref() ctor but adding a new ref (useful to |
184 | // create a new ref from a raw pointer). |
185 | template<typename T> |
186 | inline Ref<T> AddRef(T* r) { |
187 | if (r) r->ref(); |
188 | return Ref<T>(r); |
189 | } |
190 | |
191 | } // namespace base |
192 | |
193 | #endif |
194 | |