From a70ed6a2ada94df63e12c94d35166b7d4ca0b90a Mon Sep 17 00:00:00 2001 From: Jonne Ransijn Date: Tue, 29 Oct 2024 11:35:36 +0100 Subject: [PATCH] AK: Add `OptionalBase` class to reduce code duplication Using CRTP and `static_cast`s because "deducing this" is still not fully supported yet. --- AK/Optional.h | 213 ++++++++++-------- Userland/Libraries/LibJS/Runtime/Completion.h | 22 +- Userland/Libraries/LibJS/Runtime/Value.h | 22 +- 3 files changed, 122 insertions(+), 135 deletions(-) diff --git a/AK/Optional.h b/AK/Optional.h index 6ebee5ecfa8..77bd8fe22cf 100644 --- a/AK/Optional.h +++ b/AK/Optional.h @@ -48,8 +48,127 @@ struct OptionalNone { explicit OptionalNone() = default; }; +template> +requires(!IsLvalueReference) class [[nodiscard]] OptionalBase { +public: + using ValueType = T; + + template V> + Self& operator=(V) + { + static_cast(*this).clear(); + return static_cast(*this); + } + + [[nodiscard]] ALWAYS_INLINE T* ptr() & + { + return static_cast(*this).has_value() ? __builtin_launder(reinterpret_cast(&static_cast(*this).value())) : nullptr; + } + + [[nodiscard]] ALWAYS_INLINE T const* ptr() const& + { + return static_cast(*this).has_value() ? __builtin_launder(reinterpret_cast(&static_cast(*this).value())) : nullptr; + } + + [[nodiscard]] ALWAYS_INLINE T value_or(T const& fallback) const& + { + if (static_cast(*this).has_value()) + return static_cast(*this).value(); + return fallback; + } + + [[nodiscard]] ALWAYS_INLINE T value_or(T&& fallback) && + { + if (static_cast(*this).has_value()) + return move(static_cast(*this).value()); + return move(fallback); + } + + template + [[nodiscard]] ALWAYS_INLINE O value_or_lazy_evaluated(Callback callback) const + { + if (static_cast(*this).has_value()) + return static_cast(*this).value(); + return callback(); + } + + template + [[nodiscard]] ALWAYS_INLINE Optional value_or_lazy_evaluated_optional(Callback callback) const + { + if (static_cast(*this).has_value()) + return static_cast(*this).value(); + return callback(); + } + + template + [[nodiscard]] ALWAYS_INLINE ErrorOr try_value_or_lazy_evaluated(Callback callback) const + { + if (static_cast(*this).has_value()) + return static_cast(*this).value(); + return TRY(callback()); + } + + template + [[nodiscard]] ALWAYS_INLINE ErrorOr> try_value_or_lazy_evaluated_optional(Callback callback) const + { + if (static_cast(*this).has_value()) + return static_cast(*this).value(); + return TRY(callback()); + } + + template + ALWAYS_INLINE bool operator==(Optional const& other) const + { + return static_cast(*this).has_value() == (other).has_value() + && (!static_cast(*this).has_value() || static_cast(*this).value() == (other).value()); + } + + template + requires(!Detail::IsBaseOf, O>) + ALWAYS_INLINE bool operator==(O const& other) const + { + return static_cast(*this).has_value() && static_cast(*this).value() == other; + } + + [[nodiscard]] ALWAYS_INLINE T const& operator*() const { return static_cast(*this).value(); } + [[nodiscard]] ALWAYS_INLINE T& operator*() { return static_cast(*this).value(); } + + ALWAYS_INLINE T const* operator->() const { return &static_cast(*this).value(); } + ALWAYS_INLINE T* operator->() { return &static_cast(*this).value(); } + + template()(declval())), auto IsErrorOr = IsSpecializationOf, typename OptionalType = Optional>> + ALWAYS_INLINE Conditional, OptionalType> map(F&& mapper) + { + if constexpr (IsErrorOr) { + if (static_cast(*this).has_value()) + return OptionalType { TRY(mapper(static_cast(*this).value())) }; + return OptionalType {}; + } else { + if (static_cast(*this).has_value()) + return OptionalType { mapper(static_cast(*this).value()) }; + + return OptionalType {}; + } + } + + template()(declval())), auto IsErrorOr = IsSpecializationOf, typename OptionalType = Optional>> + ALWAYS_INLINE Conditional, OptionalType> map(F&& mapper) const + { + if constexpr (IsErrorOr) { + if (static_cast(*this).has_value()) + return OptionalType { TRY(mapper(static_cast(*this).value())) }; + return OptionalType {}; + } else { + if (static_cast(*this).has_value()) + return OptionalType { mapper(static_cast(*this).value()) }; + + return OptionalType {}; + } + } +}; + template -requires(!IsLvalueReference) class [[nodiscard]] Optional { +requires(!IsLvalueReference) class [[nodiscard]] Optional : public OptionalBase> { template friend class Optional; @@ -202,16 +321,6 @@ public: [[nodiscard]] ALWAYS_INLINE bool has_value() const { return m_has_value; } - [[nodiscard]] ALWAYS_INLINE T* ptr() & - { - return m_has_value ? __builtin_launder(reinterpret_cast(&m_storage)) : nullptr; - } - - [[nodiscard]] ALWAYS_INLINE T const* ptr() const& - { - return m_has_value ? __builtin_launder(reinterpret_cast(&m_storage)) : nullptr; - } - [[nodiscard]] ALWAYS_INLINE T& value() & { VERIFY(m_has_value); @@ -238,88 +347,6 @@ public: return released_value; } - [[nodiscard]] ALWAYS_INLINE T value_or(T const& fallback) const& - { - if (m_has_value) - return value(); - return fallback; - } - - [[nodiscard]] ALWAYS_INLINE T value_or(T&& fallback) && - { - if (m_has_value) - return move(value()); - return move(fallback); - } - - template - [[nodiscard]] ALWAYS_INLINE T value_or_lazy_evaluated(Callback callback) const - { - if (m_has_value) - return value(); - return callback(); - } - - template - [[nodiscard]] ALWAYS_INLINE Optional value_or_lazy_evaluated_optional(Callback callback) const - { - if (m_has_value) - return value(); - return callback(); - } - - template - [[nodiscard]] ALWAYS_INLINE ErrorOr try_value_or_lazy_evaluated(Callback callback) const - { - if (m_has_value) - return value(); - return TRY(callback()); - } - - template - [[nodiscard]] ALWAYS_INLINE ErrorOr> try_value_or_lazy_evaluated_optional(Callback callback) const - { - if (m_has_value) - return value(); - return TRY(callback()); - } - - [[nodiscard]] ALWAYS_INLINE T const& operator*() const { return value(); } - [[nodiscard]] ALWAYS_INLINE T& operator*() { return value(); } - - ALWAYS_INLINE T const* operator->() const { return &value(); } - ALWAYS_INLINE T* operator->() { return &value(); } - - template()(declval())), auto IsErrorOr = IsSpecializationOf, typename OptionalType = Optional>> - ALWAYS_INLINE Conditional, OptionalType> map(F&& mapper) - { - if constexpr (IsErrorOr) { - if (m_has_value) - return OptionalType { TRY(mapper(value())) }; - return OptionalType {}; - } else { - if (m_has_value) - return OptionalType { mapper(value()) }; - - return OptionalType {}; - } - } - - template()(declval())), auto IsErrorOr = IsSpecializationOf, typename OptionalType = Optional>> - ALWAYS_INLINE Conditional, OptionalType> map(F&& mapper) const - { - if constexpr (IsErrorOr) { - if (m_has_value) - return OptionalType { TRY(mapper(value())) }; - return OptionalType {}; - } else { - if (m_has_value) - return OptionalType { mapper(value()) }; - - return OptionalType {}; - } - } - private: alignas(T) u8 m_storage[sizeof(T)]; bool m_has_value { false }; diff --git a/Userland/Libraries/LibJS/Runtime/Completion.h b/Userland/Libraries/LibJS/Runtime/Completion.h index 3c25c7ae921..b599a0911d1 100644 --- a/Userland/Libraries/LibJS/Runtime/Completion.h +++ b/Userland/Libraries/LibJS/Runtime/Completion.h @@ -157,7 +157,7 @@ private: namespace AK { template<> -class Optional { +class Optional : public OptionalBase { template friend class Optional; @@ -237,26 +237,6 @@ public: return released_value; } - JS::Completion value_or(JS::Completion const& fallback) const& - { - if (has_value()) - return value(); - return fallback; - } - - [[nodiscard]] JS::Completion value_or(JS::Completion&& fallback) && - { - if (has_value()) - return value(); - return fallback; - } - - JS::Completion const& operator*() const { return value(); } - JS::Completion& operator*() { return value(); } - - JS::Completion const* operator->() const { return &value(); } - JS::Completion* operator->() { return &value(); } - private: JS::Completion m_value { JS::Completion::EmptyTag {} }; }; diff --git a/Userland/Libraries/LibJS/Runtime/Value.h b/Userland/Libraries/LibJS/Runtime/Value.h index 2cb4a536272..052587d4165 100644 --- a/Userland/Libraries/LibJS/Runtime/Value.h +++ b/Userland/Libraries/LibJS/Runtime/Value.h @@ -599,7 +599,7 @@ namespace AK { static_assert(sizeof(JS::Value) == sizeof(double)); template<> -class Optional { +class Optional : public OptionalBase { template friend class Optional; @@ -702,26 +702,6 @@ public: return released_value; } - JS::Value value_or(JS::Value const& fallback) const& - { - if (has_value()) - return value(); - return fallback; - } - - [[nodiscard]] JS::Value value_or(JS::Value&& fallback) && - { - if (has_value()) - return value(); - return fallback; - } - - JS::Value const& operator*() const { return value(); } - JS::Value& operator*() { return value(); } - - JS::Value const* operator->() const { return &value(); } - JS::Value* operator->() { return &value(); } - private: JS::Value m_value; };