spla
ref.hpp
Go to the documentation of this file.
1 /**********************************************************************************/
2 /* This file is part of spla project */
3 /* https://github.com/SparseLinearAlgebra/spla */
4 /**********************************************************************************/
5 /* MIT License */
6 /* */
7 /* Copyright (c) 2023 SparseLinearAlgebra */
8 /* */
9 /* Permission is hereby granted, free of charge, to any person obtaining a copy */
10 /* of this software and associated documentation files (the "Software"), to deal */
11 /* in the Software without restriction, including without limitation the rights */
12 /* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */
13 /* copies of the Software, and to permit persons to whom the Software is */
14 /* furnished to do so, subject to the following conditions: */
15 /* */
16 /* The above copyright notice and this permission notice shall be included in all */
17 /* copies or substantial portions of the Software. */
18 /* */
19 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
20 /* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
21 /* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */
22 /* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
23 /* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
24 /* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */
25 /* SOFTWARE. */
26 /**********************************************************************************/
27 
28 #ifndef SPLA_REF_HPP
29 #define SPLA_REF_HPP
30 
31 #include <atomic>
32 #include <cassert>
33 #include <functional>
34 #include <stdexcept>
35 #include <utility>
36 
37 namespace spla {
38 
55  class RefCnt {
56  public:
57  virtual ~RefCnt() {
58 #ifdef SPLA_DEBUG
59  assert(m_refs.load() == 0);
60  m_refs.store(0);
61 #endif
62  }
63 
64  bool is_unique() const {
65  return get_refs() == 1;
66  }
67 
68  std::int32_t get_refs() const {
69  return m_refs.load(std::memory_order_relaxed);
70  }
71 
72  std::int32_t add_ref() const {
73  assert(get_refs() >= 0);
74  return m_refs.fetch_add(1);
75  }
76 
77  std::int32_t rel_ref() const {
78  assert(get_refs() > 0);
79  auto refs = m_refs.fetch_sub(1);
80 
81  if (refs == 1) {
82  // Was last reference
83  // Destroy object and release memory
84  delete this;
85  }
86 
87  return refs;
88  }
89 
90  private:
91  // This type of object after creation always has no reference
92  mutable std::atomic_int32_t m_refs{0};
93  };
94 
95  template<typename T>
96  static inline T* safe_ref(T* object) {
97  if (object)
98  object->add_ref();
99  return object;
100  }
101 
102  template<typename T>
103  static inline void unref(T* object) {
104  if (object)
105  object->rel_ref();
106  }
107 
116  template<typename T>
117  class ref_ptr {
118  public:
119  ref_ptr() = default;
120 
121  explicit ref_ptr(T* object) {
122  m_object = safe_ref(object);
123  }
124 
125  ref_ptr(const ref_ptr& other) {
126  m_object = safe_ref(other.m_object);
127  }
128 
129  ref_ptr(ref_ptr&& other) noexcept {
130  m_object = other.m_object;
131  other.m_object = nullptr;
132  }
134  unref(m_object);
135  m_object = nullptr;
136  }
137 
138  ref_ptr<T>& operator=(const ref_ptr& other) {
139  if (this != &other)
140  this->reset(safe_ref(other.get()));
141  return *this;
142  }
143 
144  template<typename G>
145  ref_ptr<T>& operator=(const ref_ptr<G>& other) {
146  if (get() != other.get())
147  this->reset(safe_ref(other.get()));
148  return *this;
149  }
150 
151  ref_ptr<T>& operator=(ref_ptr&& other) noexcept {
152  if (this != &other)
153  this->reset(other.release());
154  return *this;
155  }
156 
157  bool operator==(const ref_ptr& other) const {
158  return m_object == other.m_object;
159  }
160  bool operator!=(const ref_ptr& other) const {
161  return m_object != other.m_object;
162  }
163 
164  [[nodiscard]] bool is_null() const {
165  return m_object == nullptr;
166  }
167 
168  [[nodiscard]] bool is_not_null() const {
169  return m_object;
170  }
171 
172  T* operator->() const {
173  assert(m_object);
174  return m_object;
175  }
176 
177  T& operator*() const {
178  assert(m_object);
179  return *m_object;
180  }
181 
182  explicit operator bool() const {
183  return m_object != nullptr;
184  }
185 
186  void acquire(T* safe_ref_ptr = nullptr) {
187  reset(safe_ref(safe_ref_ptr));
188  }
189 
190  void reset(T* ptr = nullptr) {
191  T* old = m_object;
192  m_object = ptr;
193  unref(old);
194  }
195 
196  T* release() {
197  T* ptr = m_object;
198  m_object = nullptr;
199  return ptr;
200  }
201 
202  T* get() const {
203  return m_object;
204  }
205 
206  T* ref_and_get() {
207  return safe_ref(m_object);
208  }
209 
210  template<typename G>
211  [[nodiscard]] bool is() const {
212  return !m_object || dynamic_cast<G*>(m_object);
213  }
214 
215  template<class G>
216  ref_ptr<G> as() const {
217  return ref_ptr<G>(m_object);
218  }
219 
220  template<class G>
222  auto casted = dynamic_cast<G*>(m_object);
223 
224  if (!casted && m_object) {
225  throw std::runtime_error("failed to do dynamic cast");
226  }
227 
228  return ref_ptr<G>(casted);
229  }
230 
231  template<class G>
232  ref_ptr<G> cast() const {
233  return ref_ptr<G>(dynamic_cast<G*>(m_object));
234  }
235 
236  template<class G>
238  return ref_ptr<G>((G*) m_object);
239  }
240 
241  private:
242  T* m_object = nullptr;
243  };
244 
245  template<typename T, typename... TArgs>
246  ref_ptr<T> make_ref(TArgs&&... args) {
247  return ref_ptr<T>(new T(std::forward<TArgs>(args)...));
248  }
249 
254 }// namespace spla
255 
256 #endif//SPLA_REF_HPP
Base class for object with built-in reference counting mechanism.
Definition: ref.hpp:55
virtual ~RefCnt()
Definition: ref.hpp:57
bool is_unique() const
Definition: ref.hpp:64
std::int32_t get_refs() const
Definition: ref.hpp:68
std::int32_t add_ref() const
Definition: ref.hpp:72
std::int32_t rel_ref() const
Definition: ref.hpp:77
Automates reference counting and behaves as shared smart pointer.
Definition: ref.hpp:117
void acquire(T *safe_ref_ptr=nullptr)
Definition: ref.hpp:186
ref_ptr< T > & operator=(const ref_ptr &other)
Definition: ref.hpp:138
bool is_null() const
Definition: ref.hpp:164
ref_ptr(T *object)
Definition: ref.hpp:121
ref_ptr< T > & operator=(const ref_ptr< G > &other)
Definition: ref.hpp:145
T * ref_and_get()
Definition: ref.hpp:206
void reset(T *ptr=nullptr)
Definition: ref.hpp:190
T * release()
Definition: ref.hpp:196
T * operator->() const
Definition: ref.hpp:172
T * get() const
Definition: ref.hpp:202
bool operator==(const ref_ptr &other) const
Definition: ref.hpp:157
bool is() const
Definition: ref.hpp:211
T & operator*() const
Definition: ref.hpp:177
bool is_not_null() const
Definition: ref.hpp:168
ref_ptr< G > cast_static() const
Definition: ref.hpp:237
ref_ptr()=default
ref_ptr(ref_ptr &&other) noexcept
Definition: ref.hpp:129
~ref_ptr()
Definition: ref.hpp:133
ref_ptr(const ref_ptr &other)
Definition: ref.hpp:125
ref_ptr< G > cast_safe() const
Definition: ref.hpp:221
ref_ptr< G > as() const
Definition: ref.hpp:216
ref_ptr< G > cast() const
Definition: ref.hpp:232
bool operator!=(const ref_ptr &other) const
Definition: ref.hpp:160
ref_ptr< T > & operator=(ref_ptr &&other) noexcept
Definition: ref.hpp:151
ref_ptr< T > make_ref(TArgs &&... args)
Definition: ref.hpp:246
Definition: algorithm.hpp:37