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