libpromeki main
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
sharedptr.h
Go to the documentation of this file.
1
9#pragma once
10
11#include <atomic>
12#include <cassert>
13#include <typeinfo>
14#include <utility>
16
18
44#define PROMEKI_SHARED(BASE) \
45 public: \
46 RefCount _promeki_refct; \
47 virtual BASE *_promeki_clone() const { return new BASE(*this); }
48
67#define PROMEKI_SHARED_DERIVED(BASE, DERIVED) \
68 public: \
69 virtual BASE *_promeki_clone() const override { return new DERIVED(*this); }
70
88#define PROMEKI_SHARED_FINAL(TYPE) \
89 public: \
90 RefCount _promeki_refct; \
91 TYPE *_promeki_clone() const { return new TYPE(*this); }
92
101class RefCount {
102 public:
104 static constexpr int Immortal = 0x40000000;
105
107 RefCount() : v(1) {}
108
110 RefCount(const RefCount &o) : v(1) {}
111
113 RefCount &operator=(const RefCount &) { v.store(1, std::memory_order_relaxed); return *this; }
114
121 void inc() {
122 if(v.load(std::memory_order_relaxed) >= Immortal) return;
123 v.fetch_add(1, std::memory_order_relaxed);
124 return;
125 }
126
133 bool dec() {
134 if(v.load(std::memory_order_relaxed) >= Immortal) return false;
135 return v.fetch_sub(1, std::memory_order_acq_rel) == 1;
136 }
137
139 int value() const {
140 return v.load(std::memory_order_relaxed);
141 }
142
144 bool isImmortal() const {
145 return v.load(std::memory_order_relaxed) >= Immortal;
146 }
147
149 void setImmortal() {
150 v.store(Immortal, std::memory_order_relaxed);
151 }
152
153 private:
154 std::atomic<int> v;
155};
156
173template<typename T>
175 public:
176 RefCount _promeki_refct;
177
178 SharedPtrProxy(T *o) : _object(o) {}
179 ~SharedPtrProxy() { delete _object; }
180 T *_promeki_clone() const {
181 // When using a proxy data type, i.e. not a "native" shared object,
182 // there's no way to make a copy from a derived object work.
183 assert(typeid(*_object) == typeid(T));
184 return new T(*_object);
185 }
186 T *object() const { return _object; }
187 private:
188 T *_object = nullptr;
189};
190
191template<typename T, typename = void> struct IsSharedObject : std::false_type {};
192template<typename T> struct IsSharedObject<T, std::void_t<decltype(&T::_promeki_refct)>> : std::true_type {};
193
253 public:
254 static constexpr bool isNative = IsSharedObject<T>::value;
255
256 // Any attempt to modify the object when it's shared (i.e. a reference
257 // count > 1), the object will be copied first and that new object
258 // will be the one that's modified.
259 static constexpr bool isCopyOnWrite = CopyOnWrite;
260
261 SharedPtr() = default;
262
263 SharedPtr(const SharedPtr &sp) : _data(sp._data) { acquire(); }
264 SharedPtr(SharedPtr &&sp) noexcept : _data(sp._data) { sp._data = nullptr; }
265 ~SharedPtr() { release(); }
266
267 template<typename... Args>
268 static SharedPtr create(Args&&... args) {
270 sp.setData(new T(std::forward<Args>(args)...));
271 return sp;
272 }
273
274 static SharedPtr takeOwnership(T *obj) {
276 if(obj != nullptr) sp.setData(obj);
277 return sp;
278 }
279
280 SharedPtr &operator=(const SharedPtr &sp) {
281 if(&sp == this) return *this; // we're setting to ourself, nothing changes.
282 release();
283 _data = sp._data;
284 acquire();
285 return *this;
286 }
287
288 SharedPtr &operator=(SharedPtr &&sp) noexcept {
289 if(&sp == this) return *this;
290 release();
291 _data = sp._data;
292 sp._data = nullptr;
293 return *this;
294 }
295
296 void swap(SharedPtr &other) noexcept {
297 std::swap(_data, other._data);
298 }
299
300 void clear() {
301 release();
302 _data = nullptr;
303 return;
304 }
305
306 void detach() {
307 // We only detach if not null and ref count > 1
308 if(_data == nullptr || _data->_promeki_refct.value() < 2) return;
309 T *copy = _data->_promeki_clone();
310 release();
311 setData(copy);
312 // No need to acquire, since new Data object will have a refct of 1
313 return;
314 }
315
316 bool isNull() const {
317 return _data == nullptr;
318 }
319
320 bool isValid() const {
321 return _data != nullptr;
322 }
323
324 explicit operator bool() const {
325 return _data != nullptr;
326 }
327
328 bool operator==(const SharedPtr &other) const {
329 return _data == other._data;
330 }
331
332 bool operator!=(const SharedPtr &other) const {
333 return _data != other._data;
334 }
335
336 bool operator==(std::nullptr_t) const {
337 return _data == nullptr;
338 }
339
340 bool operator!=(std::nullptr_t) const {
341 return _data != nullptr;
342 }
343
344 int referenceCount() const {
345 return _data == nullptr ? 0 : _data->_promeki_refct.value();
346 }
347
348 const T *ptr() const {
349 assert(_data != nullptr);
350 if constexpr (IsSharedObject<T>::value) {
351 return _data;
352 } else {
353 return _data->object();
354 }
355 }
356
357 T *modify() {
358 assert(_data != nullptr);
359 if constexpr (CopyOnWrite) detach();
360 if constexpr (IsSharedObject<T>::value) {
361 return _data;
362 } else {
363 return _data->object();
364 }
365 }
366
367 const T *operator->() const {
368 return ptr();
369 }
370
371 const T &operator*() const {
372 return *ptr();
373 }
374
375 private:
376 ST *_data = nullptr;
377
378 // This function assumes you've already assigned _data to the Data you'd like to acquire
379 void acquire() {
380 if(_data == nullptr) return;
381 _data->_promeki_refct.inc();
382 return;
383 }
384
385 // This function releases _data, and if the reference count has dropped to zero, deletes it
386 // however it does not change _data so it will be stale after calling this function.
387 // This assumes you know this and are about to change _data (i.e. in a swap situation)
388 void release() {
389 if(_data == nullptr) return;
390 if(_data->_promeki_refct.dec()) delete _data;
391 return;
392 }
393
394 constexpr void setData(T *obj) {
395 if constexpr (IsSharedObject<T>::value) {
396 _data = obj;
397 } else {
398 _data = new ST(obj);
399 }
400 return;
401 }
402
403};
404
405
Dynamic array container wrapping std::vector.
Definition list.h:40
An atomic reference count object.
Definition sharedptr.h:101
void inc()
Atomically increments the reference count.
Definition sharedptr.h:121
bool dec()
Atomically decrements the reference count.
Definition sharedptr.h:133
RefCount()
Constructs a reference count initialized to 1.
Definition sharedptr.h:107
RefCount & operator=(const RefCount &)
Copy assignment resets the reference count to 1.
Definition sharedptr.h:113
void setImmortal()
Marks this refcount as immortal. inc/dec become no-ops.
Definition sharedptr.h:149
RefCount(const RefCount &o)
Copy constructor resets the reference count to 1 for the new object.
Definition sharedptr.h:110
bool isImmortal() const
Returns true if this refcount is immortal (will never reach zero).
Definition sharedptr.h:144
int value() const
Returns the current reference count value.
Definition sharedptr.h:139
static constexpr int Immortal
Threshold at or above which the refcount is considered immortal.
Definition sharedptr.h:104
A proxy class for managing reference counting of objects that do not natively support it.
Definition sharedptr.h:174
A smart pointer class with reference counting and optional copy-on-write semantics.
Definition sharedptr.h:252
#define PROMEKI_NAMESPACE_BEGIN
Starts a promeki namespace block.
Definition namespace.h:14
#define PROMEKI_NAMESPACE_END
Ends a promeki namespace block.
Definition namespace.h:19
Definition sharedptr.h:191