40 using base_type = BaseType;
42 using const_pointer = base_type
const *;
62 template<std::derived_from<base_type> Other>
64 _pointer(other.release())
68 template<std::derived_from<base_type> Other>
72 _pointer.store(other.release(), std::memory_order::release);
75 template<std::derived_from<base_type> Other>
76 polymorphic_optional(Other&& other) noexcept : _pointer(
new Other(std::forward<Other>(*other)))
80 template<std::derived_from<base_type> Other>
81 polymorphic_optional(Other&& other)
noexcept requires(
sizeof(Other) <= capacity and
alignof(Other) <= alignment) :
82 _pointer(new (_buffer.data()) Other(
std::
forward<Other>(*other)))
86 template<std::derived_from<base_type> Other>
87 polymorphic_optional& operator=(Other&& other)
noexcept
90 auto *new_ptr =
new Other(std::forward<Other>(other));
91 _pointer.store(new_ptr, std::memory_order::release);
95 template<std::derived_from<base_type> Other>
96 polymorphic_optional& operator=(Other&& other)
noexcept requires(
sizeof(Other) <= capacity and
alignof(Other) <= alignment)
99 auto *new_ptr =
new (_buffer.data()) Other(std::forward<Other>(other));
100 _pointer.store(new_ptr, std::memory_order::release);
104 [[nodiscard]]
bool empty(std::memory_order memory_order = std::memory_order::seq_cst)
const noexcept
106 return _pointer.load(memory_order) ==
nullptr;
109 operator bool() const noexcept
114 template<
typename Value>
115 Value& value(std::memory_order memory_order = std::memory_order::seq_cst)
117 auto *ptr = _pointer.load(memory_order);
118 if (ptr ==
nullptr) {
121 return down_cast<Value&>(*ptr);
124 template<
typename Value>
125 Value
const& value(std::memory_order memory_order = std::memory_order::seq_cst)
const
127 auto *ptr = _pointer.load(memory_order);
128 if (ptr ==
nullptr) {
131 return down_cast<Value const&>(*ptr);
134 base_type *operator->() noexcept
136 return _pointer.load();
139 base_type
const *operator->() const noexcept
141 return _pointer.load();
144 base_type& operator*() noexcept
146 return *_pointer.load();
149 base_type
const& operator*() const noexcept
151 return *_pointer.load();
156 hi_force_inline
void reset() noexcept
158 if (
auto *ptr = _pointer.exchange(
nullptr, std::memory_order::acquire)) {
159 if (equal_ptr(ptr,
this)) {
160 std::destroy_at(ptr);
167 template<
typename Value,
typename... Args>
168 hi_force_inline Value& emplace(Args&&...args)
noexcept
172 if constexpr (
sizeof(Value) <= capacity and
alignof(Value) <=
alignment) {
174 auto new_ptr =
new (_buffer.data()) Value(std::forward<Args>(args)...);
175 hi_axiom(equal_ptr(new_ptr,
this));
177 _pointer.store(new_ptr, std::memory_order::release);
184 hilet new_ptr =
new Value(std::forward<Args>(args)...);
185 hi_axiom(new_ptr !=
nullptr);
187 _pointer.store(new_ptr, std::memory_order::release);
198 template<
typename Func>
201 using func_result =
decltype(std::declval<Func>()(std::declval<base_type&>()));
202 using result_type = std::conditional_t<std::is_same_v<func_result, void>, bool, std::optional<func_result>>;
205 if (
auto ptr = _pointer.load(std::memory_order::acquire)) {
206 if constexpr (std::is_same_v<func_result, void>) {
207 if (equal_ptr(ptr,
this)) {
208 std::forward<Func>(func)(*ptr);
209 std::destroy_at(ptr);
212 _pointer.store(
nullptr, std::memory_order::release);
217 _pointer.store(
nullptr, std::memory_order::release);
219 std::forward<Func>(func)(*ptr);
225 if (equal_ptr(ptr,
this)) {
226 auto result = std::forward<Func>(func)(*ptr);
227 std::destroy_at(ptr);
230 _pointer.store(
nullptr, std::memory_order::release);
235 _pointer.store(
nullptr, std::memory_order::release);
237 auto result = std::forward<Func>(func)(*ptr);
244 return result_type{};
255 template<
typename Value,
typename Func,
typename... Args>
258 using func_result =
decltype(std::declval<Func>()(std::declval<Value&>()));
260 if constexpr (
sizeof(Value) <= capacity and
alignof(Value) <=
alignment) {
264 while (_pointer.load(std::memory_order_acquire)) {
266 [[unlikely]] contended();
270 auto new_ptr =
new (_buffer.data()) Value(std::forward<Args>(args)...);
271 hi_axiom(equal_ptr(new_ptr,
this));
273 if constexpr (std::is_same_v<func_result, void>) {
275 std::forward<Func>(func)(*new_ptr);
278 _pointer.store(new_ptr, std::memory_order::release);
282 auto tmp = std::forward<Func>(func)(*new_ptr);
285 _pointer.store(new_ptr, std::memory_order::release);
293 hilet new_ptr =
new Value(std::forward<Args>(args)...);
294 hi_axiom(new_ptr !=
nullptr);
299 while (_pointer.load(std::memory_order::relaxed)) {
301 [[unlikely]] contended();
304 if constexpr (std::is_same_v<func_result, void>) {
306 std::forward<Func>(func)(*new_ptr);
309 _pointer.store(new_ptr, std::memory_order::release);
313 auto tmp = std::forward<Func>(func)(*new_ptr);
316 _pointer.store(new_ptr, std::memory_order::release);
336 hi_no_inline
void contended() noexcept
338 using namespace std::chrono_literals;
341 ++global_counter<
"polymorphic_optional:contended">;
hi_force_inline void reset() noexcept
Destroys the contained value, otherwise has no effect.
Definition polymorphic_optional.hpp:156
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:256