43 using base_type = BaseType;
44 using pointer = base_type *;
45 using const_pointer = base_type
const *;
65 template<std::derived_from<base_type> Other>
67 _pointer(other.release())
71 template<std::derived_from<base_type> Other>
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) :
89 template<std::derived_from<base_type> Other>
90 polymorphic_optional& operator=(Other&& other)
noexcept
93 auto *new_ptr =
new Other(std::forward<Other>(other));
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)
102 auto *new_ptr =
new (_buffer.data()) Other(std::forward<Other>(other));
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) {
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) {
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) {
177 auto new_ptr =
new (_buffer.data()) Value(std::forward<Args>(args)...);
178 hi_axiom(equal_ptr(new_ptr,
this));
180 _pointer.store(new_ptr, std::memory_order::release);
187 hilet new_ptr =
new Value(std::forward<Args>(args)...);
188 hi_assert_not_null(new_ptr);
190 _pointer.store(new_ptr, std::memory_order::release);
201 template<
typename Func>
204 using func_result =
decltype(std::declval<Func>()(std::declval<base_type&>()));
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)) {
211 std::forward<Func>(func)(*ptr);
212 std::destroy_at(ptr);
215 _pointer.store(
nullptr, std::memory_order::release);
220 _pointer.store(
nullptr, std::memory_order::release);
222 std::forward<Func>(func)(*ptr);
228 if (equal_ptr(ptr,
this)) {
229 auto result = std::forward<Func>(func)(*ptr);
230 std::destroy_at(ptr);
233 _pointer.store(
nullptr, std::memory_order::release);
238 _pointer.store(
nullptr, std::memory_order::release);
240 auto result = std::forward<Func>(func)(*ptr);
247 return result_type{};
258 template<
typename Value,
typename Func,
typename... Args>
261 using func_result =
decltype(std::declval<Func>()(std::declval<Value&>()));
263 if constexpr (
sizeof(Value) <= capacity and
alignof(Value) <= alignment) {
267 while (_pointer.load(std::memory_order_acquire)) {
269 [[unlikely]] contended();
273 hilet new_ptr =
new (_buffer.data()) Value(std::forward<Args>(args)...);
274 hi_assume(new_ptr !=
nullptr);
275 hi_axiom(equal_ptr(new_ptr,
this));
277 if constexpr (std::is_same_v<func_result, void>) {
279 std::forward<Func>(func)(*new_ptr);
282 _pointer.store(new_ptr, std::memory_order::release);
286 auto tmp = std::forward<Func>(func)(*new_ptr);
289 _pointer.store(new_ptr, std::memory_order::release);
297 hilet new_ptr =
new Value(std::forward<Args>(args)...);
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>) {
310 std::forward<Func>(func)(*new_ptr);
313 _pointer.store(new_ptr, std::memory_order::release);
317 auto tmp = std::forward<Func>(func)(*new_ptr);
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 void reset() noexcept
Destroys the contained value, otherwise has no effect.
Definition polymorphic_optional.hpp:159
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