Ditto 4.7.0
Loading...
Searching...
No Matches
Arc.hpp
1#ifndef _ARC_
2#define _ARC_
3
4#include <atomic>
5#include <cassert>
6#include <cstddef>
7#include <utility>
8
9namespace ditto {
10
11template <typename T> struct ArcPointee {
12 T value;
13 mutable std::atomic<size_t> strong_count;
14
15 ArcPointee(T &&value, size_t strong_count)
16 : value /* = */ (std::move(value)), strong_count /* = */ (strong_count) {}
17};
18
19template <typename T> class Arc {
20public:
21 // Post-condition: refcount = 1.
22 Arc(T value) noexcept
23 : ptr /* = */ (new ArcPointee<T>(std::move(value), 1)) {}
24
25 // Post-condition: refcount ±= 0; schedules dtor glue to `-= 1`.
26 // There has to be a 1-to-1 relation between `into_raw()` and `from_raw()`.
27 static Arc<T> from_raw(void *ptr) noexcept {
28 return Arc(static_cast<ArcPointee<T> *>(ptr));
29 }
30
31 // The equivalent of boost's `detach()`.
32 // Post-condition: refcount ±= 0; disables the `-= 1` eventual dtor glue.
33 // There has to be a 1-to-1 relation between `into_raw()` and `from_raw()`.
34 static void *into_raw(Arc<T> &&self) noexcept {
35 auto ptr = self.ptr;
36 self.ptr = nullptr;
37 return static_cast<void *>(ptr);
38 }
39
40 // The equivalent of boost's `get()`.
41 // Post-condition: refcount ±= 0; dtor untouched.
42 static void *as_raw(Arc<T> const &arc) noexcept { return arc.ptr; }
43
44 // Borrow a `void *` as a `Arc<T>`, *temporarily*.
45 // (i.e., without touching the refcount nor setting up any ownership).
46 // - You could then use the copy (clone) ctor to get back an owned `Arc<T>`,
47 // via the retain therein.
48 // Post-condition: refcount ±= 0; dtor not enabled.
49 static Arc<T> &borrow_from_raw(void *&ptr) noexcept {
50 return *reinterpret_cast<Arc<T> *>(&ptr);
51 }
52
53 // Copy (clone) constructor
54 // Post-condition: refcount += 1; schedules new `-= 1` dtor glue.
55 Arc(Arc<T> const &other) noexcept : ptr /* = */ (other.ptr) {
56 ++(ptr->strong_count);
57 }
58
59 // Destructor / dtor.
60 // Post-condition: refcount = -1.
61 ~Arc() {
62 if (ptr != nullptr) { // <- if not moved from.
63 size_t count = --(ptr->strong_count);
64 if (count == 0) {
65 delete ptr;
66 }
67 }
68 }
69
70 // Move constructor
71 // Post-condition: refcount ±= 0 (and `rhs.ptr == nullptr`); moves dtor glue
72 // from `rhs` to `this`.
73 Arc(Arc<T> &&rhs) noexcept : ptr /* = */ (rhs.ptr) { rhs.ptr = nullptr; }
74
75 // Using `swap()`, we can use temporaries to derive the tricky assignments
76 // semantics off the above constructor & destructor operations, thanks to the
77 // C++ RAII semantics of the temporary. Post-condition: refcount ±= 0.
78 void swap(Arc<T> &rhs) noexcept {
80 rhs.ptr = this->ptr;
81 this->ptr = rhs_ptr;
82 }
83
84 // Copy (clone) assignment.
85 // Post-condition: rhs refcount += 1; orig `*this` destroyed; schedules new
86 // `-= 1` dtor glue.
87 Arc<T> &operator=(Arc<T> const &rhs) noexcept {
88 {
89 Arc temporary{rhs}; // copy ctor: +1 of rhs
90 temporary.swap(*this);
91 } // dtor: -1 on what used to be `*this`.
92 return *this;
93 }
94
95 // Move assignment
96 // Post-condition: rhs refcount ±= 0; `rhs.ptr == nullptr`; orig `*this`
97 // destroyed. Moved dtor.
98 Arc<T> &operator=(Arc<T> &&rhs) noexcept {
99 {
101 std::move(rhs)}; // move ctor: +0 of rhs; `rhs` is now `nullptr`.
102 temporary.swap(*this);
103 } // dtor: -1 on what used to be `*this`.
104 return *this;
105 }
106
107 // Deref
108 // Post-condition: rhs refcount ±= 0; dtor untouched.
109 T &operator*() const noexcept {
110 assert(ptr != nullptr);
111 return ptr->value;
112 }
113
114 // Post-condition: rhs refcount ±= 0; dtor untouched.
115 T *operator->() const noexcept {
116 assert(ptr != nullptr);
117 return &ptr->value;
118 }
119
120private:
121 ArcPointee<T> *ptr;
122
123 // from raw
124 Arc<T>(ArcPointee<T> *ptr) : ptr(ptr) {}
125};
126
127#define DEFINE_RETAIN_RELEASE_FOR(T, retain, release) \
128 extern "C" { \
129 /* Post-condition: refcount += 1; dtor untouched. */ \
130 static void retain(void *ptr) { \
131 ++(static_cast<ditto::ArcPointee<T> *>(ptr)->strong_count); \
132 } \
133 \
134 /* Post-condition: refcount -= 1; dtor untouched. */ \
135 static void release(void *ptr) { ditto::Arc<T>::from_raw(ptr); } \
136 } \
137 static_assert(true, \
138 "") // <- to require a semi-colon when the macro is invoked.
139
140#define DEFINE_RETAIN_RELEASE_ERASED_T_FOR(T, retain, release) \
141 extern "C" { \
142 /* Post-condition: refcount += 1; dtor untouched. */ \
143 static Erased *retain(Erased_t const *ptr) { \
144 ++(((ditto::ArcPointee<T> *)(ptr))->strong_count); \
145 return const_cast<Erased_t *>(ptr); \
146 } \
147 \
148 /* Post-condition: refcount -= 1; dtor untouched. */ \
149 static void release(Erased_t *ptr) { \
150 ditto::Arc<T>::from_raw(static_cast<void *>(ptr)); \
151 } \
152 } \
153 static_assert(true, \
154 "") // <- to require a semi-colon when the macro is invoked.
155
156} // namespace ditto
157
158#endif // _ARC_
Definition Arc.hpp:19
Definition Arc.hpp:11