41class alignas(Alignment) polymorphic_optional {
43 using base_type = BaseType;
44 using pointer = base_type *;
45 using const_pointer = base_type
const *;
57 ~polymorphic_optional()
65 template<std::derived_from<base_type> Other>
67 _pointer(other.release())
71 template<std::derived_from<base_type> Other>
72 polymorphic_optional& operator=(std::unique_ptr<Other, std::default_delete<Other>>&& other)
noexcept
75 _pointer.store(
other.release(), std::memory_order::release);
78 template<std::derived_from<base_type> Other>
79 polymorphic_optional(Other&& other) noexcept : _pointer(
new Other(
std::forward<Other>(*other)))
83 template<std::derived_from<base_type> Other>
84 polymorphic_optional(Other&& other)
noexcept requires(
sizeof(Other) <= capacity and
alignof(Other) <= alignment) :
85 _pointer(new (_buffer.data()) Other(std::
forward<Other>(*
other)))
89 template<std::derived_from<base_type> Other>
90 polymorphic_optional& operator=(Other&& other)
noexcept
94 _pointer.store(new_ptr, std::memory_order::release);
98 template<std::derived_from<base_type> Other>
99 polymorphic_optional& operator=(Other&& other)
noexcept requires(
sizeof(Other) <= capacity and
alignof(Other) <= alignment)
103 _pointer.store(new_ptr, std::memory_order::release);
107 [[nodiscard]]
bool empty(std::memory_order memory_order = std::memory_order::seq_cst)
const noexcept
109 return _pointer.load(memory_order) ==
nullptr;
112 operator bool() const noexcept
117 template<
typename Value>
118 Value& value(std::memory_order memory_order = std::memory_order::seq_cst)
120 auto *ptr = _pointer.load(memory_order);
121 if (ptr ==
nullptr) {
122 throw std::bad_optional_access();
124 return down_cast<Value&>(*ptr);
127 template<
typename Value>
128 Value
const& value(std::memory_order memory_order = std::memory_order::seq_cst)
const
130 auto *ptr = _pointer.load(memory_order);
131 if (ptr ==
nullptr) {
132 throw std::bad_optional_access();
134 return down_cast<Value const&>(*ptr);
137 base_type *operator->() noexcept
139 return _pointer.load();
142 base_type
const *operator->() const noexcept
144 return _pointer.load();
147 base_type& operator*() noexcept
149 return *_pointer.load();
152 base_type
const& operator*() const noexcept
154 return *_pointer.load();
159 hi_force_inline
void reset() noexcept
161 if (
auto *ptr = _pointer.exchange(
nullptr, std::memory_order::acquire)) {
162 if (equal_ptr(ptr,
this)) {
163 std::destroy_at(ptr);
170 template<
typename Value,
typename... Args>
171 hi_force_inline Value& emplace(Args&&...args)
noexcept
175 if constexpr (
sizeof(Value) <= capacity and
alignof(Value) <= alignment) {
178 hi_axiom(equal_ptr(new_ptr,
this));
180 _pointer.store(new_ptr, std::memory_order::release);
188 hi_assert_not_null(new_ptr);
190 _pointer.store(new_ptr, std::memory_order::release);
201 template<
typename Func>
205 using result_type = std::conditional_t<std::is_same_v<func_result, void>, bool, std::optional<func_result>>;
208 if (
auto ptr = _pointer.load(std::memory_order::acquire)) {
209 if constexpr (std::is_same_v<func_result, void>) {
210 if (equal_ptr(ptr,
this)) {
212 std::destroy_at(ptr);
215 _pointer.store(
nullptr, std::memory_order::release);
220 _pointer.store(
nullptr, std::memory_order::release);
228 if (equal_ptr(ptr,
this)) {
230 std::destroy_at(ptr);
233 _pointer.store(
nullptr, std::memory_order::release);
238 _pointer.store(
nullptr, std::memory_order::release);
247 return result_type{};
258 template<
typename Value,
typename Func,
typename... Args>
267 while (_pointer.load(std::memory_order_acquire)) {
269 [[unlikely]] contended();
274 hi_assume(new_ptr !=
nullptr);
275 hi_axiom(equal_ptr(new_ptr,
this));
277 if constexpr (std::is_same_v<func_result, void>) {
282 _pointer.store(new_ptr, std::memory_order::release);
289 _pointer.store(new_ptr, std::memory_order::release);
298 hi_assert_not_null(new_ptr);
303 while (_pointer.load(std::memory_order::relaxed)) {
305 [[unlikely]] contended();
308 if constexpr (std::is_same_v<func_result, void>) {
313 _pointer.store(new_ptr, std::memory_order::release);
320 _pointer.store(new_ptr, std::memory_order::release);
340 hi_no_inline
void contended() noexcept
342 using namespace std::chrono_literals;
hi_force_inline auto wait_emplace_and_invoke(Func &&func, Args &&...args) noexcept
Wait until the optional is empty, emplace a value, then invoke a function on it before committing.
Definition polymorphic_optional.hpp:259