libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
sharedptr.h
Go to the documentation of this file.
1
9#pragma once
10
11
12#include <promeki/config.h>
13#if PROMEKI_ENABLE_CORE
14#include <cassert>
15#include <cstdlib>
16#include <type_traits>
17#include <typeinfo>
18#include <utility>
19#include <promeki/atomic.h>
20#include <promeki/namespace.h>
21
22PROMEKI_NAMESPACE_BEGIN
23
44template <typename T> T *promekiCloneOrAbort(const T *self) {
45 if constexpr (std::is_copy_constructible_v<T>) {
46 return new T(*self);
47 } else {
48 (void)self;
49 std::abort();
50 }
51}
52
84#define PROMEKI_SHARED_BASE(BASE) \
85public: \
86 RefCount _promeki_refct; \
87 virtual BASE *_promeki_clone() const { \
88 return ::promeki::promekiCloneOrAbort<BASE>(this); \
89 }
90
116#define PROMEKI_SHARED_DERIVED(DERIVED) \
117public: \
118 DERIVED *_promeki_clone() const override { \
119 return ::promeki::promekiCloneOrAbort<DERIVED>(this); \
120 }
121
144#define PROMEKI_SHARED_ABSTRACT(INTERMEDIATE) \
145public: \
146 INTERMEDIATE *_promeki_clone() const override = 0;
147
173#define PROMEKI_SHARED_FINAL(TYPE) \
174public: \
175 RefCount _promeki_refct; \
176 TYPE *_promeki_clone() const { \
177 return ::promeki::promekiCloneOrAbort<TYPE>(this); \
178 }
179
188class RefCount {
189 public:
191 static constexpr int Immortal = 0x40000000;
192
194 RefCount() : v(1) {}
195
197 RefCount(const RefCount &o) : v(1) {}
198
200 RefCount &operator=(const RefCount &) {
201 v.store(1, MemoryOrder::Relaxed);
202 return *this;
203 }
204
211 void inc() {
212 if (v.load(MemoryOrder::Relaxed) >= Immortal) return;
213 v.fetchAndAdd(1, MemoryOrder::Relaxed);
214 return;
215 }
216
223 bool dec() {
224 if (v.load(MemoryOrder::Relaxed) >= Immortal) return false;
225 return v.fetchAndSub(1, MemoryOrder::AcqRel) == 1;
226 }
227
229 int value() const { return v.load(MemoryOrder::Relaxed); }
230
232 bool isImmortal() const { return v.load(MemoryOrder::Relaxed) >= Immortal; }
233
235 void setImmortal() { v.store(Immortal, MemoryOrder::Relaxed); }
236
237 private:
238 Atomic<int> v;
239};
240
257template <typename T> class SharedPtrProxy {
258 public:
259 RefCount _promeki_refct;
260
261 SharedPtrProxy(T *o) : _object(o) {}
262 ~SharedPtrProxy() { delete _object; }
263 T *_promeki_clone() const {
264 // When using a proxy data type, i.e. not a "native" shared object,
265 // there's no way to make a copy from a derived object work.
266 assert(typeid(*_object) == typeid(T));
267 return new T(*_object);
268 }
269 T *object() const { return _object; }
270
271 private:
272 T *_object = nullptr;
273};
274
275template <typename T, typename = void> struct IsSharedObject : std::false_type {};
276template <typename T> struct IsSharedObject<T, std::void_t<decltype(&T::_promeki_refct)>> : std::true_type {};
277
335template <typename T, bool CopyOnWrite = true,
336 typename ST = std::conditional_t<IsSharedObject<T>::value, T, SharedPtrProxy<T>>>
337class SharedPtr {
338 public:
339 // All SharedPtr instantiations are friends of each other so the
340 // upcast/downcast helpers can reach the private _data pointer
341 // across type boundaries.
342 template <typename OT, bool OCoW, typename OST> friend class SharedPtr;
343
344 // sharedPointerCast needs access to _data for the dynamic_cast
345 // round-trip; befriend every instantiation of the free function.
346 template <typename DerivedT, typename BaseT, bool CoWT, typename STT>
347 friend SharedPtr<DerivedT, CoWT, DerivedT> sharedPointerCast(const SharedPtr<BaseT, CoWT, STT> &sp);
348
349 static constexpr bool isNative = IsSharedObject<T>::value;
350
351 // Any attempt to modify the object when it's shared (i.e. a reference
352 // count > 1), the object will be copied first and that new object
353 // will be the one that's modified.
354 static constexpr bool isCopyOnWrite = CopyOnWrite;
355
356 SharedPtr() = default;
357
358 SharedPtr(const SharedPtr &sp) : _data(sp._data) { acquire(); }
359 SharedPtr(SharedPtr &&sp) noexcept : _data(sp._data) { sp._data = nullptr; }
360
371 template <typename U, bool UCoW, typename UST,
372 typename = std::enable_if_t<std::is_base_of_v<T, U> && !std::is_same_v<T, U> &&
373 IsSharedObject<T>::value && IsSharedObject<U>::value &&
374 std::is_same_v<UST, U>>>
375 SharedPtr(const SharedPtr<U, UCoW, UST> &sp) : _data(sp._data) {
376 acquire();
377 }
378
379 template <typename U, bool UCoW, typename UST,
380 typename = std::enable_if_t<std::is_base_of_v<T, U> && !std::is_same_v<T, U> &&
381 IsSharedObject<T>::value && IsSharedObject<U>::value &&
382 std::is_same_v<UST, U>>>
383 SharedPtr(SharedPtr<U, UCoW, UST> &&sp) noexcept : _data(sp._data) {
384 sp._data = nullptr;
385 }
386
387 ~SharedPtr() { release(); }
388
389 template <typename... Args> static SharedPtr create(Args &&...args) {
390 SharedPtr sp;
391 sp.setData(new T(std::forward<Args>(args)...));
392 return sp;
393 }
394
395 static SharedPtr takeOwnership(T *obj) {
396 SharedPtr sp;
397 if (obj != nullptr) sp.setData(obj);
398 return sp;
399 }
400
401 SharedPtr &operator=(const SharedPtr &sp) {
402 if (&sp == this) return *this; // we're setting to ourself, nothing changes.
403 release();
404 _data = sp._data;
405 acquire();
406 return *this;
407 }
408
409 SharedPtr &operator=(SharedPtr &&sp) noexcept {
410 if (&sp == this) return *this;
411 release();
412 _data = sp._data;
413 sp._data = nullptr;
414 return *this;
415 }
416
417 void swap(SharedPtr &other) noexcept { std::swap(_data, other._data); }
418
419 void clear() {
420 release();
421 _data = nullptr;
422 return;
423 }
424
425 void detach() {
426 // We only detach if not null and ref count > 1
427 if (_data == nullptr || _data->_promeki_refct.value() < 2) return;
428 T *copy = _data->_promeki_clone();
429 release();
430 setData(copy);
431 // No need to acquire, since new Data object will have a refct of 1
432 return;
433 }
434
435 bool isNull() const { return _data == nullptr; }
436
437 bool isValid() const { return _data != nullptr; }
438
439 explicit operator bool() const { return _data != nullptr; }
440
441 bool operator==(const SharedPtr &other) const { return _data == other._data; }
442
443 bool operator!=(const SharedPtr &other) const { return _data != other._data; }
444
445 bool operator==(std::nullptr_t) const { return _data == nullptr; }
446
447 bool operator!=(std::nullptr_t) const { return _data != nullptr; }
448
449 int referenceCount() const { return _data == nullptr ? 0 : _data->_promeki_refct.value(); }
450
451 const T *ptr() const {
452 assert(_data != nullptr);
453 if constexpr (IsSharedObject<T>::value) {
454 return _data;
455 } else {
456 return _data->object();
457 }
458 }
459
460 T *modify() {
461 assert(_data != nullptr);
462 if constexpr (CopyOnWrite) detach();
463 if constexpr (IsSharedObject<T>::value) {
464 return _data;
465 } else {
466 return _data->object();
467 }
468 }
469
470 const T *operator->() const { return ptr(); }
471
472 const T &operator*() const { return *ptr(); }
473
474 private:
475 ST *_data = nullptr;
476
477 // This function assumes you've already assigned _data to the Data you'd like to acquire
478 void acquire() {
479 if (_data == nullptr) return;
480 _data->_promeki_refct.inc();
481 return;
482 }
483
484 // This function releases _data, and if the reference count has dropped to zero, deletes it
485 // however it does not change _data so it will be stale after calling this function.
486 // This assumes you know this and are about to change _data (i.e. in a swap situation)
487 void release() {
488 if (_data == nullptr) return;
489 if (_data->_promeki_refct.dec()) delete _data;
490 return;
491 }
492
493 constexpr void setData(T *obj) {
494 if constexpr (IsSharedObject<T>::value) {
495 _data = obj;
496 } else {
497 _data = new ST(obj);
498 }
499 return;
500 }
501};
502
521template <typename Derived, typename Base, bool CoW, typename ST>
522SharedPtr<Derived, CoW, Derived> sharedPointerCast(const SharedPtr<Base, CoW, ST> &sp) {
523 static_assert(IsSharedObject<Base>::value, "sharedPointerCast requires native shared objects");
524 static_assert(IsSharedObject<Derived>::value, "sharedPointerCast requires native shared objects");
525 static_assert(std::is_base_of_v<Base, Derived>, "Derived must publicly derive from Base");
526 SharedPtr<Derived, CoW, Derived> result;
527 if (sp.isNull()) return result;
528 Derived *d = dynamic_cast<Derived *>(sp._data);
529 if (d == nullptr) return result;
530 result._data = d;
531 result._data->_promeki_refct.inc();
532 return result;
533}
534
535PROMEKI_NAMESPACE_END
536
537#endif // PROMEKI_ENABLE_CORE