22#define OBSERVABLE_UNIQUE_PTR_INCLUDED
33
44#include < memory>
5+ #include < cstddef>
56
67namespace oup {
78
8- // / Simple deleter, suitable for objects allocated with new.
9- struct default_deleter {
10- template <typename T>
11- void operator () (T* ptr) noexcept { delete ptr; }
12-
13- void operator () (std::nullptr_t ) noexcept {}
14- };
15-
169template <typename T>
1710class observer_ptr ;
1811
@@ -46,76 +39,90 @@ class observer_ptr;
4639* has_deleter() before calling get_deleter(), or use try_get_deleter().
4740* - a moved-from observable_unique_ptr will not own a deleter instance.
4841*/
49- template <typename T, typename Deleter = default_deleter >
50- class observable_unique_ptr : private std ::shared_ptr<T> {
42+ template <typename T, typename Deleter = std::default_delete<T> >
43+ class observable_unique_ptr {
5144private:
52- // / Construct from shared_ptr.
53- /* * \param value The shared_ptr to take ownership from
54- * \note This is private since the use of std::shared_ptr is
55- * an implementation detail.
56- */
57- explicit observable_unique_ptr (std::shared_ptr<T>&& value) noexcept :
58- std::shared_ptr<T>(std::move(value)) {}
59-
6045 // Friendship is required for conversions.
6146 template <typename U, typename D>
6247 friend class observable_unique_ptr ;
6348
64- // Friendship is required for access to std::shared_ptr base
65- template <typename U>
66- friend class observer_ptr ;
49+ struct control_block {
50+ std::size_t refcount = 0u ;
51+ bool placement_allocated = false ;
52+ };
6753
68- public:
69- // / Type of the pointed object
70- using typename std::shared_ptr<T>::element_type ;
54+ control_block* block = nullptr ;
55+ T* pointer = nullptr ;
56+ Deleter deleter ;
7157
72- // / Type of the matching observer pointer
73- using observer_type = observer_ptr<T>;
58+ control_block* allocate_block_ (T* value) {
59+ return new control_block{std::size_t {1u }};
60+ }
7461
75- // / Get a non-owning raw pointer to the pointed object, or nullptr if none.
76- /* * \return 'nullptr' if no object is owned, or the pointed object otherwise
77- */
78- using std::shared_ptr<T>::get;
62+ void pop_ref_ () noexcept {
63+ deleter (pointer);
7964
80- // / Get a reference to the pointed object (undefined behavior if nullptr).
81- /* * \return A reference to the pointed object
82- * \note Using this function if this pointer is null will leave to undefined behavior.
83- */
84- using std::shared_ptr<T>::operator *;
65+ --block->refcount ;
66+ if (block->refcount == 0u ) {
67+ if (block->placement_allocated ) {
68+ block->~control_block ();
69+ delete[] static_cast <std::byte*>(block);
70+ } else {
71+ delete block;
72+ }
73+ }
74+ }
8575
86- // / Get a non-owning raw pointer to the pointed object, or nullptr if none.
87- /* * \return 'nullptr' if no object is owned, or the pointed object otherwise
76+ // / Private constructor using pre-allocated control block.
77+ /* * \param ctrl The control block pointer
78+ * \param value The pointer to own
79+ * \note This is used by make_observable_unique().
8880 */
89- using std::shared_ptr<T>:: operator ->;
81+ observable_unique_ptr (control_block* ctrl, T* value) : block(ctrl), pointer(value) {}
9082
91- // / Check if this pointer points to a valid object.
92- /* * \return 'true' if the pointed object is valid, 'false' otherwise
83+ // / Private constructor using pre-allocated control block.
84+ /* * \param ctrl The control block pointer
85+ * \param value The pointer to own
86+ * \note This is used by make_observable_unique().
9387 */
94- using std::shared_ptr<T>::operator bool ;
88+ observable_unique_ptr (control_block* ctrl, T* value, Deleter del) :
89+ block (ctrl), pointer(value), deleter(del) {}
9590
96- // Define member types for compatibility with std::unique_ptr
91+ // Friendship is required for access to std::shared_ptr base
92+ // template<typename U>
93+ // friend class observer_ptr;
94+
95+ public:
96+ // / Type of the pointed object
97+ using element_type = T;
98+
99+ // / Type of the matching observer pointer
100+ using observer_type = observer_ptr<T>;
101+
102+ // / Pointer type
97103 using pointer = element_type*;
104+
105+ // / Deleter type
98106 using deleter_type = Deleter;
99107
100108 // / Default constructor (null pointer).
101- observable_unique_ptr () noexcept :
102- std::shared_ptr<T>(nullptr , Deleter{}) {}
109+ observable_unique_ptr () noexcept = default ;
103110
104111 // / Construct a null pointer.
105- observable_unique_ptr (std::nullptr_t ) noexcept :
106- std::shared_ptr<T>(nullptr , Deleter{}) {}
112+ observable_unique_ptr (std::nullptr_t ) noexcept {}
107113
108114 // / Construct a null pointer with custom deleter.
109115 observable_unique_ptr (std::nullptr_t , Deleter deleter) noexcept :
110- std::shared_ptr<T>( nullptr , std::move(deleter)) {}
116+ deleter ( std::move(deleter)) {}
111117
112118 // / Explicit ownership capture of a raw pointer.
113119 /* * \param value The raw pointer to take ownership of
114120 * \note Do *not* manually delete this raw pointer after the
115121 * observable_unique_ptr is created. If possible, prefer
116122 * using make_observable_unique() instead of this constructor.
117123 */
118- explicit observable_unique_ptr (T* value) : std::shared_ptr<T>(value, Deleter{}) {}
124+ explicit observable_unique_ptr (T* value) :
125+ observable_unique_ptr(allocate_block_(value), value) {}
119126
120127 // / Explicit ownership capture of a raw pointer, with customer deleter.
121128 /* * \param value The raw pointer to take ownership of
@@ -124,17 +131,20 @@ class observable_unique_ptr : private std::shared_ptr<T> {
124131 * observable_unique_ptr is created. If possible, prefer
125132 * using make_observable_unique() instead of this constructor.
126133 */
127- explicit observable_unique_ptr (T* value, Deleter deleter ) :
128- std::shared_ptr<T>( value, std::move(deleter )) {}
134+ explicit observable_unique_ptr (T* value, Deleter del ) :
135+ observable_unique_ptr(allocate_block_( value), value, std::move(del )) {}
129136
130137 // / Transfer ownership by implicit casting
131138 /* * \param value The pointer to take ownership from
132139 * \note After this observable_unique_ptr is created, the source
133140 * pointer is set to null and looses ownership.
134141 */
135- template <typename U>
142+ template <typename U/* , typename enable = std::enable_if_t<std::is_convertible_v<U, T>> */ >
136143 observable_unique_ptr (observable_unique_ptr<U,Deleter>&& value) noexcept :
137- std::shared_ptr<T>(std::move(static_cast <std::shared_ptr<U>&>(value))) {}
144+ observable_unique_ptr (value.block, value.pointer, std::move(value.deleter)) {
145+ value.block = nullptr ;
146+ value.pointer = nullptr ;
147+ }
138148
139149 // / Transfer ownership by explicit casting
140150 /* * \param manager The smart pointer to take ownership from
@@ -144,47 +154,42 @@ class observable_unique_ptr : private std::shared_ptr<T> {
144154 */
145155 template <typename U>
146156 observable_unique_ptr (observable_unique_ptr<U,Deleter>&& manager, T* value) noexcept :
147- std::shared_ptr<T>(std::move(manager), value) {
148- manager.std ::template shared_ptr<U>::reset ();
157+ observable_unique_ptr (manager.block, value, std::move(manager.deleter)) {
158+ manager.block = nullptr ;
159+ manager.pointer = nullptr ;
149160 }
150161
151162 // / Transfer ownership by implicit casting
152163 /* * \param value The pointer to take ownership from
153164 * \note After this observable_unique_ptr is created, the source
154165 * pointer is set to null and looses ownership.
155166 */
156- template <typename U>
167+ template <typename U/* , typename enable = std::enable_if_t<std::is_convertible_v<U, T>> */ >
157168 observable_unique_ptr& operator =(observable_unique_ptr<U,Deleter>&& value) noexcept {
158- std::shared_ptr<T>::operator =(std::move (static_cast <std::shared_ptr<U>&>(value)));
169+ if (pointer) {
170+ pop_ref_ ();
171+ }
172+
173+ block = value.block ;
174+ value.block = nullptr ;
175+ pointer = value.pointer ;
176+ value.pointer = nullptr ;
177+ deleter = std::move (value.deleter );
178+
159179 return *this ;
160180 }
161181
162- // Movable
163- observable_unique_ptr (observable_unique_ptr&&) noexcept = default ;
164- observable_unique_ptr& operator =(observable_unique_ptr&&) noexcept = default ;
165-
166182 // Non-copyable
167183 observable_unique_ptr (const observable_unique_ptr&) = delete ;
168184 observable_unique_ptr& operator =(const observable_unique_ptr&) = delete ;
169185
170- // / Checks if this pointer has a custom deleter.
171- /* * \return 'true' if a custom deleter is used, 'false' otherwise.
172- */
173- bool has_deleter () const noexcept {
174- if constexpr (std::is_same_v<Deleter, oup::default_deleter>) {
175- return false ;
176- } else {
177- return std::get_deleter<Deleter>(*this ) != nullptr ;
178- }
179- }
180-
181186 // / Returns the deleter object which would be used for destruction of the managed object.
182187 /* * \return The deleter
183188 * \note Using the return value of this function if has_deleter() returns 'false' will cause
184189 * undefined behavior.
185190 */
186191 Deleter& get_deleter () noexcept {
187- return *std::get_deleter<Deleter>(* this ) ;
192+ return deleter ;
188193 }
189194
190195 // / Returns the deleter object which would be used for destruction of the managed object.
@@ -193,58 +198,42 @@ class observable_unique_ptr : private std::shared_ptr<T> {
193198 * undefined behavior.
194199 */
195200 const Deleter& get_deleter () const noexcept {
196- return *std::get_deleter<Deleter>(*this );
197- }
198-
199- // / Returns the deleter object which would be used for destruction of the managed object.
200- /* * \return The deleter, or nullptr if no deleter exists
201- */
202- Deleter* try_get_deleter () noexcept {
203- return std::get_deleter<Deleter>(*this );
204- }
205-
206- // / Returns the deleter object which would be used for destruction of the managed object.
207- /* * \return The deleter, or nullptr if no deleter exists
208- */
209- const Deleter* try_get_deleter () const noexcept {
210- return std::get_deleter<Deleter>(*this );
201+ return deleter;
211202 }
212203
213204 // / Swap the content of this pointer with that of another pointer.
214205 /* * \param other The other pointer to swap with
215206 */
216207 void swap (observable_unique_ptr& other) noexcept {
217- std::shared_ptr<T>::swap (other);
208+ using std::swap;
209+ swap (block, other.block );
210+ swap (pointer, other.pointer );
211+ swap (deleter, other.deleter );
218212 }
219213
220214 // / Replaces the managed object with a null pointer.
221215 /* * \param ptr A nullptr_t instance
222216 */
223217 void reset (std::nullptr_t ptr = nullptr ) noexcept {
224- if constexpr (std::is_same_v<Deleter, oup::default_deleter>) {
225- operator =(observable_unique_ptr{ptr});
226- } else {
227- if (auto * deleter = try_get_deleter ()) {
228- operator =(observable_unique_ptr{ptr, Deleter{*deleter}});
229- } else {
230- operator =(observable_unique_ptr{ptr, Deleter{}});
231- }
218+ if (pointer) {
219+ pop_ref_ ();
220+ block = nullptr ;
221+ pointer = nullptr ;
232222 }
233223 }
234224
235225 // / Replaces the managed object.
236226 /* * \param ptr A nullptr_t instance
237227 */
238228 void reset (T* ptr) noexcept {
239- if constexpr (std::is_same_v<Deleter, oup::default_deleter>) {
240- operator =(observable_unique_ptr{ptr});
241- } else {
242- if (auto * deleter = try_get_deleter ()) {
243- operator =(observable_unique_ptr{ptr, Deleter{*deleter}});
244- } else {
245- operator =(observable_unique_ptr{ptr, Deleter{}});
246- }
229+ // TODO: copy old ptr, assign new, then delete old
230+ // (to follow std::unique_ptr specs)
231+ if (pointer) {
232+ pop_ref_ ();
247233 }
234+
235+ block = ptr != nullptr ? allocate_block_ () : nullptr ;
236+ pointer = ptr;
248237 }
249238
250239 // / Replaces the managed object (with custom deleter).
@@ -256,6 +245,43 @@ class observable_unique_ptr : private std::shared_ptr<T> {
256245 operator =(observable_unique_ptr{ptr, std::move (deleter)});
257246 }
258247
248+ // / Get a non-owning raw pointer to the pointed object, or nullptr if deleted.
249+ /* * \return 'nullptr' if expired() is 'true', or the pointed object otherwise
250+ * \note Contrary to std::weak_ptr::lock(), this does not extend the lifetime
251+ * of the pointed object. Therefore, when calling this function, you must
252+ * make sure that the owning observable_unique_ptr will not be reset until
253+ * you are done using the raw pointer.
254+ */
255+ T* get () const noexcept {
256+ return pointer;
257+ }
258+
259+ // / Get a reference to the pointed object (undefined behavior if deleted).
260+ /* * \return A reference to the pointed object
261+ * \note Using this function if expired() is 'true' will leave to undefined behavior.
262+ */
263+ T& operator *() const noexcept {
264+ return *pointer;
265+ }
266+
267+ // / Get a non-owning raw pointer to the pointed object, or nullptr if deleted.
268+ /* * \return 'nullptr' if expired() is 'true', or the pointed object otherwise
269+ * \note Contrary to std::weak_ptr::lock(), this does not extend the lifetime
270+ * of the pointed object. Therefore, when calling this function, you must
271+ * make sure that the owning observable_unique_ptr will not be reset until
272+ * you are done using the raw pointer.
273+ */
274+ T* operator ->() const noexcept {
275+ return pointer;
276+ }
277+
278+ // / Check if this pointer points to a valid object.
279+ /* * \return 'true' if the pointed object is valid, 'false' otherwise
280+ */
281+ explicit operator bool () noexcept {
282+ return pointer != nullptr ;
283+ }
284+
259285 template <typename U, typename ... Args>
260286 friend observable_unique_ptr<U> make_observable_unique (Args&& ... args);
261287};
@@ -266,7 +292,26 @@ class observable_unique_ptr : private std::shared_ptr<T> {
266292*/
267293template <typename T, typename ... Args>
268294observable_unique_ptr<T> make_observable_unique (Args&& ... args) {
269- return observable_unique_ptr<T>(std::make_shared<T>(std::forward<Args>(args)...));
295+ using block_type = typename observable_unique_ptr<T>::control_block;
296+
297+ // Allocate memory
298+ constexpr std::size_t block_size = sizeof (block_type);
299+ constexpr std::size_t object_size = sizeof (T);
300+ std::byte* buffer = new std::byte[block_size + object_size];
301+
302+ try {
303+ // Construct control block and object
304+ block_type* block = new (buffer) control_block{1u };
305+ T* ptr = new (buffer + block_size) T (std::forward<Args>(args)...);
306+
307+ // Make owner pointer
308+ return observable_unique_ptr<T>(block, ptr, deleter);
309+ } catch (...) {
310+ // Exception thrown durimg object construction,
311+ // clean up memory and let exception propagate
312+ delete[] buffer;
313+ throw ;
314+ }
270315}
271316
272317template <typename T, typename Deleter>
0 commit comments