31 using base_type = BaseType;
33 using const_pointer = base_type
const *;
51 constexpr polymorphic_optional(std::nullopt_t) noexcept : polymorphic_optional() {}
53 template<std::derived_from<base_type> Other>
55 _pointer(other.release())
59 template<std::derived_from<base_type> Other>
63 _pointer.store(other.release(), std::memory_order::release);
66 template<std::derived_from<base_type> Other>
67 polymorphic_optional(Other&& other) noexcept : _pointer(
new Other(std::forward<Other>(*other)))
71 template<std::derived_from<base_type> Other>
72 polymorphic_optional(Other&& other)
noexcept requires(
sizeof(Other) <= capacity and
alignof(Other) <= alignment) :
73 _pointer(new (_buffer.data()) Other(
std::
forward<Other>(*other)))
77 template<std::derived_from<base_type> Other>
78 polymorphic_optional& operator=(Other&& other)
noexcept
81 auto *new_ptr =
new Other(std::forward<Other>(other));
82 _pointer.store(new_ptr, std::memory_order::release);
86 template<std::derived_from<base_type> Other>
87 polymorphic_optional& operator=(Other&& other)
noexcept requires(
sizeof(Other) <= capacity and
alignof(Other) <= alignment)
90 auto *new_ptr =
new (_buffer.data()) Other(std::forward<Other>(other));
91 _pointer.store(new_ptr, std::memory_order::release);
95 [[nodiscard]]
bool empty(std::memory_order memory_order = std::memory_order::seq_cst)
const noexcept
97 return _pointer.load(memory_order) ==
nullptr;
100 operator bool() const noexcept
105 template<
typename Value>
106 Value& value(std::memory_order memory_order = std::memory_order::seq_cst)
108 auto *ptr = _pointer.load(memory_order);
109 if (ptr ==
nullptr) {
112 return down_cast<Value&>(*ptr);
115 template<
typename Value>
116 Value
const& value(std::memory_order memory_order = std::memory_order::seq_cst)
const
118 auto *ptr = _pointer.load(memory_order);
119 if (ptr ==
nullptr) {
122 return down_cast<Value const&>(*ptr);
125 base_type *operator->() noexcept
127 return _pointer.load();
130 base_type
const *operator->() const noexcept
132 return _pointer.load();
135 base_type& operator*() noexcept
137 return *_pointer.load();
140 base_type
const& operator*() const noexcept
142 return *_pointer.load();
147 hi_force_inline
void reset() noexcept
149 if (
auto *ptr = _pointer.exchange(
nullptr, std::memory_order::acquire)) {
150 if (equal_ptr(ptr,
this)) {
151 std::destroy_at(ptr);
158 template<
typename Value,
typename... Args>
159 hi_force_inline Value& emplace(Args&&...args)
noexcept
163 if constexpr (
sizeof(Value) <= capacity and
alignof(Value) <=
alignment) {
165 auto new_ptr =
new (_buffer.data()) Value(std::forward<Args>(args)...);
166 hi_axiom(equal_ptr(new_ptr,
this));
168 _pointer.store(new_ptr, std::memory_order::release);
175 hilet new_ptr =
new Value(std::forward<Args>(args)...);
176 hi_axiom(new_ptr !=
nullptr);
178 _pointer.store(new_ptr, std::memory_order::release);
189 template<
typename Func>
192 using func_result =
decltype(std::declval<Func>()(std::declval<base_type&>()));
193 using result_type = std::conditional_t<std::is_same_v<func_result, void>, bool, std::optional<func_result>>;
196 if (
auto ptr = _pointer.load(std::memory_order::acquire)) {
197 if constexpr (std::is_same_v<func_result, void>) {
198 if (equal_ptr(ptr,
this)) {
199 std::forward<Func>(func)(*ptr);
200 std::destroy_at(ptr);
203 _pointer.store(
nullptr, std::memory_order::release);
208 _pointer.store(
nullptr, std::memory_order::release);
210 std::forward<Func>(func)(*ptr);
216 if (equal_ptr(ptr,
this)) {
217 auto result = std::forward<Func>(func)(*ptr);
218 std::destroy_at(ptr);
221 _pointer.store(
nullptr, std::memory_order::release);
226 _pointer.store(
nullptr, std::memory_order::release);
228 auto result = std::forward<Func>(func)(*ptr);
235 return result_type{};
246 template<
typename Value,
typename Func,
typename... Args>
249 using func_result =
decltype(std::declval<Func>()(std::declval<Value&>()));
251 if constexpr (
sizeof(Value) <= capacity and
alignof(Value) <=
alignment) {
255 while (_pointer.load(std::memory_order_acquire)) {
257 [[unlikely]] contended();
261 auto new_ptr =
new (_buffer.data()) Value(std::forward<Args>(args)...);
262 hi_axiom(equal_ptr(new_ptr,
this));
264 if constexpr (std::is_same_v<func_result, void>) {
266 std::forward<Func>(func)(*new_ptr);
269 _pointer.store(new_ptr, std::memory_order::release);
273 auto tmp = std::forward<Func>(func)(*new_ptr);
276 _pointer.store(new_ptr, std::memory_order::release);
284 hilet new_ptr =
new Value(std::forward<Args>(args)...);
285 hi_axiom(new_ptr !=
nullptr);
290 while (_pointer.load(std::memory_order::relaxed)) {
292 [[unlikely]] contended();
295 if constexpr (std::is_same_v<func_result, void>) {
297 std::forward<Func>(func)(*new_ptr);
300 _pointer.store(new_ptr, std::memory_order::release);
304 auto tmp = std::forward<Func>(func)(*new_ptr);
307 _pointer.store(new_ptr, std::memory_order::release);
327 hi_no_inline
void contended() noexcept
329 using namespace std::chrono_literals;
332 ++global_counter<
"polymorphic_optional:contended">;
hi_force_inline void reset() noexcept
Destroys the contained value, otherwise has no effect.
Definition polymorphic_optional.hpp:147
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:247