GCC Code Coverage Report


Directory: ./
File: libs/capy/include/boost/capy/core/neunique_ptr.hpp
Date: 2026-01-15 20:40:20
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 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 8 explicit ebo_storage(T&& t)
75 8 : value_(std::move(t))
76 {
77 8 }
78
79 8 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 12 explicit ebo_storage(T&& t)
94 12 : T(std::move(t))
95 {
96 12 }
97
98 12 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 34 explicit scope_guard(F f) noexcept
114 34 : f_(std::move(f))
115 {
116 34 }
117
118 34 ~scope_guard()
119 {
120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
34 if(active_)
121 f_();
122 34 }
123
124 34 void release() noexcept
125 {
126 34 active_ = false;
127 34 }
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 34 make_scope_guard(F f) noexcept
144 {
145 34 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 14 alloc_type& get_alloc() noexcept
186 {
187 14 return alloc_storage::get();
188 }
189
190 14 control_block_pda(T* p, D d, A const& a)
191 18 : alloc_storage(alloc_type(a))
192 14 , ptr(p)
193 34 , deleter(std::move(d))
194 {
195 14 }
196
197 14 void destroy_and_deallocate() noexcept override
198 {
199 14 T* p = ptr;
200 14 D del = std::move(deleter);
201 14 alloc_type a = std::move(get_alloc());
202 14 this->~control_block_pda();
203 4 alloc_traits::deallocate(a, this, 1);
204
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
14 if(p)
205 14 del(p);
206 14 }
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 4 alloc_type& get_alloc() noexcept
231 {
232 4 return alloc_storage::get();
233 }
234
235 8 T* get() noexcept
236 {
237 8 return reinterpret_cast<T*>(storage);
238 }
239
240 template<class... Args>
241 4 explicit control_block_embedded(A const& a, Args&&... args)
242 6 : alloc_storage(alloc_type(a))
243 {
244 6 ::new(static_cast<void*>(storage)) T(
245 4 std::forward<Args>(args)...);
246 4 }
247
248 4 void destroy_and_deallocate() noexcept override
249 {
250 4 get()->~T();
251 4 alloc_type a = std::move(get_alloc());
252 4 this->~control_block_embedded();
253 2 alloc_traits::deallocate(a, this, 1);
254 4 }
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
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
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 12 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 4 alloc_type alloc(a);
400
1/1
✓ Branch 1 taken 2 times.
12 cb_type* cb = alloc_traits::allocate(alloc, 1);
401 12 auto guard = detail::make_scope_guard(
402 [&]{ alloc_traits::deallocate(alloc, cb, 1); });
403
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);
404 12 guard.release();
405 12 ptr_ = p;
406 12 cb_ = cb;
407 20 }
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 42 explicit neunique_ptr(pointer p) noexcept
447 42 : ptr_(p)
448 {
449 42 }
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 8 neunique_ptr(pointer p, D d)
466
1/1
✓ Branch 2 taken 4 times.
8 : neunique_ptr(p, std::move(d), std::allocator<T>{})
467 {
468 8 }
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 12 neunique_ptr(pointer p, D d, A const& a)
486 12 {
487
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
12 if(!p)
488 return;
489 12 auto guard = detail::make_scope_guard(
490 [&]{ d(p); });
491
1/1
✓ Branch 2 taken 6 times.
12 init_pda(p, std::move(d), a);
492 12 guard.release();
493 12 }
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/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));
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 82 ~neunique_ptr()
578 {
579
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 35 times.
82 if(cb_)
580 12 cb_->destroy_and_deallocate();
581
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 16 times.
70 else if(ptr_)
582 38 detail::try_delete(ptr_, 0);
583 82 }
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/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if(this != &other)
605 {
606
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(cb_)
607 cb_->destroy_and_deallocate();
608
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
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/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(cb_)
635 cb_->destroy_and_deallocate();
636
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 else if(ptr_)
637 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 10 void reset(pointer p = nullptr) noexcept
676 {
677
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
10 if(cb_)
678 4 cb_->destroy_and_deallocate();
679
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
6 else if(ptr_)
680 4 detail::try_delete(ptr_, 0);
681 10 ptr_ = p;
682 10 cb_ = nullptr;
683 10 }
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/1
✓ Branch 2 taken 1 times.
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/1
✓ Branch 2 taken 1 times.
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 70 explicit operator bool() const noexcept
753 {
754 70 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 12 pointer operator->() const noexcept
776 {
777 12 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 [&]{ alloc_traits::deallocate(alloc, cb, 1); });
819
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);
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/1
✓ Branch 2 taken 1 times.
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/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(!p)
907 return;
908 1 auto guard = detail::make_scope_guard(
909 [&]{ d(p); });
910
1/1
✓ Branch 2 taken 1 times.
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
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 13 times.
15 if(cb_)
976 2 cb_->destroy_and_deallocate();
977
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 5 times.
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/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(this != &other)
1001 {
1002
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(cb_)
1003 cb_->destroy_and_deallocate();
1004
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
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/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(cb_)
1043 cb_->destroy_and_deallocate();
1044
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
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/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(cb_)
1066 cb_->destroy_and_deallocate();
1067
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 else if(ptr_)
1068 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 4 make_neunique(Args&&... args)
1182 {
1183 4 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
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]());
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 4 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 2 alloc_type alloc(a);
1238
1/1
✓ Branch 1 taken 1 times.
4 cb_type* cb = alloc_traits::allocate(alloc, 1);
1239 4 auto guard = detail::make_scope_guard(
1240 [&]{ alloc_traits::deallocate(alloc, cb, 1); });
1241
1/3
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 3 taken 1 times.
4 ::new(static_cast<void*>(cb)) cb_type(
1242 a, std::forward<Args>(args)...);
1243 4 guard.release();
1244
1245 4 neunique_ptr<T> result;
1246 4 result.ptr_ = cb->get();
1247 4 result.cb_ = cb;
1248 8 return result;
1249 4 }
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/1
✓ Branch 1 taken 1 times.
1 cb_type* cb = alloc_traits::allocate(alloc, units);
1278
1279 1 auto guard = detail::make_scope_guard(
1280 [&]{ alloc_traits::deallocate(alloc, cb, units); });
1281
1282
1/3
✓ Branch 2 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1 ::new(static_cast<void*>(cb)) cb_type(a);
1283
1284 1 U* arr = cb->get();
1285
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
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
1573