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