From 343d872be9f91e5fcb9167021790831458cbf19c Mon Sep 17 00:00:00 2001 From: Steven Le Rouzic Date: Fri, 1 Nov 2024 22:56:06 +0100 Subject: More work on option --- asl/option.hpp | 263 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 246 insertions(+), 17 deletions(-) (limited to 'asl/option.hpp') diff --git a/asl/option.hpp b/asl/option.hpp index ddd531d..2651a00 100644 --- a/asl/option.hpp +++ b/asl/option.hpp @@ -15,39 +15,117 @@ static constexpr nullopt_t nullopt{}; // @Todo(option) Function // @Todo(option) Arrays +template class option; + +namespace option_internal +{ + +template +concept convertible_from_option = + convertible&, T> && + convertible&, T> && + convertible&&, T> && + convertible&&, T>; + +template +concept constructible_from_option = + constructible&> && + constructible&> && + constructible&&> && + constructible&&>; + +template +concept assignable_from_option = + assignable&> && + assignable&> && + assignable&&> && + assignable&&>; + +template +concept convertible_constructible_from_option = + convertible_from_option && constructible_from_option; + + +template +concept convertible_constructible_assignable_from_option = + convertible_constructible_from_option && assignable_from_option; + +} // namespace option_internal + +template +concept is_option = requires +{ + typename T::type; + requires is_same, option>; +}; + template class option { - maybe_uninit m_payload; - bool m_has_value = false; + static constexpr bool kIsTrivial = + trivially_default_constructible && + trivially_copy_constructible && + trivially_move_constructible && + trivially_copy_assignable && + trivially_move_assignable && + trivially_destructible; + + using Storage = select_t>; + + Storage m_payload{}; + bool m_has_value = false; template constexpr void construct(Args&&... args) & { ASL_ASSERT(!m_has_value); - m_payload.init_unsafe(ASL_FWD(args)...); m_has_value = true; + if constexpr (kIsTrivial) + { + new(&m_payload) T(ASL_FWD(args)...); + } + else + { + m_payload.init_unsafe(ASL_FWD(args)...); + } } template constexpr void assign(Arg&& arg) & { ASL_ASSERT(m_has_value); - m_payload.as_init_unsafe() = ASL_FWD(arg); + if constexpr (kIsTrivial) + { + m_payload = ASL_FWD(arg); + } + else + { + m_payload.as_init_unsafe() = ASL_FWD(arg); + } } public: + using type = T; + constexpr option() = default; constexpr option(nullopt_t) {} // NOLINT(*-explicit-conversions) - template - // NOLINTNEXTLINE(*-explicit-conversions) - constexpr option(U&& value) requires constructible + template + constexpr explicit (!convertible) + option(U&& value) + requires ( + constructible && + !is_same, option> + ) { construct(ASL_FWD(value)); } + + constexpr option(const option& other) + requires copy_constructible && kIsTrivial = default; - constexpr option(const option& other) requires copy_constructible + constexpr option(const option& other) + requires copy_constructible && (!kIsTrivial) { if (other.m_has_value) { @@ -55,7 +133,14 @@ public: } } - constexpr option(option&& other) requires move_constructible + constexpr option(const option& other) + requires (!copy_constructible) = delete; + + constexpr option(option&& other) + requires move_constructible && kIsTrivial = default; + + constexpr option(option&& other) + requires move_constructible && (!kIsTrivial) { if (other.m_has_value) { @@ -63,7 +148,68 @@ public: } } - constexpr option& operator=(const option& other) & requires copy_assignable && copy_constructible + template + constexpr explicit (!convertible) + option(const option& other) + requires ( + constructible && + !option_internal::convertible_constructible_from_option + ) + { + if (other.has_value()) + { + construct(other.value()); + } + } + + template + constexpr explicit (!convertible) + option(option&& other) + requires ( + constructible && + !option_internal::convertible_constructible_from_option + ) + { + if (other.has_value()) + { + construct(ASL_MOVE(other).value()); + } + } + + constexpr option& operator=(nullopt_t) & + { + reset(); + return *this; + } + + template + constexpr option& operator=(U&& value) & + requires ( + assignable && + constructible && + !is_same, option> + ) + { + if (m_has_value) + { + assign(ASL_FWD(value)); + } + else + { + construct(ASL_FWD(value)); + } + + return *this; + } + + constexpr option& operator=(const option& other) & + requires (!copy_assignable || copy_constructible) = delete; + + constexpr option& operator=(const option& other) & + requires copy_assignable && copy_constructible && kIsTrivial = default; + + constexpr option& operator=(const option& other) & + requires copy_assignable && copy_constructible && (!kIsTrivial) { if (&other == this) { return *this; } @@ -78,7 +224,7 @@ public: construct(other.m_payload.as_init_unsafe()); } } - else if (m_has_value) + else { reset(); } @@ -86,7 +232,11 @@ public: return *this; } - constexpr option& operator=(option&& other) & requires move_assignable && move_constructible + constexpr option& operator=(option&& other) & + requires move_assignable && move_constructible && kIsTrivial = default; + + constexpr option& operator=(option&& other) & + requires move_assignable && move_constructible && (!kIsTrivial) { if (&other == this) { return *this; } @@ -101,13 +251,67 @@ public: construct(ASL_MOVE(other.m_payload.as_init_unsafe())); } } - else if (m_has_value) + else { reset(); } return *this; } + + template + constexpr option& operator=(const option& other) & + requires ( + constructible && + assignable && + !option_internal::convertible_constructible_assignable_from_option + ) + { + if (other.has_value()) + { + if (m_has_value) + { + assign(other.value()); + } + else + { + construct(other.value()); + } + } + else + { + reset(); + } + + return *this; + } + + template + constexpr option& operator=(option&& other) & + requires ( + constructible && + assignable && + !option_internal::convertible_constructible_assignable_from_option + ) + { + if (other.has_value()) + { + if (m_has_value) + { + assign(ASL_MOVE(other).value()); + } + else + { + construct(ASL_MOVE(other).value()); + } + } + else + { + reset(); + } + + return *this; + } constexpr ~option() = default; constexpr ~option() requires (!trivially_destructible) @@ -117,7 +321,11 @@ public: constexpr void reset() { - if (m_has_value) + if constexpr (kIsTrivial) + { + m_has_value = false; + } + else if (m_has_value) { m_payload.uninit_unsafe(); m_has_value = false; @@ -130,19 +338,40 @@ public: constexpr T&& value() && { ASL_ASSERT(m_has_value); // @Todo Release assert - return ASL_MOVE(m_payload).as_init_unsafe(); + if constexpr (kIsTrivial) + { + return ASL_MOVE(m_payload); + } + else + { + return ASL_MOVE(m_payload).as_init_unsafe(); + } } constexpr T& value() & { ASL_ASSERT(m_has_value); // @Todo Release assert - return m_payload.as_init_unsafe(); + if constexpr (kIsTrivial) + { + return m_payload; + } + else + { + return m_payload.as_init_unsafe(); + } } constexpr const T& value() const& { ASL_ASSERT(m_has_value); // @Todo Release assert - return m_payload.as_init_unsafe(); + if constexpr (kIsTrivial) + { + return m_payload; + } + else + { + return m_payload.as_init_unsafe(); + } } }; -- cgit