Skip to content

Commit 27128d7

Browse files
Add detail::intrusive_small_ptr for type-erasure
1 parent 0e1d151 commit 27128d7

File tree

1 file changed

+142
-0
lines changed

1 file changed

+142
-0
lines changed
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2+
3+
#ifndef BEMAN_ANY_VIEW_DETAIL_INTRUSIVE_SMALL_PTR_HPP
4+
#define BEMAN_ANY_VIEW_DETAIL_INTRUSIVE_SMALL_PTR_HPP
5+
6+
#include <concepts>
7+
#include <cstddef>
8+
#include <memory>
9+
#include <type_traits>
10+
#include <utility>
11+
12+
namespace beman::any_view::detail {
13+
14+
template <class InterfaceT>
15+
concept interface_movable =
16+
std::has_virtual_destructor_v<InterfaceT> and requires(InterfaceT instance, void* destination) {
17+
{ instance.move_to(destination) } noexcept -> std::same_as<void>;
18+
};
19+
20+
template <class InterfaceT>
21+
concept interface_copyable = interface_movable<InterfaceT> and requires(const InterfaceT instance, void* destination) {
22+
{ instance.copy_to(destination) } -> std::same_as<void>;
23+
{ instance.copy() } -> std::same_as<InterfaceT*>;
24+
};
25+
26+
template <class T>
27+
using value_type_t = T::value_type;
28+
29+
enum class index_type : bool {
30+
is_inplace,
31+
is_pointer,
32+
};
33+
34+
template <interface_movable InterfaceT, std::size_t SizeV = sizeof(void*), std::size_t AlignV = alignof(void*)>
35+
class intrusive_small_ptr {
36+
struct alignas(AlignV) inplace_type {
37+
std::byte data[SizeV];
38+
};
39+
40+
using pointer_type = InterfaceT*;
41+
42+
union {
43+
// mutable inplace storage allows "shallow const" semantics consistent with a pointer type
44+
mutable inplace_type inplace;
45+
pointer_type pointer;
46+
};
47+
48+
index_type index;
49+
50+
[[nodiscard]] auto get_inplace_ptr() const -> pointer_type {
51+
return static_cast<pointer_type>(static_cast<void*>(&inplace));
52+
}
53+
54+
public:
55+
constexpr intrusive_small_ptr() noexcept : pointer(nullptr), index(index_type::is_pointer) {}
56+
57+
template <std::derived_from<InterfaceT> AdaptorT, class... ArgsT>
58+
requires std::constructible_from<AdaptorT, ArgsT...>
59+
constexpr intrusive_small_ptr([[maybe_unused]] std::in_place_type_t<AdaptorT> tag, ArgsT&&... args) {
60+
if constexpr (sizeof(AdaptorT) <= sizeof(inplace_type) and alignof(AdaptorT) <= alignof(inplace_type) and
61+
// SBO requires nothrow move construction
62+
std::is_nothrow_move_constructible_v<value_type_t<AdaptorT>>) {
63+
// placement new is not allowed in a constant expression
64+
if (not std::is_constant_evaluated()) {
65+
::new (&inplace) AdaptorT(std::forward<ArgsT>(args)...);
66+
index = index_type::is_inplace;
67+
return;
68+
}
69+
}
70+
71+
pointer = new AdaptorT(std::forward<ArgsT>(args)...);
72+
index = index_type::is_pointer;
73+
}
74+
75+
constexpr intrusive_small_ptr(const intrusive_small_ptr& other)
76+
requires interface_copyable<InterfaceT>
77+
: index(other.index) {
78+
if (index == index_type::is_inplace) {
79+
other.get_inplace_ptr()->copy_to(&inplace);
80+
} else {
81+
pointer = other.pointer->copy();
82+
}
83+
}
84+
85+
constexpr intrusive_small_ptr(intrusive_small_ptr&& other) noexcept : index(other.index) {
86+
if (index == index_type::is_inplace) {
87+
other.get_inplace_ptr()->move_to(&inplace);
88+
} else {
89+
pointer = std::exchange(other.pointer, nullptr);
90+
}
91+
}
92+
93+
constexpr auto operator=(const intrusive_small_ptr& other) -> intrusive_small_ptr&
94+
requires interface_copyable<InterfaceT>
95+
{
96+
// prevent self-assignment
97+
if (this == std::addressof(other)) {
98+
return *this;
99+
}
100+
101+
this->~intrusive_small_ptr();
102+
std::construct_at(this, other);
103+
return *this;
104+
}
105+
106+
constexpr auto operator=(intrusive_small_ptr&& other) noexcept -> intrusive_small_ptr& {
107+
this->~intrusive_small_ptr();
108+
std::construct_at(this, std::move(other));
109+
return *this;
110+
}
111+
112+
[[nodiscard]] constexpr explicit operator bool() const noexcept {
113+
return index == index_type::is_inplace or pointer != nullptr;
114+
}
115+
116+
[[nodiscard]] constexpr auto get() const noexcept -> InterfaceT* {
117+
if (index == index_type::is_inplace) {
118+
return get_inplace_ptr();
119+
}
120+
121+
return pointer;
122+
}
123+
124+
[[nodiscard]] constexpr auto operator*() const noexcept -> InterfaceT& { return *get(); }
125+
126+
[[nodiscard]] constexpr auto operator->() const noexcept -> InterfaceT* { return get(); }
127+
128+
constexpr ~intrusive_small_ptr() noexcept {
129+
if (index == index_type::is_inplace) {
130+
get_inplace_ptr()->~InterfaceT();
131+
} else {
132+
delete pointer;
133+
}
134+
135+
pointer = nullptr;
136+
index = index_type::is_pointer;
137+
}
138+
};
139+
140+
} // namespace beman::any_view::detail
141+
142+
#endif // BEMAN_ANY_VIEW_DETAIL_INTRUSIVE_SMALL_PTR_HPP

0 commit comments

Comments
 (0)