Skip to content

Commit 2c7d05a

Browse files
Add comprehensive tests for neunique_ptr
- Default and nullptr construction tests - Raw pointer construction tests - Custom deleter and allocator tests - make_neunique and allocate_neunique tests - Move construction and assignment tests - Converting move operations tests - Aliasing constructor tests - Reset, swap, and observer tests - Destruction tracking tests - Comparison operator tests - Hash function tests - Array specialization tests - Empty base optimization tests - Incomplete type support tests Co-authored-by: Vinnie Falco <vinniefalco@users.noreply.github.com>
1 parent 3d5ea41 commit 2c7d05a

File tree

2 files changed

+708
-11
lines changed

2 files changed

+708
-11
lines changed

include/boost/capy/neunique_ptr.hpp

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#define BOOST_CAPY_NEUNIQUE_PTR_HPP
1212

1313
#include <boost/capy/detail/config.hpp>
14+
#include <boost/assert.hpp>
1415
#include <cstddef>
1516
#include <functional>
1617
#include <memory>
@@ -24,6 +25,33 @@ namespace detail {
2425

2526
//----------------------------------------------------------
2627

28+
template<class U>
29+
auto try_delete(U* p, int) noexcept
30+
-> decltype(sizeof(U), void())
31+
{
32+
delete p;
33+
}
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+
auto try_delete_array(U* p, int) noexcept
44+
-> decltype(sizeof(U), void())
45+
{
46+
delete[] p;
47+
}
48+
49+
template<class U>
50+
void try_delete_array(U*, long) noexcept
51+
{
52+
std::terminate();
53+
}
54+
2755
/** Storage wrapper applying empty base optimization.
2856
2957
When `T` is empty and non-final, inherits from it to
@@ -253,6 +281,12 @@ struct control_block_array final
253281
std::size_t size;
254282
// Flexible array member follows
255283

284+
explicit control_block_array(A const& a)
285+
: alloc_storage(alloc_type(a))
286+
, size(0)
287+
{
288+
}
289+
256290
alloc_type& get_alloc() noexcept
257291
{
258292
return alloc_storage::get();
@@ -489,10 +523,11 @@ class neunique_ptr
489523
: ptr_(p)
490524
, cb_(other.cb_)
491525
{
526+
// aliasing requires control block; use allocate_neunique
527+
BOOST_ASSERT((other.cb_ != nullptr || other.ptr_ == nullptr));
492528
other.ptr_ = nullptr;
493529
other.cb_ = nullptr;
494530
}
495-
496531
/** Move constructor.
497532
498533
Takes ownership from `other`. After construction,
@@ -550,7 +585,7 @@ class neunique_ptr
550585
if(cb_)
551586
cb_->destroy_and_deallocate();
552587
else if(ptr_)
553-
delete ptr_;
588+
detail::try_delete(ptr_, 0);
554589
}
555590

556591
//------------------------------------------------------
@@ -577,7 +612,7 @@ class neunique_ptr
577612
if(cb_)
578613
cb_->destroy_and_deallocate();
579614
else if(ptr_)
580-
delete ptr_;
615+
detail::try_delete(ptr_, 0);
581616
ptr_ = other.ptr_;
582617
cb_ = other.cb_;
583618
other.ptr_ = nullptr;
@@ -648,7 +683,7 @@ class neunique_ptr
648683
if(cb_)
649684
cb_->destroy_and_deallocate();
650685
else if(ptr_)
651-
delete ptr_;
686+
detail::try_delete(ptr_, 0);
652687
ptr_ = p;
653688
cb_ = nullptr;
654689
}
@@ -903,6 +938,8 @@ class neunique_ptr<T[]>
903938
: ptr_(p)
904939
, cb_(other.cb_)
905940
{
941+
// aliasing requires control block; use allocate_neunique
942+
BOOST_ASSERT((other.cb_ != nullptr || other.ptr_ == nullptr));
906943
other.ptr_ = nullptr;
907944
other.cb_ = nullptr;
908945
}
@@ -944,7 +981,7 @@ class neunique_ptr<T[]>
944981
if(cb_)
945982
cb_->destroy_and_deallocate();
946983
else if(ptr_)
947-
delete[] ptr_;
984+
detail::try_delete_array(ptr_, 0);
948985
}
949986

950987
//------------------------------------------------------
@@ -971,7 +1008,7 @@ class neunique_ptr<T[]>
9711008
if(cb_)
9721009
cb_->destroy_and_deallocate();
9731010
else if(ptr_)
974-
delete[] ptr_;
1011+
detail::try_delete_array(ptr_, 0);
9751012
ptr_ = other.ptr_;
9761013
cb_ = other.cb_;
9771014
other.ptr_ = nullptr;
@@ -1000,6 +1037,22 @@ class neunique_ptr<T[]>
10001037
//
10011038
//------------------------------------------------------
10021039

1040+
/** Replace the owned array.
1041+
1042+
Releases the currently owned array.
1043+
1044+
@post `get() == nullptr`
1045+
*/
1046+
void reset() noexcept
1047+
{
1048+
if(cb_)
1049+
cb_->destroy_and_deallocate();
1050+
else if(ptr_)
1051+
detail::try_delete_array(ptr_, 0);
1052+
ptr_ = nullptr;
1053+
cb_ = nullptr;
1054+
}
1055+
10031056
/** Replace the owned array.
10041057
10051058
Releases the currently owned array and takes
@@ -1013,7 +1066,7 @@ class neunique_ptr<T[]>
10131066
template<class U, class = typename std::enable_if<
10141067
std::is_same<U, T*>::value ||
10151068
std::is_same<U, std::nullptr_t>::value>::type>
1016-
void reset(U p = nullptr) noexcept
1069+
void reset(U p) noexcept
10171070
{
10181071
if(cb_)
10191072
cb_->destroy_and_deallocate();
@@ -1232,9 +1285,7 @@ allocate_neunique(A const& a, std::size_t n)
12321285
auto guard = detail::make_scope_guard(
12331286
[&]{ alloc_traits::deallocate(alloc, cb, units); });
12341287

1235-
::new(static_cast<void*>(cb)) cb_type();
1236-
cb->get_alloc() = alloc;
1237-
cb->size = 0;
1288+
::new(static_cast<void*>(cb)) cb_type(a);
12381289

12391290
U* arr = cb->get();
12401291
for(std::size_t i = 0; i < n; ++i)

0 commit comments

Comments
 (0)