From 27128d760d7c95eb4265bdcbcd492af89fb2d1fe Mon Sep 17 00:00:00 2001 From: Patrick Roberts Date: Mon, 3 Feb 2025 00:15:14 -0600 Subject: [PATCH] Add detail::intrusive_small_ptr for type-erasure --- .../any_view/detail/intrusive_small_ptr.hpp | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 include/beman/any_view/detail/intrusive_small_ptr.hpp diff --git a/include/beman/any_view/detail/intrusive_small_ptr.hpp b/include/beman/any_view/detail/intrusive_small_ptr.hpp new file mode 100644 index 0000000..b235318 --- /dev/null +++ b/include/beman/any_view/detail/intrusive_small_ptr.hpp @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef BEMAN_ANY_VIEW_DETAIL_INTRUSIVE_SMALL_PTR_HPP +#define BEMAN_ANY_VIEW_DETAIL_INTRUSIVE_SMALL_PTR_HPP + +#include +#include +#include +#include +#include + +namespace beman::any_view::detail { + +template +concept interface_movable = + std::has_virtual_destructor_v and requires(InterfaceT instance, void* destination) { + { instance.move_to(destination) } noexcept -> std::same_as; + }; + +template +concept interface_copyable = interface_movable and requires(const InterfaceT instance, void* destination) { + { instance.copy_to(destination) } -> std::same_as; + { instance.copy() } -> std::same_as; +}; + +template +using value_type_t = T::value_type; + +enum class index_type : bool { + is_inplace, + is_pointer, +}; + +template +class intrusive_small_ptr { + struct alignas(AlignV) inplace_type { + std::byte data[SizeV]; + }; + + using pointer_type = InterfaceT*; + + union { + // mutable inplace storage allows "shallow const" semantics consistent with a pointer type + mutable inplace_type inplace; + pointer_type pointer; + }; + + index_type index; + + [[nodiscard]] auto get_inplace_ptr() const -> pointer_type { + return static_cast(static_cast(&inplace)); + } + + public: + constexpr intrusive_small_ptr() noexcept : pointer(nullptr), index(index_type::is_pointer) {} + + template AdaptorT, class... ArgsT> + requires std::constructible_from + constexpr intrusive_small_ptr([[maybe_unused]] std::in_place_type_t tag, ArgsT&&... args) { + if constexpr (sizeof(AdaptorT) <= sizeof(inplace_type) and alignof(AdaptorT) <= alignof(inplace_type) and + // SBO requires nothrow move construction + std::is_nothrow_move_constructible_v>) { + // placement new is not allowed in a constant expression + if (not std::is_constant_evaluated()) { + ::new (&inplace) AdaptorT(std::forward(args)...); + index = index_type::is_inplace; + return; + } + } + + pointer = new AdaptorT(std::forward(args)...); + index = index_type::is_pointer; + } + + constexpr intrusive_small_ptr(const intrusive_small_ptr& other) + requires interface_copyable + : index(other.index) { + if (index == index_type::is_inplace) { + other.get_inplace_ptr()->copy_to(&inplace); + } else { + pointer = other.pointer->copy(); + } + } + + constexpr intrusive_small_ptr(intrusive_small_ptr&& other) noexcept : index(other.index) { + if (index == index_type::is_inplace) { + other.get_inplace_ptr()->move_to(&inplace); + } else { + pointer = std::exchange(other.pointer, nullptr); + } + } + + constexpr auto operator=(const intrusive_small_ptr& other) -> intrusive_small_ptr& + requires interface_copyable + { + // prevent self-assignment + if (this == std::addressof(other)) { + return *this; + } + + this->~intrusive_small_ptr(); + std::construct_at(this, other); + return *this; + } + + constexpr auto operator=(intrusive_small_ptr&& other) noexcept -> intrusive_small_ptr& { + this->~intrusive_small_ptr(); + std::construct_at(this, std::move(other)); + return *this; + } + + [[nodiscard]] constexpr explicit operator bool() const noexcept { + return index == index_type::is_inplace or pointer != nullptr; + } + + [[nodiscard]] constexpr auto get() const noexcept -> InterfaceT* { + if (index == index_type::is_inplace) { + return get_inplace_ptr(); + } + + return pointer; + } + + [[nodiscard]] constexpr auto operator*() const noexcept -> InterfaceT& { return *get(); } + + [[nodiscard]] constexpr auto operator->() const noexcept -> InterfaceT* { return get(); } + + constexpr ~intrusive_small_ptr() noexcept { + if (index == index_type::is_inplace) { + get_inplace_ptr()->~InterfaceT(); + } else { + delete pointer; + } + + pointer = nullptr; + index = index_type::is_pointer; + } +}; + +} // namespace beman::any_view::detail + +#endif // BEMAN_ANY_VIEW_DETAIL_INTRUSIVE_SMALL_PTR_HPP