From 43b7fa2b6f15ac82c91659237c8b76e2c353921a Mon Sep 17 00:00:00 2001 From: KRM7 <70973547+KRM7@users.noreply.github.com> Date: Tue, 20 Feb 2024 10:03:01 +0100 Subject: [PATCH] add size parameter (closes #6) --- README.md | 17 +-- src/small_unique_ptr.hpp | 263 ++++++++++++++++++-------------------- test/small_unique_ptr.cpp | 31 ++++- 3 files changed, 157 insertions(+), 154 deletions(-) diff --git a/README.md b/README.md index 51b682f..1104f92 100644 --- a/README.md +++ b/README.md @@ -10,25 +10,26 @@ small_unique_ptr p = make_unique_small(); Objects created with `make_unique_small` are allocated on the stack if: - Their size is not greater than the size of the internal stack buffer - - Their required alignment is not greater than 64 + - Their alignment is not greater than the alignment of the stack buffer - Their move constructor is `noexcept` -The size of the stack buffer is architecture dependent, but on 64 bit architectures it will -generally be: +The size of the stack buffer depends on the architecture and the overall size of the +`small_unique_ptr` object, but the default values on 64 bit architectures will typically be: - 48 for polymorphic types - 56 for polymorphic types that implement a virtual `small_unique_ptr_move` method - `sizeof(T)` for non-polymophic types, with an upper limit of 56 - - 56 for array types + - 56 for array types (rounded down to a multiple of the element size) -The overall size of a `small_unique_ptr` object is: +The overall size of a `small_unique_ptr` object for a polymorphic type is: - - 64 if `T` may be allocated in the stack buffer + - `Size` if `T` may be allocated in the stack buffer (64 by default) - `sizeof(T*)` otherwise -The interface matches `std::unique_ptr`, except for: +The interface matches `std::unique_ptr`, except for: - There is no `Deleter` template parameter or any of the associated methods + - There is a `Size` template parameter that specifies the (maximum) size of the `small_unique_ptr` object - Constructors from pointers are not provided except for the nullptr constructor - `release()` is not implemented - `T` can't be an incomplete type @@ -36,7 +37,7 @@ The interface matches `std::unique_ptr`, except for: Everything is constexpr, but the stack buffer is not used in constant evaluated contexts, so any constexpr usage is subject to the same transient allocation requirements that a constexpr -`std::unique_ptr` would be. +`std::unique_ptr` would be. -------------------------------------------------------------------------------------------------- diff --git a/src/small_unique_ptr.hpp b/src/small_unique_ptr.hpp index db6619f..e163c23 100644 --- a/src/small_unique_ptr.hpp +++ b/src/small_unique_ptr.hpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -23,6 +25,8 @@ namespace detail constexpr const T& max(const T& left, const T& right) { return (left < right) ? right : left; } + using move_fn = void(*)(void* src, void* dst) noexcept; + template void move_buffer(void* src, void* dst) noexcept(std::is_nothrow_move_constructible_v) { @@ -55,80 +59,62 @@ namespace detail inline constexpr bool is_proper_base_of_v = is_proper_base_of::value; - inline constexpr std::size_t small_ptr_size = 64; + inline constexpr std::size_t default_small_ptr_size = 64; - template + template struct buffer_size { private: - static constexpr std::size_t dynamic_buffer_size = small_ptr_size - sizeof(T*) - !has_virtual_move * sizeof(decltype(&move_buffer)); + static constexpr std::size_t dynamic_buffer_size = small_ptr_size - sizeof(T*) - !has_virtual_move * sizeof(move_fn); static constexpr std::size_t static_buffer_size = detail::min(sizeof(T), small_ptr_size - sizeof(T*)); public: static constexpr std::size_t value = std::has_virtual_destructor_v ? dynamic_buffer_size : static_buffer_size; }; - template - struct buffer_size + template + struct buffer_size { static constexpr std::size_t value = small_ptr_size - sizeof(T*); }; - template - inline constexpr std::size_t buffer_size_v = buffer_size::value; + template + inline constexpr std::size_t buffer_size_v = buffer_size::value; - template + template struct buffer_alignment { private: - static constexpr std::size_t dynamic_buffer_alignment = small_ptr_size; - static constexpr std::size_t static_buffer_alignment = detail::min(alignof(T), small_ptr_size); + static constexpr std::size_t dynamic_buffer_alignment = std::gcd(std::bit_floor(small_ptr_size), small_ptr_size); + static constexpr std::size_t static_buffer_alignment = std::gcd(std::bit_floor(min(alignof(T), small_ptr_size)), min(alignof(T), small_ptr_size)); public: static constexpr std::size_t value = std::has_virtual_destructor_v ? dynamic_buffer_alignment : static_buffer_alignment; }; - template - inline constexpr std::size_t buffer_alignment_v = buffer_alignment::value; - + template + inline constexpr std::size_t buffer_alignment_v = buffer_alignment::value; - template - struct buffer_elements {}; - template - struct buffer_elements - { - static constexpr std::size_t value = buffer_size_v / sizeof(T); - }; - - template - inline constexpr std::size_t buffer_elements_v = buffer_elements::value; - - - template + template struct is_always_heap_allocated { - static constexpr bool value = (sizeof(T) > buffer_size_v) || (alignof(T) > buffer_alignment_v) || - (!std::is_abstract_v && !std::is_nothrow_move_constructible_v>); - }; + using U = std::remove_cv_t>; - template - struct is_always_heap_allocated - { - static constexpr bool value = (sizeof(T) > buffer_size_v) || (alignof(T) > buffer_alignment_v) || - !std::is_nothrow_move_constructible_v>; + static constexpr bool value = (sizeof(U) > buffer_size_v) || + (alignof(U) > buffer_alignment_v) || + (!std::is_abstract_v && !std::is_nothrow_move_constructible_v); }; - template - inline constexpr bool is_always_heap_allocated_v = is_always_heap_allocated::value; + template + inline constexpr bool is_always_heap_allocated_v = is_always_heap_allocated::value; - template + template struct small_unique_ptr_base { using pointer = std::remove_cv_t*; - using buffer_t = unsigned char[buffer_size_v]; - using move_fn = void(*)(void*, void*) noexcept; + using buffer_t = unsigned char[buffer_size_v]; pointer buffer(std::ptrdiff_t offset = 0) const noexcept { @@ -136,7 +122,7 @@ namespace detail } template - void move_buffer_to(small_unique_ptr_base& dst) noexcept + void move_buffer_to(small_unique_ptr_base& dst) noexcept { move_(std::launder(buffer()), dst.buffer()); dst.move_ = move_; @@ -147,23 +133,23 @@ namespace detail return static_cast(move_); } - alignas(buffer_alignment_v) mutable buffer_t buffer_ = {}; + alignas(buffer_alignment_v) mutable buffer_t buffer_ = {}; T* data_ = nullptr; move_fn move_ = nullptr; }; - template - requires(is_always_heap_allocated_v) - struct small_unique_ptr_base + template + requires(is_always_heap_allocated_v) + struct small_unique_ptr_base { static constexpr bool is_stack_allocated() noexcept { return false; } std::remove_extent_t* data_ = nullptr; }; - template - requires(!is_always_heap_allocated_v && !std::is_polymorphic_v && !std::is_array_v) - struct small_unique_ptr_base + template + requires(!is_always_heap_allocated_v && !std::is_polymorphic_v && !std::is_array_v) + struct small_unique_ptr_base { using pointer = std::remove_cv_t*; using buffer_t = std::remove_cv_t; @@ -174,7 +160,7 @@ namespace detail } template - constexpr void move_buffer_to(small_unique_ptr_base& dst) noexcept + constexpr void move_buffer_to(small_unique_ptr_base& dst) noexcept { std::construct_at(dst.buffer(), std::move(*buffer())); } @@ -191,12 +177,12 @@ namespace detail T* data_ = nullptr; }; - template - requires(!is_always_heap_allocated_v && has_virtual_move) - struct small_unique_ptr_base + template + requires(!is_always_heap_allocated_v && has_virtual_move) + struct small_unique_ptr_base { using pointer = std::remove_cv_t*; - using buffer_t = unsigned char[buffer_size_v]; + using buffer_t = unsigned char[buffer_size_v]; pointer buffer(std::ptrdiff_t offset = 0) const noexcept { @@ -205,7 +191,7 @@ namespace detail template requires(has_virtual_move) - void move_buffer_to(small_unique_ptr_base& dst) noexcept + void move_buffer_to(small_unique_ptr_base& dst) noexcept { const pointer data = const_cast(data_); data->small_unique_ptr_move(dst.buffer()); @@ -217,23 +203,25 @@ namespace detail auto* data = reinterpret_cast(data_); auto* buffer_first = static_cast(buffer_); - auto* buffer_last = buffer_first + buffer_size_v; + auto* buffer_last = buffer_first + buffer_size_v; - assert(reinterpret_cast(buffer_last) - reinterpret_cast(buffer_first) == buffer_size_v); + assert(reinterpret_cast(buffer_last) - reinterpret_cast(buffer_first) == (buffer_size_v)); return std::less_equal{}(buffer_first, data) && std::less{}(data, buffer_last); } - alignas(buffer_alignment_v) mutable buffer_t buffer_ = {}; + alignas(buffer_alignment_v) mutable buffer_t buffer_ = {}; T* data_ = nullptr; }; - template - requires(!is_always_heap_allocated_v && std::is_array_v) - struct small_unique_ptr_base + template + requires(!is_always_heap_allocated_v && std::is_array_v) + struct small_unique_ptr_base { + static constexpr std::size_t array_size = buffer_size_v / sizeof(std::remove_extent_t); + using pointer = std::remove_cv_t>*; - using buffer_t = std::remove_cv_t>[buffer_elements_v]; + using buffer_t = std::remove_cv_t>[array_size]; constexpr pointer buffer(std::ptrdiff_t = 0) const noexcept { @@ -241,9 +229,9 @@ namespace detail } template - void move_buffer_to(small_unique_ptr_base& dst) noexcept + void move_buffer_to(small_unique_ptr_base& dst) noexcept { - std::uninitialized_move(buffer(), buffer() + buffer_elements_v, dst.buffer()); + std::uninitialized_move(buffer(), buffer() + array_size, dst.buffer()); } constexpr bool is_stack_allocated() const noexcept @@ -263,11 +251,13 @@ namespace detail } // namespace detail -template -class small_unique_ptr : private detail::small_unique_ptr_base +template +class small_unique_ptr : private detail::small_unique_ptr_base { public: - static_assert(!std::is_bounded_array_v); + static_assert(!std::is_bounded_array_v, "Only unbounded array types are supported."); + static_assert(Size >= 2 * sizeof(T*), "The size must be at least the size of 2 pointers."); + static_assert(Size % sizeof(T*) == 0, "The size must be a multiple of the size of a pointer."); using element_type = std::remove_extent_t; using pointer = std::remove_extent_t*; @@ -283,7 +273,7 @@ class small_unique_ptr : private detail::small_unique_ptr_base {} template - constexpr small_unique_ptr(small_unique_ptr&& other, constructor_tag_t = {}) noexcept + constexpr small_unique_ptr(small_unique_ptr&& other, constructor_tag_t = {}) noexcept { static_assert(!detail::is_proper_base_of_v || std::has_virtual_destructor_v); @@ -292,7 +282,7 @@ class small_unique_ptr : private detail::small_unique_ptr_base this->data_ = std::exchange(other.data_, nullptr); return; } - if constexpr (!detail::is_always_heap_allocated_v) // other.is_stack_allocated() + if constexpr (!detail::is_always_heap_allocated_v) // other.is_stack_allocated() { other.move_buffer_to(*this); this->data_ = std::launder(this->buffer(other.template offsetof_base())); @@ -308,7 +298,7 @@ class small_unique_ptr : private detail::small_unique_ptr_base } template - constexpr small_unique_ptr& operator=(small_unique_ptr&& other) noexcept + constexpr small_unique_ptr& operator=(small_unique_ptr&& other) noexcept { static_assert(!detail::is_proper_base_of_v || std::has_virtual_destructor_v); @@ -317,7 +307,7 @@ class small_unique_ptr : private detail::small_unique_ptr_base reset(std::exchange(other.data_, nullptr)); return *this; } - if constexpr (!detail::is_always_heap_allocated_v) // other.is_stack_allocated() + if constexpr (!detail::is_always_heap_allocated_v) // other.is_stack_allocated() { reset(); other.move_buffer_to(*this); @@ -347,7 +337,7 @@ class small_unique_ptr : private detail::small_unique_ptr_base constexpr void swap(small_unique_ptr& other) noexcept { - if constexpr (detail::is_always_heap_allocated_v) + if constexpr (small_unique_ptr::is_always_heap_allocated()) { std::swap(this->data_, other.data_); } @@ -357,22 +347,9 @@ class small_unique_ptr : private detail::small_unique_ptr_base } else if (is_stack_allocated() && other.is_stack_allocated()) { - const std::ptrdiff_t other_offset = other.offsetof_base(); - const std::ptrdiff_t this_offset = this->offsetof_base(); - - detail::small_unique_ptr_base temp; - - other.move_buffer_to(temp); - temp.data_ = std::launder(temp.buffer(other_offset)); - std::destroy_at(other.data_); - - this->move_buffer_to(other); - other.data_ = std::launder(other.buffer(this_offset)); - std::destroy_at(this->data_); - - temp.move_buffer_to(*this); - this->data_ = std::launder(this->buffer(other_offset)); - std::destroy_at(temp.data_); + small_unique_ptr temp = std::move(other); + other = std::move(*this); + *this = std::move(temp); } else if (!is_stack_allocated() && other.is_stack_allocated()) { @@ -394,7 +371,7 @@ class small_unique_ptr : private detail::small_unique_ptr_base [[nodiscard]] static constexpr bool is_always_heap_allocated() noexcept { - return detail::is_always_heap_allocated_v; + return detail::is_always_heap_allocated_v; } [[nodiscard]] @@ -406,8 +383,14 @@ class small_unique_ptr : private detail::small_unique_ptr_base [[nodiscard]] static constexpr std::size_t stack_buffer_size() noexcept { - if constexpr (detail::is_always_heap_allocated_v) return 0; - else return detail::buffer_size_v; + if constexpr (is_always_heap_allocated()) return 0; + else return sizeof(typename small_unique_ptr::buffer_t); + } + + [[nodiscard]] + static constexpr std::size_t stack_array_size() noexcept requires(std::is_array_v) + { + return stack_buffer_size() / sizeof(std::remove_extent_t); } [[nodiscard]] @@ -460,7 +443,7 @@ class small_unique_ptr : private detail::small_unique_ptr_base } template - constexpr std::strong_ordering operator<=>(const small_unique_ptr& rhs) const noexcept + constexpr std::strong_ordering operator<=>(const small_unique_ptr& rhs) const noexcept { return this->data_ <=> rhs.data_; } @@ -501,10 +484,10 @@ class small_unique_ptr : private detail::small_unique_ptr_base constexpr void destroy() noexcept requires(std::is_array_v) { - is_stack_allocated() ? std::destroy(this->data_, this->data_ + detail::buffer_elements_v) : delete[] this->data_; + is_stack_allocated() ? std::destroy(this->data_, this->data_ + stack_array_size()) : delete[] this->data_; } - template + template friend class small_unique_ptr; friend struct detail::make_unique_small_impl; @@ -512,12 +495,12 @@ class small_unique_ptr : private detail::small_unique_ptr_base namespace std { - template - struct hash> + template + struct hash> { - std::size_t operator()(const small_unique_ptr& p) const noexcept + std::size_t operator()(const small_unique_ptr& p) const noexcept { - return std::hash::pointer>{}(p.get()); + return std::hash::pointer>{}(p.get()); } }; @@ -527,22 +510,22 @@ namespace detail { struct make_unique_small_impl { - template - static constexpr small_unique_ptr invoke_scalar(Args&&... args) - noexcept(std::is_nothrow_constructible_v && !detail::is_always_heap_allocated_v) + template + static constexpr small_unique_ptr invoke_scalar(Args&&... args) + noexcept(std::is_nothrow_constructible_v && !detail::is_always_heap_allocated_v) { - small_unique_ptr ptr; + small_unique_ptr ptr; - if (detail::is_always_heap_allocated_v || std::is_constant_evaluated()) + if (detail::is_always_heap_allocated_v || std::is_constant_evaluated()) { ptr.data_ = new T(std::forward(args)...); } - else if constexpr (!detail::is_always_heap_allocated_v && std::is_polymorphic_v && !detail::has_virtual_move) + else if constexpr (!detail::is_always_heap_allocated_v && std::is_polymorphic_v && !detail::has_virtual_move) { ptr.data_ = std::construct_at(ptr.buffer(), std::forward(args)...); ptr.move_ = detail::move_buffer>; } - else if constexpr (!detail::is_always_heap_allocated_v) + else if constexpr (!detail::is_always_heap_allocated_v) { ptr.data_ = std::construct_at(ptr.buffer(), std::forward(args)...); } @@ -550,40 +533,40 @@ namespace detail return ptr; } - template - static constexpr small_unique_ptr invoke_array(std::size_t count) + template + static constexpr small_unique_ptr invoke_array(std::size_t count) { - small_unique_ptr ptr; + small_unique_ptr ptr; - if (detail::is_always_heap_allocated_v || (count > detail::buffer_elements_v) || std::is_constant_evaluated()) + if (detail::is_always_heap_allocated_v || (ptr.stack_array_size() < count) || std::is_constant_evaluated()) { ptr.data_ = new std::remove_extent_t[count]{}; } - else if constexpr (!detail::is_always_heap_allocated_v) + else if constexpr (!detail::is_always_heap_allocated_v) { - std::uninitialized_value_construct_n(ptr.buffer(), detail::buffer_elements_v); + std::uninitialized_value_construct_n(ptr.buffer(), ptr.stack_array_size()); ptr.data_ = std::launder(ptr.buffer()); } return ptr; } - template - static constexpr small_unique_ptr invoke_for_overwrite_scalar() - noexcept(std::is_nothrow_default_constructible_v && !detail::is_always_heap_allocated_v) + template + static constexpr small_unique_ptr invoke_for_overwrite_scalar() + noexcept(std::is_nothrow_default_constructible_v && !detail::is_always_heap_allocated_v) { - small_unique_ptr ptr; + small_unique_ptr ptr; - if (detail::is_always_heap_allocated_v || std::is_constant_evaluated()) + if (detail::is_always_heap_allocated_v || std::is_constant_evaluated()) { ptr.data_ = new T; } - else if constexpr (!detail::is_always_heap_allocated_v && std::is_polymorphic_v && !detail::has_virtual_move) + else if constexpr (!detail::is_always_heap_allocated_v && std::is_polymorphic_v && !detail::has_virtual_move) { ptr.data_ = ::new(static_cast(ptr.buffer())) std::remove_cv_t; ptr.move_ = detail::move_buffer>; } - else if constexpr (!detail::is_always_heap_allocated_v) + else if constexpr (!detail::is_always_heap_allocated_v) { ptr.data_ = ::new(static_cast(ptr.buffer())) std::remove_cv_t; } @@ -591,18 +574,18 @@ namespace detail return ptr; } - template - static constexpr small_unique_ptr invoke_for_overwrite_array(std::size_t count) + template + static constexpr small_unique_ptr invoke_for_overwrite_array(std::size_t count) { - small_unique_ptr ptr; + small_unique_ptr ptr; - if (detail::is_always_heap_allocated_v || (count > detail::buffer_elements_v) || std::is_constant_evaluated()) + if (detail::is_always_heap_allocated_v || (ptr.stack_array_size() < count) || std::is_constant_evaluated()) { ptr.data_ = new std::remove_extent_t[count]; } - else if constexpr (!detail::is_always_heap_allocated_v) + else if constexpr (!detail::is_always_heap_allocated_v) { - std::uninitialized_default_construct_n(ptr.buffer(), detail::buffer_elements_v); + std::uninitialized_default_construct_n(ptr.buffer(), ptr.stack_array_size()); ptr.data_ = std::launder(ptr.buffer()); } @@ -612,37 +595,37 @@ namespace detail } // namespace detail -template -[[nodiscard]] constexpr small_unique_ptr make_unique_small(Args&&... args) -noexcept(std::is_nothrow_constructible_v && !detail::is_always_heap_allocated_v) requires(!std::is_array_v) +template +[[nodiscard]] constexpr small_unique_ptr make_unique_small(Args&&... args) +noexcept(std::is_nothrow_constructible_v && !detail::is_always_heap_allocated_v) requires(!std::is_array_v) { - return detail::make_unique_small_impl::invoke_scalar(std::forward(args)...); + return detail::make_unique_small_impl::invoke_scalar(std::forward(args)...); } -template -[[nodiscard]] constexpr small_unique_ptr make_unique_small(std::size_t count) requires(std::is_unbounded_array_v) +template +[[nodiscard]] constexpr small_unique_ptr make_unique_small(std::size_t count) requires(std::is_unbounded_array_v) { - return detail::make_unique_small_impl::invoke_array(count); + return detail::make_unique_small_impl::invoke_array(count); } -template -[[nodiscard]] constexpr small_unique_ptr make_unique_small(Args&&...) requires(std::is_bounded_array_v) = delete; +template +[[nodiscard]] constexpr small_unique_ptr make_unique_small(Args&&...) requires(std::is_bounded_array_v) = delete; -template -[[nodiscard]] constexpr small_unique_ptr make_unique_small_for_overwrite() -noexcept(std::is_nothrow_default_constructible_v && !detail::is_always_heap_allocated_v) requires(!std::is_array_v) +template +[[nodiscard]] constexpr small_unique_ptr make_unique_small_for_overwrite() +noexcept(std::is_nothrow_default_constructible_v && !detail::is_always_heap_allocated_v) requires(!std::is_array_v) { - return detail::make_unique_small_impl::invoke_for_overwrite_scalar(); + return detail::make_unique_small_impl::invoke_for_overwrite_scalar(); } -template -[[nodiscard]] constexpr small_unique_ptr make_unique_small_for_overwrite(std::size_t count) requires(std::is_unbounded_array_v) +template +[[nodiscard]] constexpr small_unique_ptr make_unique_small_for_overwrite(std::size_t count) requires(std::is_unbounded_array_v) { - return detail::make_unique_small_impl::invoke_for_overwrite_array(count); + return detail::make_unique_small_impl::invoke_for_overwrite_array(count); } -template -[[nodiscard]] constexpr small_unique_ptr make_unique_small_for_overwrite(Args&&...) requires(std::is_bounded_array_v) = delete; +template +[[nodiscard]] constexpr small_unique_ptr make_unique_small_for_overwrite(Args&&...) requires(std::is_bounded_array_v) = delete; #endif // !SMALL_UNIQUE_PTR_HPP diff --git a/test/small_unique_ptr.cpp b/test/small_unique_ptr.cpp index efcdfd1..ad554af 100644 --- a/test/small_unique_ptr.cpp +++ b/test/small_unique_ptr.cpp @@ -90,7 +90,7 @@ TEST_CASE("object_layout", "[small_unique_ptr]") STATIC_REQUIRE(std::is_standard_layout_v>); } -TEST_CASE("object_size", "[small_unique_ptr]") +TEST_CASE("object_size_default", "[small_unique_ptr]") { STATIC_REQUIRE(sizeof(small_unique_ptr) <= 2 * sizeof(void*)); STATIC_REQUIRE(sizeof(small_unique_ptr) == sizeof(void*)); @@ -99,27 +99,38 @@ TEST_CASE("object_size", "[small_unique_ptr]") STATIC_REQUIRE(alignof(small_unique_ptr) == alignof(void*)); - STATIC_REQUIRE(sizeof(small_unique_ptr) == detail::small_ptr_size); + STATIC_REQUIRE(sizeof(small_unique_ptr) == detail::default_small_ptr_size); STATIC_REQUIRE(sizeof(small_unique_ptr) == sizeof(void*)); STATIC_REQUIRE(alignof(small_unique_ptr) == alignof(void*)); STATIC_REQUIRE(alignof(small_unique_ptr) == alignof(void*)); - STATIC_REQUIRE(sizeof(small_unique_ptr) == detail::small_ptr_size); + STATIC_REQUIRE(sizeof(small_unique_ptr) == detail::default_small_ptr_size); STATIC_REQUIRE(sizeof(small_unique_ptr) == sizeof(void*)); - STATIC_REQUIRE(alignof(small_unique_ptr) == detail::small_ptr_size); + STATIC_REQUIRE(alignof(small_unique_ptr) == detail::default_small_ptr_size); STATIC_REQUIRE(alignof(small_unique_ptr) == alignof(void*)); - STATIC_REQUIRE(sizeof(small_unique_ptr) == detail::small_ptr_size); + STATIC_REQUIRE(sizeof(small_unique_ptr) == detail::default_small_ptr_size); STATIC_REQUIRE(sizeof(small_unique_ptr) == sizeof(void*)); - STATIC_REQUIRE(alignof(small_unique_ptr) == detail::small_ptr_size); + STATIC_REQUIRE(alignof(small_unique_ptr) == detail::default_small_ptr_size); STATIC_REQUIRE(alignof(small_unique_ptr) == alignof(void*)); } +TEMPLATE_TEST_CASE("object_size_custom", "[small_unique_ptr]", Base, BaseIntrusive) +{ + STATIC_REQUIRE(sizeof(small_unique_ptr) == 24); + STATIC_REQUIRE(sizeof(small_unique_ptr) == 32); + STATIC_REQUIRE(sizeof(small_unique_ptr) == 40); + STATIC_REQUIRE(sizeof(small_unique_ptr) == 48); + STATIC_REQUIRE(sizeof(small_unique_ptr) == 56); + STATIC_REQUIRE(sizeof(small_unique_ptr) == 64); + STATIC_REQUIRE(sizeof(small_unique_ptr) == 128); +} + TEST_CASE("stack_buffer_size", "[small_unique_ptr]") { STATIC_REQUIRE(small_unique_ptr::stack_buffer_size() == sizeof(SmallPOD)); @@ -142,6 +153,14 @@ TEST_CASE("stack_buffer_size_archdep", "[small_unique_ptr][!mayfail]") REQUIRE(small_unique_ptr::stack_buffer_size() == 56); } +TEST_CASE("stack_array_size_archdep", "[small_unique_ptr][!mayfail]") +{ + REQUIRE(small_unique_ptr::stack_array_size() == 56); + REQUIRE(small_unique_ptr::stack_array_size() == 0); + + REQUIRE(small_unique_ptr::stack_array_size() > 0); +} + TEMPLATE_TEST_CASE("construction_scalar", "[small_unique_ptr]", SmallPOD, LargePOD, Base, SmallDerived, LargeDerived, BaseIntrusive, SmallIntrusive, LargeIntrusive) { STATIC_REQUIRE( std::invoke([]{ (void) small_unique_ptr(); return true; }) );