GCC Code Coverage Report


Directory: ./
File: libs/capy/include/boost/capy/neunique_ptr.hpp
Date: 2025-12-15 05:33:30
Exec Total Coverage
Lines: 294 312 94.2%
Functions: 176 181 97.2%
Branches: 52 85 61.2%

Line Branch Exec Source
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 44 auto try_delete(U* p, int) noexcept
30 -> decltype(sizeof(U), void())
31 {
32
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
44 delete p;
33 44 }
34
35 template<class U>
36 void try_delete(U*, long) noexcept
37 {
38 // Incomplete type - should never reach here
39 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
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
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 8 explicit ebo_storage(T&& t)
81 8 : value_(std::move(t))
82 {
83 8 }
84
85 8 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 12 explicit ebo_storage(T&& t)
100 12 : T(std::move(t))
101 {
102 12 }
103
104 12 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 34 explicit scope_guard(F f) noexcept
120 34 : f_(std::move(f))
121 {
122 34 }
123
124 34 ~scope_guard()
125 {
126
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
34 if(active_)
127 f_();
128 34 }
129
130 34 void release() noexcept
131 {
132 34 active_ = false;
133 34 }
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 34 make_scope_guard(F f) noexcept
150 {
151 34 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 14 alloc_type& get_alloc() noexcept
192 {
193 14 return alloc_storage::get();
194 }
195
196 14 control_block_pda(T* p, D d, A const& a)
197 18 : alloc_storage(alloc_type(a))
198 14 , ptr(p)
199 34 , deleter(std::move(d))
200 {
201 14 }
202
203 14 void destroy_and_deallocate() noexcept override
204 {
205 14 T* p = ptr;
206 14 D del = std::move(deleter);
207 14 alloc_type a = std::move(get_alloc());
208 14 this->~control_block_pda();
209 4 alloc_traits::deallocate(a, this, 1);
210
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
14 if(p)
211 14 del(p);
212 14 }
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 4 alloc_type& get_alloc() noexcept
237 {
238 4 return alloc_storage::get();
239 }
240
241 8 T* get() noexcept
242 {
243 8 return reinterpret_cast<T*>(storage);
244 }
245
246 template<class... Args>
247 4 explicit control_block_embedded(A const& a, Args&&... args)
248 6 : alloc_storage(alloc_type(a))
249 {
250 6 ::new(static_cast<void*>(storage)) T(
251 4 std::forward<Args>(args)...);
252 4 }
253
254 4 void destroy_and_deallocate() noexcept override
255 {
256 4 get()->~T();
257 4 alloc_type a = std::move(get_alloc());
258 4 this->~control_block_embedded();
259 2 alloc_traits::deallocate(a, this, 1);
260 4 }
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
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
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 12 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 4 alloc_type alloc(a);
406
1/1
✓ Branch 1 taken 2 times.
12 cb_type* cb = alloc_traits::allocate(alloc, 1);
407 12 auto guard = detail::make_scope_guard(
408 [&]{ alloc_traits::deallocate(alloc, cb, 1); });
409
1/3
✓ Branch 3 taken 6 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
12 ::new(static_cast<void*>(cb)) cb_type(p, std::move(d), a);
410 12 guard.release();
411 12 ptr_ = p;
412 12 cb_ = cb;
413 20 }
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 42 explicit neunique_ptr(pointer p) noexcept
453 42 : ptr_(p)
454 {
455 42 }
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 8 neunique_ptr(pointer p, D d)
472
1/1
✓ Branch 2 taken 4 times.
8 : neunique_ptr(p, std::move(d), std::allocator<T>{})
473 {
474 8 }
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 12 neunique_ptr(pointer p, D d, A const& a)
492 12 {
493
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
12 if(!p)
494 return;
495 12 auto guard = detail::make_scope_guard(
496 [&]{ d(p); });
497
1/1
✓ Branch 2 taken 6 times.
12 init_pda(p, std::move(d), a);
498 12 guard.release();
499 12 }
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/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
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 82 ~neunique_ptr()
584 {
585
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 35 times.
82 if(cb_)
586 12 cb_->destroy_and_deallocate();
587
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 16 times.
70 else if(ptr_)
588 38 detail::try_delete(ptr_, 0);
589 82 }
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/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if(this != &other)
611 {
612
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(cb_)
613 cb_->destroy_and_deallocate();
614
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
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/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(cb_)
641 cb_->destroy_and_deallocate();
642
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 else if(ptr_)
643 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 10 void reset(pointer p = nullptr) noexcept
682 {
683
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
10 if(cb_)
684 4 cb_->destroy_and_deallocate();
685
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
6 else if(ptr_)
686 4 detail::try_delete(ptr_, 0);
687 10 ptr_ = p;
688 10 cb_ = nullptr;
689 10 }
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/1
✓ Branch 2 taken 1 times.
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/1
✓ Branch 2 taken 1 times.
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 70 explicit operator bool() const noexcept
759 {
760 70 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 12 pointer operator->() const noexcept
782 {
783 12 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 [&]{ alloc_traits::deallocate(alloc, cb, 1); });
825
1/3
✓ Branch 3 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
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/1
✓ Branch 2 taken 1 times.
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/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(!p)
913 return;
914 1 auto guard = detail::make_scope_guard(
915 [&]{ d(p); });
916
1/1
✓ Branch 2 taken 1 times.
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
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 13 times.
15 if(cb_)
982 2 cb_->destroy_and_deallocate();
983
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 5 times.
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/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(this != &other)
1007 {
1008
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(cb_)
1009 cb_->destroy_and_deallocate();
1010
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
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/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(cb_)
1049 cb_->destroy_and_deallocate();
1050
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
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/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(cb_)
1072 cb_->destroy_and_deallocate();
1073
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 else if(ptr_)
1074 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 4 make_neunique(Args&&... args)
1188 {
1189 4 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
3/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 1 times.
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 4 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 2 alloc_type alloc(a);
1244
1/1
✓ Branch 1 taken 1 times.
4 cb_type* cb = alloc_traits::allocate(alloc, 1);
1245 4 auto guard = detail::make_scope_guard(
1246 [&]{ alloc_traits::deallocate(alloc, cb, 1); });
1247
1/3
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 3 taken 1 times.
4 ::new(static_cast<void*>(cb)) cb_type(
1248 a, std::forward<Args>(args)...);
1249 4 guard.release();
1250
1251 4 neunique_ptr<T> result;
1252 4 result.ptr_ = cb->get();
1253 4 result.cb_ = cb;
1254 8 return result;
1255 4 }
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/1
✓ Branch 1 taken 1 times.
1 cb_type* cb = alloc_traits::allocate(alloc, units);
1284
1285 1 auto guard = detail::make_scope_guard(
1286 [&]{ alloc_traits::deallocate(alloc, cb, units); });
1287
1288
1/3
✓ Branch 2 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1 ::new(static_cast<void*>(cb)) cb_type(a);
1289
1290 1 U* arr = cb->get();
1291
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
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
1579