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
16namespace 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