41 using base_type = BaseType;
43 using const_pointer = base_type
const *;
63 template<std::derived_from<base_type> Other>
65 _pointer(other.release())
69 template<std::derived_from<base_type> Other>
73 _pointer.store(other.release(), std::memory_order::release);
76 template<std::derived_from<base_type> Other>
77 polymorphic_optional(Other&& other) noexcept : _pointer(
new Other(std::forward<Other>(*other)))
81 template<std::derived_from<base_type> Other>
82 polymorphic_optional(Other&& other)
noexcept requires(
sizeof(Other) <= capacity and
alignof(Other) <= alignment) :
83 _pointer(new (_buffer.data()) Other(
std::
forward<Other>(*other)))
87 template<std::derived_from<base_type> Other>
88 polymorphic_optional& operator=(Other&& other)
noexcept
91 auto *new_ptr =
new Other(std::forward<Other>(other));
92 _pointer.store(new_ptr, std::memory_order::release);
96 template<std::derived_from<base_type> Other>
97 polymorphic_optional& operator=(Other&& other)
noexcept requires(
sizeof(Other) <= capacity and
alignof(Other) <= alignment)
100 auto *new_ptr =
new (_buffer.data()) Other(std::forward<Other>(other));
101 _pointer.store(new_ptr, std::memory_order::release);
105 [[nodiscard]]
bool empty(std::memory_order memory_order = std::memory_order::seq_cst)
const noexcept
107 return _pointer.load(memory_order) ==
nullptr;
110 operator bool() const noexcept
115 template<
typename Value>
116 Value& value(std::memory_order memory_order = std::memory_order::seq_cst)
118 auto *ptr = _pointer.load(memory_order);
119 if (ptr ==
nullptr) {
122 return down_cast<Value&>(*ptr);
125 template<
typename Value>
126 Value
const& value(std::memory_order memory_order = std::memory_order::seq_cst)
const
128 auto *ptr = _pointer.load(memory_order);
129 if (ptr ==
nullptr) {
132 return down_cast<Value const&>(*ptr);
135 base_type *operator->() noexcept
137 return _pointer.load();
140 base_type
const *operator->() const noexcept
142 return _pointer.load();
145 base_type& operator*() noexcept
147 return *_pointer.load();
150 base_type
const& operator*() const noexcept
152 return *_pointer.load();
157 hi_force_inline
void reset() noexcept
159 if (
auto *ptr = _pointer.exchange(
nullptr, std::memory_order::acquire)) {
160 if (equal_ptr(ptr,
this)) {
161 std::destroy_at(ptr);
168 template<
typename Value,
typename... Args>
169 hi_force_inline Value& emplace(Args&&...args)
noexcept
173 if constexpr (
sizeof(Value) <= capacity and
alignof(Value) <=
alignment) {
175 auto new_ptr =
new (_buffer.data()) Value(std::forward<Args>(args)...);
178 _pointer.store(new_ptr, std::memory_order::release);
185 hilet new_ptr =
new Value(std::forward<Args>(args)...);
188 _pointer.store(new_ptr, std::memory_order::release);
199 template<
typename Func>
202 using func_result =
decltype(std::declval<Func>()(std::declval<base_type&>()));
203 using result_type = std::conditional_t<std::is_same_v<func_result, void>, bool, std::optional<func_result>>;
206 if (
auto ptr = _pointer.load(std::memory_order::acquire)) {
207 if constexpr (std::is_same_v<func_result, void>) {
208 if (equal_ptr(ptr,
this)) {
209 std::forward<Func>(func)(*ptr);
210 std::destroy_at(ptr);
213 _pointer.store(
nullptr, std::memory_order::release);
218 _pointer.store(
nullptr, std::memory_order::release);
220 std::forward<Func>(func)(*ptr);
226 if (equal_ptr(ptr,
this)) {
227 auto result = std::forward<Func>(func)(*ptr);
228 std::destroy_at(ptr);
231 _pointer.store(
nullptr, std::memory_order::release);
236 _pointer.store(
nullptr, std::memory_order::release);
238 auto result = std::forward<Func>(func)(*ptr);
245 return result_type{};
256 template<
typename Value,
typename Func,
typename... Args>
259 using func_result =
decltype(std::declval<Func>()(std::declval<Value&>()));
261 if constexpr (
sizeof(Value) <= capacity and
alignof(Value) <=
alignment) {
265 while (_pointer.load(std::memory_order_acquire)) {
267 [[unlikely]] contended();
271 auto new_ptr =
new (_buffer.data()) Value(std::forward<Args>(args)...);
274 if constexpr (std::is_same_v<func_result, void>) {
276 std::forward<Func>(func)(*new_ptr);
279 _pointer.store(new_ptr, std::memory_order::release);
283 auto tmp = std::forward<Func>(func)(*new_ptr);
286 _pointer.store(new_ptr, std::memory_order::release);
294 hilet new_ptr =
new Value(std::forward<Args>(args)...);
300 while (_pointer.load(std::memory_order::relaxed)) {
302 [[unlikely]] contended();
305 if constexpr (std::is_same_v<func_result, void>) {
307 std::forward<Func>(func)(*new_ptr);
310 _pointer.store(new_ptr, std::memory_order::release);
314 auto tmp = std::forward<Func>(func)(*new_ptr);
317 _pointer.store(new_ptr, std::memory_order::release);
337 hi_no_inline
void contended() noexcept
339 using namespace std::chrono_literals;
342 ++global_counter<
"polymorphic_optional:contended">;
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:133
#define hi_assert_not_null(x,...)
Assert if an expression is not nullptr.
Definition assert.hpp:118
hi_force_inline void reset() noexcept
Destroys the contained value, otherwise has no effect.
Definition polymorphic_optional.hpp:157
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:257