Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
3 : //
4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 : //
7 : // Official repository: https://github.com/cppalliance/capy
8 : //
9 :
10 : #ifndef BOOST_CAPY_NEUNIQUE_PTR_HPP
11 : #define BOOST_CAPY_NEUNIQUE_PTR_HPP
12 :
13 : #include <boost/capy/detail/config.hpp>
14 : #include <boost/assert.hpp>
15 : #include <cstddef>
16 : #include <functional>
17 : #include <memory>
18 : #include <type_traits>
19 : #include <utility>
20 :
21 : namespace boost {
22 : namespace capy {
23 :
24 : namespace detail {
25 :
26 : //----------------------------------------------------------
27 :
28 : template<class U>
29 22 : auto try_delete(U* p, int) noexcept
30 : -> decltype(sizeof(U), void())
31 : {
32 22 : delete p;
33 22 : }
34 :
35 : template<class U>
36 0 : void try_delete(U*, long) noexcept
37 : {
38 : // Incomplete type - should never reach here
39 0 : std::terminate();
40 : }
41 :
42 : template<class U>
43 10 : auto try_delete_array(U* p, int) noexcept
44 : -> decltype(sizeof(U), void())
45 : {
46 10 : delete[] p;
47 10 : }
48 :
49 : template<class U>
50 : void try_delete_array(U*, long) noexcept
51 : {
52 : std::terminate();
53 : }
54 :
55 : /** Storage wrapper applying empty base optimization.
56 :
57 : When `T` is empty and non-final, inherits from it to
58 : apply EBO. Otherwise stores as a member.
59 : */
60 : #if __cplusplus >= 201402L
61 : template<
62 : class T,
63 : bool = std::is_empty<T>::value && !std::is_final<T>::value>
64 : #else
65 : template<
66 : class T,
67 : bool = std::is_empty<T>::value>
68 : #endif
69 : struct ebo_storage
70 : {
71 : T value_;
72 :
73 : ebo_storage() = default;
74 :
75 : explicit ebo_storage(T const& t)
76 : : value_(t)
77 : {
78 : }
79 :
80 4 : explicit ebo_storage(T&& t)
81 4 : : value_(std::move(t))
82 : {
83 4 : }
84 :
85 4 : T& get() noexcept { return value_; }
86 : T const& get() const noexcept { return value_; }
87 : };
88 :
89 : template<class T>
90 : struct ebo_storage<T, true> : T
91 : {
92 : ebo_storage() = default;
93 :
94 : explicit ebo_storage(T const& t)
95 : : T(t)
96 : {
97 : }
98 :
99 6 : explicit ebo_storage(T&& t)
100 6 : : T(std::move(t))
101 : {
102 6 : }
103 :
104 6 : T& get() noexcept { return *this; }
105 : T const& get() const noexcept { return *this; }
106 : };
107 :
108 : //----------------------------------------------------------
109 :
110 : /** RAII scope guard for rollback on exception.
111 : */
112 : template<class F>
113 : class scope_guard
114 : {
115 : F f_;
116 : bool active_ = true;
117 :
118 : public:
119 17 : explicit scope_guard(F f) noexcept
120 17 : : f_(std::move(f))
121 : {
122 17 : }
123 :
124 17 : ~scope_guard()
125 : {
126 17 : if(active_)
127 0 : f_();
128 17 : }
129 :
130 17 : void release() noexcept
131 : {
132 17 : active_ = false;
133 17 : }
134 :
135 : scope_guard(scope_guard&& other) noexcept
136 : : f_(std::move(other.f_))
137 : , active_(other.active_)
138 : {
139 : other.active_ = false;
140 : }
141 :
142 : scope_guard(scope_guard const&) = delete;
143 : scope_guard& operator=(scope_guard const&) = delete;
144 : scope_guard& operator=(scope_guard&&) = delete;
145 : };
146 :
147 : template<class F>
148 : scope_guard<F>
149 17 : make_scope_guard(F f) noexcept
150 : {
151 17 : return scope_guard<F>(std::move(f));
152 : }
153 :
154 : //----------------------------------------------------------
155 :
156 : /** Base class for type-erased control blocks.
157 :
158 : The control block stores the deleter and allocator,
159 : enabling type erasure and incomplete type support.
160 : */
161 : struct control_block_base
162 : {
163 : virtual void destroy_and_deallocate() noexcept = 0;
164 :
165 : protected:
166 : ~control_block_base() = default;
167 : };
168 :
169 : //----------------------------------------------------------
170 :
171 : /** Control block storing pointer, deleter, and allocator.
172 :
173 : Used when constructing from a raw pointer with a
174 : custom deleter and/or allocator.
175 : */
176 : template<class T, class D, class A>
177 : struct control_block_pda final
178 : : control_block_base
179 : , private ebo_storage<
180 : typename std::allocator_traits<A>::
181 : template rebind_alloc<control_block_pda<T, D, A>>>
182 : {
183 : using alloc_type = typename std::allocator_traits<A>::
184 : template rebind_alloc<control_block_pda>;
185 : using alloc_traits = std::allocator_traits<alloc_type>;
186 : using alloc_storage = ebo_storage<alloc_type>;
187 :
188 : T* ptr;
189 : D deleter;
190 :
191 7 : alloc_type& get_alloc() noexcept
192 : {
193 7 : return alloc_storage::get();
194 : }
195 :
196 7 : control_block_pda(T* p, D d, A const& a)
197 9 : : alloc_storage(alloc_type(a))
198 7 : , ptr(p)
199 17 : , deleter(std::move(d))
200 : {
201 7 : }
202 :
203 7 : void destroy_and_deallocate() noexcept override
204 : {
205 7 : T* p = ptr;
206 7 : D del = std::move(deleter);
207 7 : alloc_type a = std::move(get_alloc());
208 7 : this->~control_block_pda();
209 2 : alloc_traits::deallocate(a, this, 1);
210 7 : if(p)
211 7 : del(p);
212 7 : }
213 : };
214 :
215 : //----------------------------------------------------------
216 :
217 : /** Control block with embedded object storage.
218 :
219 : Used by allocate_neunique to store the object
220 : inline with the control block.
221 : */
222 : template<class T, class A>
223 : struct control_block_embedded final
224 : : control_block_base
225 : , private ebo_storage<
226 : typename std::allocator_traits<A>::
227 : template rebind_alloc<control_block_embedded<T, A>>>
228 : {
229 : using alloc_type = typename std::allocator_traits<A>::
230 : template rebind_alloc<control_block_embedded>;
231 : using alloc_traits = std::allocator_traits<alloc_type>;
232 : using alloc_storage = ebo_storage<alloc_type>;
233 :
234 : alignas(T) unsigned char storage[sizeof(T)];
235 :
236 2 : alloc_type& get_alloc() noexcept
237 : {
238 2 : return alloc_storage::get();
239 : }
240 :
241 4 : T* get() noexcept
242 : {
243 4 : return reinterpret_cast<T*>(storage);
244 : }
245 :
246 : template<class... Args>
247 2 : explicit control_block_embedded(A const& a, Args&&... args)
248 3 : : alloc_storage(alloc_type(a))
249 : {
250 3 : ::new(static_cast<void*>(storage)) T(
251 2 : std::forward<Args>(args)...);
252 2 : }
253 :
254 2 : void destroy_and_deallocate() noexcept override
255 : {
256 2 : get()->~T();
257 2 : alloc_type a = std::move(get_alloc());
258 2 : this->~control_block_embedded();
259 1 : alloc_traits::deallocate(a, this, 1);
260 2 : }
261 : };
262 :
263 : //----------------------------------------------------------
264 :
265 : /** Control block for arrays with embedded storage.
266 :
267 : Used by allocate_neunique for array types.
268 : */
269 : template<class T, class A>
270 : struct control_block_array final
271 : : control_block_base
272 : , private ebo_storage<
273 : typename std::allocator_traits<A>::
274 : template rebind_alloc<control_block_array<T, A>>>
275 : {
276 : using alloc_type = typename std::allocator_traits<A>::
277 : template rebind_alloc<control_block_array>;
278 : using alloc_traits = std::allocator_traits<alloc_type>;
279 : using alloc_storage = ebo_storage<alloc_type>;
280 :
281 : std::size_t size;
282 : // Flexible array member follows
283 :
284 1 : explicit control_block_array(A const& a)
285 2 : : alloc_storage(alloc_type(a))
286 1 : , size(0)
287 : {
288 1 : }
289 :
290 1 : alloc_type& get_alloc() noexcept
291 : {
292 1 : return alloc_storage::get();
293 : }
294 :
295 2 : T* get() noexcept
296 : {
297 : return reinterpret_cast<T*>(
298 : reinterpret_cast<unsigned char*>(this) +
299 2 : sizeof(control_block_array));
300 : }
301 :
302 2 : static std::size_t storage_size(std::size_t n) noexcept
303 : {
304 2 : return sizeof(control_block_array) + sizeof(T) * n;
305 : }
306 :
307 1 : void destroy_and_deallocate() noexcept override
308 : {
309 1 : T* p = get();
310 4 : for(std::size_t i = size; i > 0; --i)
311 3 : p[i - 1].~T();
312 1 : alloc_type a = std::move(get_alloc());
313 1 : std::size_t sz = size;
314 1 : this->~control_block_array();
315 1 : std::size_t units = (storage_size(sz) +
316 1 : sizeof(control_block_array) - 1) /
317 : sizeof(control_block_array);
318 1 : alloc_traits::deallocate(a,
319 : reinterpret_cast<control_block_array*>(this), units);
320 1 : }
321 : };
322 :
323 : } // detail
324 :
325 : //----------------------------------------------------------
326 :
327 : template<class T>
328 : class neunique_ptr;
329 :
330 : template<class T, class A, class... Args>
331 : typename std::enable_if<
332 : !std::is_array<T>::value,
333 : neunique_ptr<T>>::type
334 : allocate_neunique(A const& a, Args&&... args);
335 :
336 : template<class T, class A>
337 : typename std::enable_if<
338 : std::is_array<T>::value && std::extent<T>::value == 0,
339 : neunique_ptr<T>>::type
340 : allocate_neunique(A const& a, std::size_t n);
341 :
342 : //----------------------------------------------------------
343 :
344 : /** A smart pointer with unique ownership, type-erased deleter,
345 : and allocator support.
346 :
347 : This class provides unique ownership semantics similar to
348 : `std::unique_ptr`, combined with features from `std::shared_ptr`:
349 :
350 : @li Type-erased deleters - pointers with different deleters
351 : share the same static type
352 : @li Allocator support - custom allocators for the control block
353 : @li Incomplete types - the destructor is captured at construction
354 : @li Aliasing - the stored pointer can differ from the owned pointer
355 :
356 : The implementation uses a control block to store the deleter
357 : and allocator, similar to `std::shared_ptr` but without
358 : reference counting.
359 :
360 : @par Control Block Elision
361 :
362 : When constructed from a raw pointer without a custom deleter
363 : or allocator, no control block is allocated. The pointer is
364 : deleted directly using `delete`. This optimization requires
365 : the type to be complete at destruction time.
366 :
367 : @par Size
368 :
369 : `sizeof(neunique_ptr<T>)` is two pointers (16 bytes on 64-bit).
370 :
371 : @par Thread Safety
372 :
373 : Distinct `neunique_ptr` objects may be accessed concurrently.
374 : A single `neunique_ptr` object may not be accessed concurrently
375 : from multiple threads.
376 :
377 : @tparam T The element type. May be incomplete at declaration.
378 : For arrays, use `neunique_ptr<T[]>`.
379 :
380 : @see make_neunique, allocate_neunique
381 : */
382 : template<class T>
383 : class neunique_ptr
384 : {
385 : template<class U> friend class neunique_ptr;
386 :
387 : template<class U, class A, class... Args>
388 : friend typename std::enable_if<
389 : !std::is_array<U>::value,
390 : neunique_ptr<U>>::type
391 : allocate_neunique(A const& a, Args&&... args);
392 :
393 : using control_block = detail::control_block_base;
394 :
395 : T* ptr_ = nullptr;
396 : control_block* cb_ = nullptr;
397 :
398 : template<class D, class A>
399 6 : void init_pda(T* p, D d, A const& a)
400 : {
401 : using cb_type = detail::control_block_pda<T, D, A>;
402 : using alloc_type = typename cb_type::alloc_type;
403 : using alloc_traits = std::allocator_traits<alloc_type>;
404 :
405 2 : alloc_type alloc(a);
406 6 : cb_type* cb = alloc_traits::allocate(alloc, 1);
407 6 : auto guard = detail::make_scope_guard(
408 0 : [&]{ alloc_traits::deallocate(alloc, cb, 1); });
409 6 : ::new(static_cast<void*>(cb)) cb_type(p, std::move(d), a);
410 6 : guard.release();
411 6 : ptr_ = p;
412 6 : cb_ = cb;
413 10 : }
414 :
415 : public:
416 : /** The pointer type.
417 : */
418 : using pointer = T*;
419 :
420 : /** The element type.
421 : */
422 : using element_type = T;
423 :
424 : //------------------------------------------------------
425 : //
426 : // Constructors
427 : //
428 : //------------------------------------------------------
429 :
430 : /** Construct an empty pointer.
431 :
432 : @post `get() == nullptr`
433 : */
434 : constexpr neunique_ptr() noexcept = default;
435 :
436 : /** Construct an empty pointer from nullptr.
437 :
438 : @post `get() == nullptr`
439 : */
440 : constexpr neunique_ptr(std::nullptr_t) noexcept {}
441 :
442 : /** Construct from a raw pointer.
443 :
444 : Takes ownership of `p` using `delete`. No control
445 : block is allocated. The type must be complete at
446 : destruction time.
447 :
448 : @param p Pointer to take ownership of, or nullptr.
449 :
450 : @post `get() == p`
451 : */
452 21 : explicit neunique_ptr(pointer p) noexcept
453 21 : : ptr_(p)
454 : {
455 21 : }
456 :
457 : /** Construct from a raw pointer with custom deleter.
458 :
459 : Takes ownership of `p` using the specified deleter
460 : and the default allocator.
461 :
462 : @param p Pointer to take ownership of, or nullptr.
463 : @param d Deleter callable as `d(p)`.
464 :
465 : @throws std::bad_alloc if control block allocation fails.
466 : If an exception is thrown, `d(p)` is called.
467 :
468 : @post `get() == p`
469 : */
470 : template<class D>
471 4 : neunique_ptr(pointer p, D d)
472 4 : : neunique_ptr(p, std::move(d), std::allocator<T>{})
473 : {
474 4 : }
475 :
476 : /** Construct from a raw pointer with custom deleter and allocator.
477 :
478 : Takes ownership of `p` using the specified deleter.
479 : The allocator is used to allocate the control block.
480 :
481 : @param p Pointer to take ownership of, or nullptr.
482 : @param d Deleter callable as `d(p)`.
483 : @param a Allocator for control block allocation.
484 :
485 : @throws Any exception thrown by control block allocation.
486 : If an exception is thrown, `d(p)` is called.
487 :
488 : @post `get() == p`
489 : */
490 : template<class D, class A>
491 6 : neunique_ptr(pointer p, D d, A const& a)
492 6 : {
493 6 : if(!p)
494 0 : return;
495 6 : auto guard = detail::make_scope_guard(
496 0 : [&]{ d(p); });
497 6 : init_pda(p, std::move(d), a);
498 6 : guard.release();
499 6 : }
500 :
501 : /** Aliasing constructor.
502 :
503 : Constructs a `neunique_ptr` that stores `p` but shares
504 : ownership with `other`. After construction, `get() == p`
505 : and `other` is empty.
506 :
507 : This allows a `neunique_ptr` to point to a subobject
508 : of the owned object.
509 :
510 : @note If `other` has no control block (was constructed
511 : from a raw pointer without a deleter), the behavior
512 : is undefined unless `p` equals `other.get()`.
513 :
514 : @param other Pointer to transfer ownership from.
515 : @param p Pointer to store (typically to a subobject
516 : of the object owned by `other`).
517 :
518 : @post `get() == p`
519 : @post `other.get() == nullptr`
520 : */
521 : template<class U>
522 1 : neunique_ptr(neunique_ptr<U>&& other, pointer p) noexcept
523 1 : : ptr_(p)
524 1 : , cb_(other.cb_)
525 : {
526 : // aliasing requires control block; use allocate_neunique
527 1 : BOOST_ASSERT((other.cb_ != nullptr || other.ptr_ == nullptr));
528 1 : other.ptr_ = nullptr;
529 1 : other.cb_ = nullptr;
530 1 : }
531 : /** Move constructor.
532 :
533 : Takes ownership from `other`. After construction,
534 : `other` is empty.
535 :
536 : @param other Pointer to move from.
537 :
538 : @post `other.get() == nullptr`
539 : */
540 2 : neunique_ptr(neunique_ptr&& other) noexcept
541 2 : : ptr_(other.ptr_)
542 2 : , cb_(other.cb_)
543 : {
544 2 : other.ptr_ = nullptr;
545 2 : other.cb_ = nullptr;
546 2 : }
547 :
548 : /** Converting move constructor.
549 :
550 : Takes ownership from `other`. After construction,
551 : `other` is empty. Participates in overload resolution
552 : only if `U*` is convertible to `T*`.
553 :
554 : @param other Pointer to move from.
555 :
556 : @post `other.get() == nullptr`
557 : */
558 : template<class U, class = typename std::enable_if<
559 : std::is_convertible<U*, T*>::value>::type>
560 1 : neunique_ptr(neunique_ptr<U>&& other) noexcept
561 1 : : ptr_(other.ptr_)
562 1 : , cb_(other.cb_)
563 : {
564 1 : other.ptr_ = nullptr;
565 1 : other.cb_ = nullptr;
566 1 : }
567 :
568 : neunique_ptr(neunique_ptr const&) = delete;
569 : neunique_ptr& operator=(neunique_ptr const&) = delete;
570 :
571 : //------------------------------------------------------
572 : //
573 : // Destructor
574 : //
575 : //------------------------------------------------------
576 :
577 : /** Destructor.
578 :
579 : Destroys the owned object using the stored deleter
580 : and deallocates the control block using the stored
581 : allocator. If no control block exists, uses `delete`.
582 : */
583 41 : ~neunique_ptr()
584 : {
585 41 : if(cb_)
586 6 : cb_->destroy_and_deallocate();
587 35 : else if(ptr_)
588 19 : detail::try_delete(ptr_, 0);
589 41 : }
590 :
591 : //------------------------------------------------------
592 : //
593 : // Assignment
594 : //
595 : //------------------------------------------------------
596 :
597 : /** Move assignment.
598 :
599 : Releases the currently owned object and takes
600 : ownership from `other`.
601 :
602 : @param other Pointer to move from.
603 :
604 : @return `*this`
605 :
606 : @post `other.get() == nullptr`
607 : */
608 2 : neunique_ptr& operator=(neunique_ptr&& other) noexcept
609 : {
610 2 : if(this != &other)
611 : {
612 1 : if(cb_)
613 0 : cb_->destroy_and_deallocate();
614 1 : else if(ptr_)
615 1 : detail::try_delete(ptr_, 0);
616 1 : ptr_ = other.ptr_;
617 1 : cb_ = other.cb_;
618 1 : other.ptr_ = nullptr;
619 1 : other.cb_ = nullptr;
620 : }
621 2 : return *this;
622 : }
623 :
624 : /** Converting move assignment.
625 :
626 : Releases the currently owned object and takes
627 : ownership from `other`. Participates in overload
628 : resolution only if `U*` is convertible to `T*`.
629 :
630 : @param other Pointer to move from.
631 :
632 : @return `*this`
633 :
634 : @post `other.get() == nullptr`
635 : */
636 : template<class U, class = typename std::enable_if<
637 : std::is_convertible<U*, T*>::value>::type>
638 1 : neunique_ptr& operator=(neunique_ptr<U>&& other) noexcept
639 : {
640 1 : if(cb_)
641 0 : cb_->destroy_and_deallocate();
642 1 : else if(ptr_)
643 0 : delete ptr_;
644 1 : ptr_ = other.ptr_;
645 1 : cb_ = other.cb_;
646 1 : other.ptr_ = nullptr;
647 1 : other.cb_ = nullptr;
648 1 : return *this;
649 : }
650 :
651 : /** Assign nullptr.
652 :
653 : Releases the currently owned object.
654 :
655 : @return `*this`
656 :
657 : @post `get() == nullptr`
658 : */
659 1 : neunique_ptr& operator=(std::nullptr_t) noexcept
660 : {
661 1 : reset();
662 1 : return *this;
663 : }
664 :
665 : //------------------------------------------------------
666 : //
667 : // Modifiers
668 : //
669 : //------------------------------------------------------
670 :
671 : /** Replace the owned object.
672 :
673 : Releases the currently owned object and takes
674 : ownership of `p` using `delete`. No control block
675 : is allocated.
676 :
677 : @param p Pointer to take ownership of, or nullptr.
678 :
679 : @post `get() == p`
680 : */
681 5 : void reset(pointer p = nullptr) noexcept
682 : {
683 5 : if(cb_)
684 2 : cb_->destroy_and_deallocate();
685 3 : else if(ptr_)
686 2 : detail::try_delete(ptr_, 0);
687 5 : ptr_ = p;
688 5 : cb_ = nullptr;
689 5 : }
690 :
691 : /** Replace the owned object with custom deleter.
692 :
693 : Releases the currently owned object and takes
694 : ownership of `p` using the specified deleter.
695 :
696 : @param p Pointer to take ownership of, or nullptr.
697 : @param d Deleter callable as `d(p)`.
698 :
699 : @throws Any exception thrown by control block allocation.
700 :
701 : @post `get() == p`
702 : */
703 : template<class D>
704 1 : void reset(pointer p, D d)
705 : {
706 1 : neunique_ptr(p, std::move(d)).swap(*this);
707 1 : }
708 :
709 : /** Replace the owned object with custom deleter and allocator.
710 :
711 : Releases the currently owned object and takes
712 : ownership of `p` using the specified deleter and
713 : allocator.
714 :
715 : @param p Pointer to take ownership of, or nullptr.
716 : @param d Deleter callable as `d(p)`.
717 : @param a Allocator for control block allocation.
718 :
719 : @throws Any exception thrown by control block allocation.
720 :
721 : @post `get() == p`
722 : */
723 : template<class D, class A>
724 1 : void reset(pointer p, D d, A const& a)
725 : {
726 1 : neunique_ptr(p, std::move(d), a).swap(*this);
727 1 : }
728 :
729 : /** Swap with another pointer.
730 :
731 : @param other Pointer to swap with.
732 : */
733 4 : void swap(neunique_ptr& other) noexcept
734 : {
735 4 : std::swap(ptr_, other.ptr_);
736 4 : std::swap(cb_, other.cb_);
737 4 : }
738 :
739 : //------------------------------------------------------
740 : //
741 : // Observers
742 : //
743 : //------------------------------------------------------
744 :
745 : /** Return the stored pointer.
746 :
747 : @return The stored pointer, or nullptr if empty.
748 : */
749 43 : pointer get() const noexcept
750 : {
751 43 : return ptr_;
752 : }
753 :
754 : /** Check if non-empty.
755 :
756 : @return `true` if `get() != nullptr`.
757 : */
758 35 : explicit operator bool() const noexcept
759 : {
760 35 : return ptr_ != nullptr;
761 : }
762 :
763 : /** Dereference the pointer.
764 :
765 : @pre `get() != nullptr`
766 :
767 : @return Reference to the pointed-to object.
768 : */
769 : typename std::add_lvalue_reference<T>::type
770 15 : operator*() const noexcept
771 : {
772 15 : return *ptr_;
773 : }
774 :
775 : /** Member access.
776 :
777 : @pre `get() != nullptr`
778 :
779 : @return The stored pointer.
780 : */
781 6 : pointer operator->() const noexcept
782 : {
783 6 : return ptr_;
784 : }
785 : };
786 :
787 : //----------------------------------------------------------
788 :
789 : /** A smart pointer with unique ownership for arrays.
790 :
791 : Array specialization of @ref neunique_ptr. Provides
792 : `operator[]` instead of `operator*` and `operator->`.
793 :
794 : @tparam T The element type (without `[]`).
795 :
796 : @see neunique_ptr, make_neunique, allocate_neunique
797 : */
798 : template<class T>
799 : class neunique_ptr<T[]>
800 : {
801 : template<class U> friend class neunique_ptr;
802 :
803 : template<class U, class A>
804 : friend typename std::enable_if<
805 : std::is_array<U>::value && std::extent<U>::value == 0,
806 : neunique_ptr<U>>::type
807 : allocate_neunique(A const& a, std::size_t n);
808 :
809 : using control_block = detail::control_block_base;
810 :
811 : T* ptr_ = nullptr;
812 : control_block* cb_ = nullptr;
813 :
814 : template<class D, class A>
815 1 : void init_pda(T* p, D d, A const& a)
816 : {
817 : using cb_type = detail::control_block_pda<T, D, A>;
818 : using alloc_type = typename cb_type::alloc_type;
819 : using alloc_traits = std::allocator_traits<alloc_type>;
820 :
821 : alloc_type alloc(a);
822 1 : cb_type* cb = alloc_traits::allocate(alloc, 1);
823 1 : auto guard = detail::make_scope_guard(
824 0 : [&]{ alloc_traits::deallocate(alloc, cb, 1); });
825 1 : ::new(static_cast<void*>(cb)) cb_type(p, std::move(d), a);
826 1 : guard.release();
827 1 : ptr_ = p;
828 1 : cb_ = cb;
829 2 : }
830 :
831 : public:
832 : /** The pointer type.
833 : */
834 : using pointer = T*;
835 :
836 : /** The element type.
837 : */
838 : using element_type = T;
839 :
840 : //------------------------------------------------------
841 : //
842 : // Constructors
843 : //
844 : //------------------------------------------------------
845 :
846 : /** Construct an empty pointer.
847 :
848 : @post `get() == nullptr`
849 : */
850 : constexpr neunique_ptr() noexcept = default;
851 :
852 : /** Construct an empty pointer from nullptr.
853 :
854 : @post `get() == nullptr`
855 : */
856 : constexpr neunique_ptr(std::nullptr_t) noexcept {}
857 :
858 : /** Construct from a raw pointer.
859 :
860 : Takes ownership of `p` using `delete[]`. No control
861 : block is allocated. The type must be complete at
862 : destruction time.
863 :
864 : @param p Pointer to take ownership of, or nullptr.
865 :
866 : @post `get() == p`
867 : */
868 : template<class U, class = typename std::enable_if<
869 : std::is_same<U, T*>::value ||
870 : std::is_same<U, std::nullptr_t>::value>::type>
871 9 : explicit neunique_ptr(U p) noexcept
872 9 : : ptr_(p)
873 : {
874 9 : }
875 :
876 : /** Construct from a raw pointer with custom deleter.
877 :
878 : Takes ownership of `p` using the specified deleter
879 : and the default allocator.
880 :
881 : @param p Pointer to take ownership of, or nullptr.
882 : @param d Deleter callable as `d(p)`.
883 :
884 : @throws std::bad_alloc if control block allocation fails.
885 : If an exception is thrown, `d(p)` is called.
886 :
887 : @post `get() == p`
888 : */
889 : template<class D>
890 1 : neunique_ptr(pointer p, D d)
891 1 : : neunique_ptr(p, std::move(d), std::allocator<T>{})
892 : {
893 1 : }
894 :
895 : /** Construct from a raw pointer with custom deleter and allocator.
896 :
897 : Takes ownership of `p` using the specified deleter.
898 : The allocator is used to allocate the control block.
899 :
900 : @param p Pointer to take ownership of, or nullptr.
901 : @param d Deleter callable as `d(p)`.
902 : @param a Allocator for control block allocation.
903 :
904 : @throws Any exception thrown by control block allocation.
905 : If an exception is thrown, `d(p)` is called.
906 :
907 : @post `get() == p`
908 : */
909 : template<class D, class A>
910 1 : neunique_ptr(pointer p, D d, A const& a)
911 1 : {
912 1 : if(!p)
913 0 : return;
914 1 : auto guard = detail::make_scope_guard(
915 0 : [&]{ d(p); });
916 1 : init_pda(p, std::move(d), a);
917 1 : guard.release();
918 1 : }
919 :
920 : /** Aliasing constructor.
921 :
922 : Constructs a `neunique_ptr` that stores `p` but shares
923 : ownership with `other`. After construction, `get() == p`
924 : and `other` is empty.
925 :
926 : @note If `other` has no control block (was constructed
927 : from a raw pointer without a deleter), the behavior
928 : is undefined unless `p` equals `other.get()`.
929 :
930 : @param other Pointer to transfer ownership from.
931 : @param p Pointer to store.
932 :
933 : @post `get() == p`
934 : @post `other.get() == nullptr`
935 : */
936 : template<class U>
937 : neunique_ptr(neunique_ptr<U>&& other, pointer p) noexcept
938 : : ptr_(p)
939 : , cb_(other.cb_)
940 : {
941 : // aliasing requires control block; use allocate_neunique
942 : BOOST_ASSERT((other.cb_ != nullptr || other.ptr_ == nullptr));
943 : other.ptr_ = nullptr;
944 : other.cb_ = nullptr;
945 : }
946 :
947 : /** Move constructor.
948 :
949 : Takes ownership from `other`. After construction,
950 : `other` is empty.
951 :
952 : @param other Pointer to move from.
953 :
954 : @post `other.get() == nullptr`
955 : */
956 1 : neunique_ptr(neunique_ptr&& other) noexcept
957 1 : : ptr_(other.ptr_)
958 1 : , cb_(other.cb_)
959 : {
960 1 : other.ptr_ = nullptr;
961 1 : other.cb_ = nullptr;
962 1 : }
963 :
964 : neunique_ptr(neunique_ptr const&) = delete;
965 : neunique_ptr& operator=(neunique_ptr const&) = delete;
966 :
967 : //------------------------------------------------------
968 : //
969 : // Destructor
970 : //
971 : //------------------------------------------------------
972 :
973 : /** Destructor.
974 :
975 : Destroys the owned array using the stored deleter
976 : and deallocates the control block. If no control
977 : block exists, uses `delete[]`.
978 : */
979 15 : ~neunique_ptr()
980 : {
981 15 : if(cb_)
982 2 : cb_->destroy_and_deallocate();
983 13 : else if(ptr_)
984 8 : detail::try_delete_array(ptr_, 0);
985 15 : }
986 :
987 : //------------------------------------------------------
988 : //
989 : // Assignment
990 : //
991 : //------------------------------------------------------
992 :
993 : /** Move assignment.
994 :
995 : Releases the currently owned array and takes
996 : ownership from `other`.
997 :
998 : @param other Pointer to move from.
999 :
1000 : @return `*this`
1001 :
1002 : @post `other.get() == nullptr`
1003 : */
1004 1 : neunique_ptr& operator=(neunique_ptr&& other) noexcept
1005 : {
1006 1 : if(this != &other)
1007 : {
1008 1 : if(cb_)
1009 0 : cb_->destroy_and_deallocate();
1010 1 : else if(ptr_)
1011 1 : detail::try_delete_array(ptr_, 0);
1012 1 : ptr_ = other.ptr_;
1013 1 : cb_ = other.cb_;
1014 1 : other.ptr_ = nullptr;
1015 1 : other.cb_ = nullptr;
1016 : }
1017 1 : return *this;
1018 : }
1019 :
1020 : /** Assign nullptr.
1021 :
1022 : Releases the currently owned array.
1023 :
1024 : @return `*this`
1025 :
1026 : @post `get() == nullptr`
1027 : */
1028 : neunique_ptr& operator=(std::nullptr_t) noexcept
1029 : {
1030 : reset();
1031 : return *this;
1032 : }
1033 :
1034 : //------------------------------------------------------
1035 : //
1036 : // Modifiers
1037 : //
1038 : //------------------------------------------------------
1039 :
1040 : /** Replace the owned array.
1041 :
1042 : Releases the currently owned array.
1043 :
1044 : @post `get() == nullptr`
1045 : */
1046 1 : void reset() noexcept
1047 : {
1048 1 : if(cb_)
1049 0 : cb_->destroy_and_deallocate();
1050 1 : else if(ptr_)
1051 1 : detail::try_delete_array(ptr_, 0);
1052 1 : ptr_ = nullptr;
1053 1 : cb_ = nullptr;
1054 1 : }
1055 :
1056 : /** Replace the owned array.
1057 :
1058 : Releases the currently owned array and takes
1059 : ownership of `p` using `delete[]`. No control
1060 : block is allocated.
1061 :
1062 : @param p Pointer to take ownership of, or nullptr.
1063 :
1064 : @post `get() == p`
1065 : */
1066 : template<class U, class = typename std::enable_if<
1067 : std::is_same<U, T*>::value ||
1068 : std::is_same<U, std::nullptr_t>::value>::type>
1069 1 : void reset(U p) noexcept
1070 : {
1071 1 : if(cb_)
1072 0 : cb_->destroy_and_deallocate();
1073 1 : else if(ptr_)
1074 0 : delete[] ptr_;
1075 1 : ptr_ = p;
1076 1 : cb_ = nullptr;
1077 1 : }
1078 :
1079 : /** Replace the owned array with custom deleter.
1080 :
1081 : Releases the currently owned array and takes
1082 : ownership of `p` using the specified deleter.
1083 :
1084 : @param p Pointer to take ownership of, or nullptr.
1085 : @param d Deleter callable as `d(p)`.
1086 :
1087 : @throws Any exception thrown by control block allocation.
1088 :
1089 : @post `get() == p`
1090 : */
1091 : template<class D>
1092 : void reset(pointer p, D d)
1093 : {
1094 : neunique_ptr(p, std::move(d)).swap(*this);
1095 : }
1096 :
1097 : /** Replace the owned array with custom deleter and allocator.
1098 :
1099 : Releases the currently owned array and takes
1100 : ownership of `p` using the specified deleter.
1101 :
1102 : @param p Pointer to take ownership of, or nullptr.
1103 : @param d Deleter callable as `d(p)`.
1104 : @param a Allocator for control block allocation.
1105 :
1106 : @throws Any exception thrown by control block allocation.
1107 :
1108 : @post `get() == p`
1109 : */
1110 : template<class D, class A>
1111 : void reset(pointer p, D d, A const& a)
1112 : {
1113 : neunique_ptr(p, std::move(d), a).swap(*this);
1114 : }
1115 :
1116 : /** Swap with another pointer.
1117 :
1118 : @param other Pointer to swap with.
1119 : */
1120 1 : void swap(neunique_ptr& other) noexcept
1121 : {
1122 1 : std::swap(ptr_, other.ptr_);
1123 1 : std::swap(cb_, other.cb_);
1124 1 : }
1125 :
1126 : //------------------------------------------------------
1127 : //
1128 : // Observers
1129 : //
1130 : //------------------------------------------------------
1131 :
1132 : /** Return the stored pointer.
1133 :
1134 : @return The stored pointer, or nullptr if empty.
1135 : */
1136 12 : pointer get() const noexcept
1137 : {
1138 12 : return ptr_;
1139 : }
1140 :
1141 : /** Check if non-empty.
1142 :
1143 : @return `true` if `get() != nullptr`.
1144 : */
1145 15 : explicit operator bool() const noexcept
1146 : {
1147 15 : return ptr_ != nullptr;
1148 : }
1149 :
1150 : /** Access array element.
1151 :
1152 : @param i Index of element to access.
1153 :
1154 : @pre `get() != nullptr`
1155 : @pre `i` is within bounds.
1156 :
1157 : @return Reference to element at index `i`.
1158 : */
1159 24 : T& operator[](std::size_t i) const noexcept
1160 : {
1161 24 : return ptr_[i];
1162 : }
1163 : };
1164 :
1165 : //----------------------------------------------------------
1166 : //
1167 : // Free functions
1168 : //
1169 : //----------------------------------------------------------
1170 :
1171 : /** Create a neunique_ptr for a single object.
1172 :
1173 : Allocates and constructs an object of type `T` using
1174 : `new`. No control block is allocated.
1175 :
1176 : @param args Arguments forwarded to `T`'s constructor.
1177 :
1178 : @return A `neunique_ptr<T>` owning the new object.
1179 :
1180 : @throws std::bad_alloc if allocation fails.
1181 : @throws Any exception thrown by `T`'s constructor.
1182 : */
1183 : template<class T, class... Args>
1184 : typename std::enable_if<
1185 : !std::is_array<T>::value,
1186 : neunique_ptr<T>>::type
1187 2 : make_neunique(Args&&... args)
1188 : {
1189 2 : return neunique_ptr<T>(new T(std::forward<Args>(args)...));
1190 : }
1191 :
1192 : /** Create a neunique_ptr for an array.
1193 :
1194 : Allocates an array of `n` value-initialized elements
1195 : using `new[]`. No control block is allocated.
1196 :
1197 : @param n Number of elements.
1198 :
1199 : @return A `neunique_ptr<T[]>` owning the new array.
1200 :
1201 : @throws std::bad_alloc if allocation fails.
1202 : */
1203 : template<class T>
1204 : typename std::enable_if<
1205 : std::is_array<T>::value && std::extent<T>::value == 0,
1206 : neunique_ptr<T>>::type
1207 1 : make_neunique(std::size_t n)
1208 : {
1209 : using U = typename std::remove_extent<T>::type;
1210 6 : return neunique_ptr<T>(new U[n]());
1211 : }
1212 :
1213 : template<class T, class... Args>
1214 : typename std::enable_if<
1215 : std::extent<T>::value != 0>::type
1216 : make_neunique(Args&&...) = delete;
1217 :
1218 : //----------------------------------------------------------
1219 :
1220 : /** Create a neunique_ptr using a custom allocator.
1221 :
1222 : Allocates and constructs an object of type `T` using
1223 : the specified allocator. The object and control block
1224 : are allocated together for efficiency.
1225 :
1226 : @param a Allocator to use.
1227 : @param args Arguments forwarded to `T`'s constructor.
1228 :
1229 : @return A `neunique_ptr<T>` owning the new object.
1230 :
1231 : @throws Any exception thrown by allocation or construction.
1232 : */
1233 : template<class T, class A, class... Args>
1234 : typename std::enable_if<
1235 : !std::is_array<T>::value,
1236 : neunique_ptr<T>>::type
1237 2 : allocate_neunique(A const& a, Args&&... args)
1238 : {
1239 : using cb_type = detail::control_block_embedded<T, A>;
1240 : using alloc_type = typename cb_type::alloc_type;
1241 : using alloc_traits = std::allocator_traits<alloc_type>;
1242 :
1243 1 : alloc_type alloc(a);
1244 2 : cb_type* cb = alloc_traits::allocate(alloc, 1);
1245 2 : auto guard = detail::make_scope_guard(
1246 0 : [&]{ alloc_traits::deallocate(alloc, cb, 1); });
1247 2 : ::new(static_cast<void*>(cb)) cb_type(
1248 : a, std::forward<Args>(args)...);
1249 2 : guard.release();
1250 :
1251 2 : neunique_ptr<T> result;
1252 2 : result.ptr_ = cb->get();
1253 2 : result.cb_ = cb;
1254 4 : return result;
1255 2 : }
1256 :
1257 : /** Create a neunique_ptr for an array using a custom allocator.
1258 :
1259 : Allocates an array of `n` value-initialized elements
1260 : using the specified allocator.
1261 :
1262 : @param a Allocator to use.
1263 : @param n Number of elements.
1264 :
1265 : @return A `neunique_ptr<T[]>` owning the new array.
1266 :
1267 : @throws Any exception thrown by allocation or construction.
1268 : */
1269 : template<class T, class A>
1270 : typename std::enable_if<
1271 : std::is_array<T>::value && std::extent<T>::value == 0,
1272 : neunique_ptr<T>>::type
1273 1 : allocate_neunique(A const& a, std::size_t n)
1274 : {
1275 : using U = typename std::remove_extent<T>::type;
1276 : using cb_type = detail::control_block_array<U, A>;
1277 : using alloc_type = typename cb_type::alloc_type;
1278 : using alloc_traits = std::allocator_traits<alloc_type>;
1279 :
1280 1 : alloc_type alloc(a);
1281 1 : std::size_t units = (cb_type::storage_size(n) +
1282 1 : sizeof(cb_type) - 1) / sizeof(cb_type);
1283 1 : cb_type* cb = alloc_traits::allocate(alloc, units);
1284 :
1285 1 : auto guard = detail::make_scope_guard(
1286 0 : [&]{ alloc_traits::deallocate(alloc, cb, units); });
1287 :
1288 1 : ::new(static_cast<void*>(cb)) cb_type(a);
1289 :
1290 1 : U* arr = cb->get();
1291 4 : for(std::size_t i = 0; i < n; ++i)
1292 : {
1293 3 : ::new(static_cast<void*>(arr + i)) U();
1294 3 : ++cb->size;
1295 : }
1296 1 : guard.release();
1297 :
1298 1 : neunique_ptr<T> result;
1299 1 : result.ptr_ = arr;
1300 1 : result.cb_ = cb;
1301 2 : return result;
1302 1 : }
1303 :
1304 : //----------------------------------------------------------
1305 :
1306 : /** Swap two pointers.
1307 :
1308 : @param a First pointer.
1309 : @param b Second pointer.
1310 : */
1311 : template<class T>
1312 1 : void swap(neunique_ptr<T>& a, neunique_ptr<T>& b) noexcept
1313 : {
1314 1 : a.swap(b);
1315 1 : }
1316 :
1317 : //----------------------------------------------------------
1318 : //
1319 : // Comparison operators
1320 : //
1321 : //----------------------------------------------------------
1322 :
1323 : /** Compare for equality.
1324 :
1325 : @return `true` if both store the same pointer.
1326 : */
1327 : template<class T, class U>
1328 1 : bool operator==(
1329 : neunique_ptr<T> const& a,
1330 : neunique_ptr<U> const& b) noexcept
1331 : {
1332 1 : return a.get() == b.get();
1333 : }
1334 :
1335 : /** Compare with nullptr.
1336 :
1337 : @return `true` if `a` is empty.
1338 : */
1339 : template<class T>
1340 1 : bool operator==(
1341 : neunique_ptr<T> const& a,
1342 : std::nullptr_t) noexcept
1343 : {
1344 1 : return !a;
1345 : }
1346 :
1347 : /** Compare with nullptr.
1348 :
1349 : @return `true` if `a` is empty.
1350 : */
1351 : template<class T>
1352 1 : bool operator==(
1353 : std::nullptr_t,
1354 : neunique_ptr<T> const& a) noexcept
1355 : {
1356 1 : return !a;
1357 : }
1358 :
1359 : /** Compare for inequality.
1360 :
1361 : @return `true` if pointers differ.
1362 : */
1363 : template<class T, class U>
1364 1 : bool operator!=(
1365 : neunique_ptr<T> const& a,
1366 : neunique_ptr<U> const& b) noexcept
1367 : {
1368 1 : return a.get() != b.get();
1369 : }
1370 :
1371 : /** Compare with nullptr.
1372 :
1373 : @return `true` if `a` is non-empty.
1374 : */
1375 : template<class T>
1376 1 : bool operator!=(
1377 : neunique_ptr<T> const& a,
1378 : std::nullptr_t) noexcept
1379 : {
1380 1 : return static_cast<bool>(a);
1381 : }
1382 :
1383 : /** Compare with nullptr.
1384 :
1385 : @return `true` if `a` is non-empty.
1386 : */
1387 : template<class T>
1388 1 : bool operator!=(
1389 : std::nullptr_t,
1390 : neunique_ptr<T> const& a) noexcept
1391 : {
1392 1 : return static_cast<bool>(a);
1393 : }
1394 :
1395 : /** Less-than comparison.
1396 :
1397 : @return `true` if `a.get() < b.get()`.
1398 : */
1399 : template<class T, class U>
1400 4 : bool operator<(
1401 : neunique_ptr<T> const& a,
1402 : neunique_ptr<U> const& b) noexcept
1403 : {
1404 : using V = typename std::common_type<
1405 : typename neunique_ptr<T>::pointer,
1406 : typename neunique_ptr<U>::pointer>::type;
1407 4 : return std::less<V>()(a.get(), b.get());
1408 : }
1409 :
1410 : /** Less-than comparison with nullptr.
1411 :
1412 : @return `true` if `a.get() < nullptr`.
1413 : */
1414 : template<class T>
1415 : bool operator<(
1416 : neunique_ptr<T> const& a,
1417 : std::nullptr_t) noexcept
1418 : {
1419 : return std::less<typename neunique_ptr<T>::pointer>()(
1420 : a.get(), nullptr);
1421 : }
1422 :
1423 : /** Less-than comparison with nullptr.
1424 :
1425 : @return `true` if `nullptr < a.get()`.
1426 : */
1427 : template<class T>
1428 : bool operator<(
1429 : std::nullptr_t,
1430 : neunique_ptr<T> const& a) noexcept
1431 : {
1432 : return std::less<typename neunique_ptr<T>::pointer>()(
1433 : nullptr, a.get());
1434 : }
1435 :
1436 : /** Less-than-or-equal comparison.
1437 :
1438 : @return `true` if `a.get() <= b.get()`.
1439 : */
1440 : template<class T, class U>
1441 1 : bool operator<=(
1442 : neunique_ptr<T> const& a,
1443 : neunique_ptr<U> const& b) noexcept
1444 : {
1445 1 : return !(b < a);
1446 : }
1447 :
1448 : /** Less-than-or-equal comparison with nullptr.
1449 :
1450 : @return `true` if `a.get() <= nullptr`.
1451 : */
1452 : template<class T>
1453 : bool operator<=(
1454 : neunique_ptr<T> const& a,
1455 : std::nullptr_t) noexcept
1456 : {
1457 : return !(nullptr < a);
1458 : }
1459 :
1460 : /** Less-than-or-equal comparison with nullptr.
1461 :
1462 : @return `true` if `nullptr <= a.get()`.
1463 : */
1464 : template<class T>
1465 : bool operator<=(
1466 : std::nullptr_t,
1467 : neunique_ptr<T> const& a) noexcept
1468 : {
1469 : return !(a < nullptr);
1470 : }
1471 :
1472 : /** Greater-than comparison.
1473 :
1474 : @return `true` if `a.get() > b.get()`.
1475 : */
1476 : template<class T, class U>
1477 1 : bool operator>(
1478 : neunique_ptr<T> const& a,
1479 : neunique_ptr<U> const& b) noexcept
1480 : {
1481 1 : return b < a;
1482 : }
1483 :
1484 : /** Greater-than comparison with nullptr.
1485 :
1486 : @return `true` if `a.get() > nullptr`.
1487 : */
1488 : template<class T>
1489 : bool operator>(
1490 : neunique_ptr<T> const& a,
1491 : std::nullptr_t) noexcept
1492 : {
1493 : return nullptr < a;
1494 : }
1495 :
1496 : /** Greater-than comparison with nullptr.
1497 :
1498 : @return `true` if `nullptr > a.get()`.
1499 : */
1500 : template<class T>
1501 : bool operator>(
1502 : std::nullptr_t,
1503 : neunique_ptr<T> const& a) noexcept
1504 : {
1505 : return a < nullptr;
1506 : }
1507 :
1508 : /** Greater-than-or-equal comparison.
1509 :
1510 : @return `true` if `a.get() >= b.get()`.
1511 : */
1512 : template<class T, class U>
1513 1 : bool operator>=(
1514 : neunique_ptr<T> const& a,
1515 : neunique_ptr<U> const& b) noexcept
1516 : {
1517 1 : return !(a < b);
1518 : }
1519 :
1520 : /** Greater-than-or-equal comparison with nullptr.
1521 :
1522 : @return `true` if `a.get() >= nullptr`.
1523 : */
1524 : template<class T>
1525 : bool operator>=(
1526 : neunique_ptr<T> const& a,
1527 : std::nullptr_t) noexcept
1528 : {
1529 : return !(a < nullptr);
1530 : }
1531 :
1532 : /** Greater-than-or-equal comparison with nullptr.
1533 :
1534 : @return `true` if `nullptr >= a.get()`.
1535 : */
1536 : template<class T>
1537 : bool operator>=(
1538 : std::nullptr_t,
1539 : neunique_ptr<T> const& a) noexcept
1540 : {
1541 : return !(nullptr < a);
1542 : }
1543 :
1544 : } // capy
1545 : } // boost
1546 :
1547 : //----------------------------------------------------------
1548 : //
1549 : // Hash support
1550 : //
1551 : //----------------------------------------------------------
1552 :
1553 : namespace std {
1554 :
1555 : /** Hash support for neunique_ptr.
1556 :
1557 : Allows `neunique_ptr` to be used as a key in
1558 : unordered containers.
1559 : */
1560 : template<class T>
1561 : struct hash<::boost::capy::neunique_ptr<T>>
1562 : {
1563 : /** Return hash value for a neunique_ptr.
1564 :
1565 : @param p Pointer to hash.
1566 :
1567 : @return Hash of the stored pointer.
1568 : */
1569 3 : std::size_t operator()(
1570 : ::boost::capy::neunique_ptr<T> const& p) const noexcept
1571 : {
1572 3 : return std::hash<typename boost::capy::neunique_ptr<T>::pointer>()(p.get());
1573 : }
1574 : };
1575 :
1576 : } // std
1577 :
1578 : #endif
|