mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 00:50:22 +00:00
AK: Add OptionalBase
class to reduce code duplication
Using CRTP and `static_cast`s because "deducing this" is still not fully supported yet.
This commit is contained in:
parent
f80fca2dee
commit
a70ed6a2ad
Notes:
github-actions[bot]
2024-10-31 22:27:34 +00:00
Author: https://github.com/yyny Commit: https://github.com/LadybirdBrowser/ladybird/commit/a70ed6a2ada Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2032 Reviewed-by: https://github.com/Hendiadyoin1 Reviewed-by: https://github.com/awesomekling
3 changed files with 122 additions and 135 deletions
213
AK/Optional.h
213
AK/Optional.h
|
@ -48,8 +48,127 @@ struct OptionalNone {
|
|||
explicit OptionalNone() = default;
|
||||
};
|
||||
|
||||
template<typename T, typename Self = Optional<T>>
|
||||
requires(!IsLvalueReference<Self>) class [[nodiscard]] OptionalBase {
|
||||
public:
|
||||
using ValueType = T;
|
||||
|
||||
template<SameAs<OptionalNone> V>
|
||||
Self& operator=(V)
|
||||
{
|
||||
static_cast<Self&>(*this).clear();
|
||||
return static_cast<Self&>(*this);
|
||||
}
|
||||
|
||||
[[nodiscard]] ALWAYS_INLINE T* ptr() &
|
||||
{
|
||||
return static_cast<Self&>(*this).has_value() ? __builtin_launder(reinterpret_cast<T*>(&static_cast<Self&>(*this).value())) : nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] ALWAYS_INLINE T const* ptr() const&
|
||||
{
|
||||
return static_cast<Self const&>(*this).has_value() ? __builtin_launder(reinterpret_cast<T const*>(&static_cast<Self const&>(*this).value())) : nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] ALWAYS_INLINE T value_or(T const& fallback) const&
|
||||
{
|
||||
if (static_cast<Self const&>(*this).has_value())
|
||||
return static_cast<Self const&>(*this).value();
|
||||
return fallback;
|
||||
}
|
||||
|
||||
[[nodiscard]] ALWAYS_INLINE T value_or(T&& fallback) &&
|
||||
{
|
||||
if (static_cast<Self&>(*this).has_value())
|
||||
return move(static_cast<Self&>(*this).value());
|
||||
return move(fallback);
|
||||
}
|
||||
|
||||
template<typename Callback, typename O = T>
|
||||
[[nodiscard]] ALWAYS_INLINE O value_or_lazy_evaluated(Callback callback) const
|
||||
{
|
||||
if (static_cast<Self const&>(*this).has_value())
|
||||
return static_cast<Self const&>(*this).value();
|
||||
return callback();
|
||||
}
|
||||
|
||||
template<typename Callback, typename O = T>
|
||||
[[nodiscard]] ALWAYS_INLINE Optional<O> value_or_lazy_evaluated_optional(Callback callback) const
|
||||
{
|
||||
if (static_cast<Self const&>(*this).has_value())
|
||||
return static_cast<Self const&>(*this).value();
|
||||
return callback();
|
||||
}
|
||||
|
||||
template<typename Callback, typename O = T>
|
||||
[[nodiscard]] ALWAYS_INLINE ErrorOr<O> try_value_or_lazy_evaluated(Callback callback) const
|
||||
{
|
||||
if (static_cast<Self const&>(*this).has_value())
|
||||
return static_cast<Self const&>(*this).value();
|
||||
return TRY(callback());
|
||||
}
|
||||
|
||||
template<typename Callback, typename O = T>
|
||||
[[nodiscard]] ALWAYS_INLINE ErrorOr<Optional<O>> try_value_or_lazy_evaluated_optional(Callback callback) const
|
||||
{
|
||||
if (static_cast<Self const&>(*this).has_value())
|
||||
return static_cast<Self const&>(*this).value();
|
||||
return TRY(callback());
|
||||
}
|
||||
|
||||
template<typename O>
|
||||
ALWAYS_INLINE bool operator==(Optional<O> const& other) const
|
||||
{
|
||||
return static_cast<Self const&>(*this).has_value() == (other).has_value()
|
||||
&& (!static_cast<Self const&>(*this).has_value() || static_cast<Self const&>(*this).value() == (other).value());
|
||||
}
|
||||
|
||||
template<typename O>
|
||||
requires(!Detail::IsBaseOf<OptionalBase<T, Self>, O>)
|
||||
ALWAYS_INLINE bool operator==(O const& other) const
|
||||
{
|
||||
return static_cast<Self const&>(*this).has_value() && static_cast<Self const&>(*this).value() == other;
|
||||
}
|
||||
|
||||
[[nodiscard]] ALWAYS_INLINE T const& operator*() const { return static_cast<Self const&>(*this).value(); }
|
||||
[[nodiscard]] ALWAYS_INLINE T& operator*() { return static_cast<Self&>(*this).value(); }
|
||||
|
||||
ALWAYS_INLINE T const* operator->() const { return &static_cast<Self const&>(*this).value(); }
|
||||
ALWAYS_INLINE T* operator->() { return &static_cast<Self&>(*this).value(); }
|
||||
|
||||
template<typename F, typename MappedType = decltype(declval<F>()(declval<T&>())), auto IsErrorOr = IsSpecializationOf<MappedType, ErrorOr>, typename OptionalType = Optional<ConditionallyResultType<IsErrorOr, MappedType>>>
|
||||
ALWAYS_INLINE Conditional<IsErrorOr, ErrorOr<OptionalType>, OptionalType> map(F&& mapper)
|
||||
{
|
||||
if constexpr (IsErrorOr) {
|
||||
if (static_cast<Self&>(*this).has_value())
|
||||
return OptionalType { TRY(mapper(static_cast<Self&>(*this).value())) };
|
||||
return OptionalType {};
|
||||
} else {
|
||||
if (static_cast<Self&>(*this).has_value())
|
||||
return OptionalType { mapper(static_cast<Self&>(*this).value()) };
|
||||
|
||||
return OptionalType {};
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F, typename MappedType = decltype(declval<F>()(declval<T&>())), auto IsErrorOr = IsSpecializationOf<MappedType, ErrorOr>, typename OptionalType = Optional<ConditionallyResultType<IsErrorOr, MappedType>>>
|
||||
ALWAYS_INLINE Conditional<IsErrorOr, ErrorOr<OptionalType>, OptionalType> map(F&& mapper) const
|
||||
{
|
||||
if constexpr (IsErrorOr) {
|
||||
if (static_cast<Self const&>(*this).has_value())
|
||||
return OptionalType { TRY(mapper(static_cast<Self const&>(*this).value())) };
|
||||
return OptionalType {};
|
||||
} else {
|
||||
if (static_cast<Self const&>(*this).has_value())
|
||||
return OptionalType { mapper(static_cast<Self const&>(*this).value()) };
|
||||
|
||||
return OptionalType {};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
requires(!IsLvalueReference<T>) class [[nodiscard]] Optional<T> {
|
||||
requires(!IsLvalueReference<T>) class [[nodiscard]] Optional<T> : public OptionalBase<T, Optional<T>> {
|
||||
template<typename U>
|
||||
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<T*>(&m_storage)) : nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] ALWAYS_INLINE T const* ptr() const&
|
||||
{
|
||||
return m_has_value ? __builtin_launder(reinterpret_cast<T const*>(&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<typename Callback>
|
||||
[[nodiscard]] ALWAYS_INLINE T value_or_lazy_evaluated(Callback callback) const
|
||||
{
|
||||
if (m_has_value)
|
||||
return value();
|
||||
return callback();
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
[[nodiscard]] ALWAYS_INLINE Optional<T> value_or_lazy_evaluated_optional(Callback callback) const
|
||||
{
|
||||
if (m_has_value)
|
||||
return value();
|
||||
return callback();
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
[[nodiscard]] ALWAYS_INLINE ErrorOr<T> try_value_or_lazy_evaluated(Callback callback) const
|
||||
{
|
||||
if (m_has_value)
|
||||
return value();
|
||||
return TRY(callback());
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
[[nodiscard]] ALWAYS_INLINE ErrorOr<Optional<T>> 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<typename F, typename MappedType = decltype(declval<F>()(declval<T&>())), auto IsErrorOr = IsSpecializationOf<MappedType, ErrorOr>, typename OptionalType = Optional<ConditionallyResultType<IsErrorOr, MappedType>>>
|
||||
ALWAYS_INLINE Conditional<IsErrorOr, ErrorOr<OptionalType>, 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<typename F, typename MappedType = decltype(declval<F>()(declval<T&>())), auto IsErrorOr = IsSpecializationOf<MappedType, ErrorOr>, typename OptionalType = Optional<ConditionallyResultType<IsErrorOr, MappedType>>>
|
||||
ALWAYS_INLINE Conditional<IsErrorOr, ErrorOr<OptionalType>, 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 };
|
||||
|
|
|
@ -157,7 +157,7 @@ private:
|
|||
namespace AK {
|
||||
|
||||
template<>
|
||||
class Optional<JS::Completion> {
|
||||
class Optional<JS::Completion> : public OptionalBase<JS::Completion> {
|
||||
template<typename U>
|
||||
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 {} };
|
||||
};
|
||||
|
|
|
@ -599,7 +599,7 @@ namespace AK {
|
|||
static_assert(sizeof(JS::Value) == sizeof(double));
|
||||
|
||||
template<>
|
||||
class Optional<JS::Value> {
|
||||
class Optional<JS::Value> : public OptionalBase<JS::Value> {
|
||||
template<typename U>
|
||||
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;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue