/* * Copyright (c) Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include #include namespace ams::util { namespace impl { class NulloptHelper { public: template static consteval T CreateInstance() { return T(T::ConstructionArgument::Token); } }; } struct nullopt_t { private: friend class impl::NulloptHelper; enum class ConstructionArgument { Token, }; public: consteval nullopt_t(ConstructionArgument) { /* ... */ } }; constexpr inline nullopt_t nullopt = impl::NulloptHelper::CreateInstance(); namespace impl { template struct OptionalPayloadBase { using StoredType = typename std::remove_const::type; struct EmptyType{}; template::value> union StorageType { EmptyType m_empty; U m_value; constexpr ALWAYS_INLINE StorageType() : m_empty() { /* ... */ } template constexpr ALWAYS_INLINE StorageType(in_place_t, Args &&... args) : m_value(std::forward(args)...) { /* ... */ } template constexpr ALWAYS_INLINE StorageType(std::initializer_list il, Args &&... args) : m_value(il, std::forward(args)...) { /* ... */ } }; template union StorageType { EmptyType m_empty; U m_value; constexpr ALWAYS_INLINE StorageType() : m_empty() { /* ... */ } template constexpr ALWAYS_INLINE StorageType(in_place_t, Args &&... args) : m_value(std::forward(args)...) { /* ... */ } template constexpr ALWAYS_INLINE StorageType(std::initializer_list il, Args &&... args) : m_value(il, std::forward(args)...) { /* ... */ } constexpr ALWAYS_INLINE ~StorageType() { /* ... */ } }; StorageType m_payload; bool m_engaged = false; constexpr OptionalPayloadBase() = default; constexpr ~OptionalPayloadBase() = default; template constexpr OptionalPayloadBase(in_place_t tag, Args &&... args) : m_payload(tag, std::forward(args)...), m_engaged(true) { /* ... */ } template constexpr OptionalPayloadBase(std::initializer_list il, Args &&... args) : m_payload(il, std::forward(args)...), m_engaged(true) { /* ... */ } constexpr OptionalPayloadBase(bool engaged, const OptionalPayloadBase &rhs) { AMS_UNUSED(engaged); if (rhs.m_engaged) { this->Construct(rhs.Get()); } } constexpr OptionalPayloadBase(bool engaged, OptionalPayloadBase &&rhs) { AMS_UNUSED(engaged); if (rhs.m_engaged) { this->Construct(std::move(rhs.Get())); } } constexpr OptionalPayloadBase(const OptionalPayloadBase &) = default; constexpr OptionalPayloadBase(OptionalPayloadBase &&) = default; constexpr OptionalPayloadBase &operator=(const OptionalPayloadBase &) = default; constexpr OptionalPayloadBase &operator=(OptionalPayloadBase &&) = default; constexpr void CopyAssign(const OptionalPayloadBase &rhs) { if (m_engaged && rhs.m_engaged) { this->Get() = rhs.Get(); } else if (rhs.m_engaged) { this->Construct(rhs.Get()); } else { this->Reset(); } } constexpr void MoveAssign(OptionalPayloadBase &&rhs) { if (m_engaged && rhs.m_engaged) { this->Get() = std::move(rhs.Get()); } else if (rhs.m_engaged) { this->Construct(std::move(rhs.Get())); } else { this->Reset(); } } template constexpr void Construct(Args &&... args) { std::construct_at(std::addressof(m_payload.m_value), std::forward(args)...); m_engaged = true; } constexpr void Destroy() { m_engaged = false; std::destroy_at(std::addressof(m_payload.m_value)); } constexpr ALWAYS_INLINE T &Get() { return m_payload.m_value; } constexpr ALWAYS_INLINE const T &Get() const { return m_payload.m_value; } constexpr void Reset() { if (m_engaged) { this->Destroy(); } } }; template::value, bool = std::is_trivially_copy_assignable::value && std::is_trivially_copy_constructible::value, bool = std::is_trivially_move_assignable::value && std::is_trivially_move_constructible::value> struct OptionalPayload; template struct OptionalPayload : OptionalPayloadBase { using OptionalPayloadBase::OptionalPayloadBase; constexpr OptionalPayload() = default; }; template struct OptionalPayload : OptionalPayloadBase { using OptionalPayloadBase::OptionalPayloadBase; constexpr OptionalPayload() = default; constexpr ~OptionalPayload() = default; constexpr OptionalPayload(const OptionalPayload &) = default; constexpr OptionalPayload(OptionalPayload &&) = default; constexpr OptionalPayload& operator=(OptionalPayload &&) = default; constexpr OptionalPayload &operator=(const OptionalPayload &rhs) { this->CopyAssign(rhs); return *this; } }; template struct OptionalPayload : OptionalPayloadBase { using OptionalPayloadBase::OptionalPayloadBase; constexpr OptionalPayload() = default; constexpr ~OptionalPayload() = default; constexpr OptionalPayload(const OptionalPayload &) = default; constexpr OptionalPayload(OptionalPayload &&) = default; constexpr OptionalPayload& operator=(const OptionalPayload &) = default; constexpr OptionalPayload &operator=(OptionalPayload &&rhs) { this->MoveAssign(std::move(rhs)); return *this; } }; template struct OptionalPayload : OptionalPayloadBase { using OptionalPayloadBase::OptionalPayloadBase; constexpr OptionalPayload() = default; constexpr ~OptionalPayload() = default; constexpr OptionalPayload(const OptionalPayload &) = default; constexpr OptionalPayload(OptionalPayload &&) = default; constexpr OptionalPayload &operator=(const OptionalPayload &rhs) { this->CopyAssign(rhs); return *this; } constexpr OptionalPayload &operator=(OptionalPayload &&rhs) { this->MoveAssign(std::move(rhs)); return *this; } }; template struct OptionalPayload : OptionalPayload { using OptionalPayload::OptionalPayload; constexpr OptionalPayload() = default; constexpr OptionalPayload(const OptionalPayload &) = default; constexpr OptionalPayload(OptionalPayload &&) = default; constexpr OptionalPayload& operator=(const OptionalPayload &) = default; constexpr OptionalPayload& operator=(OptionalPayload &&) = default; constexpr ~OptionalPayload() { this->Reset(); } }; template class OptionalBaseImpl { protected: using StoredType = std::remove_const_t; template constexpr void ConstructImpl(Args &&... args) { static_cast(this)->m_payload.Construct(std::forward(args)...); } constexpr void DestructImpl() { static_cast(this)->m_payload.Destroy(); } constexpr void ResetImpl() { static_cast(this)->m_payload.Reset(); } constexpr ALWAYS_INLINE bool IsEngagedImpl() const { return static_cast(this)->m_payload.m_engaged; } constexpr ALWAYS_INLINE T &GetImpl() { return static_cast(this)->m_payload.Get(); } constexpr ALWAYS_INLINE const T &GetImpl() const { return static_cast(this)->m_payload.Get(); } }; template::value, bool = std::is_trivially_move_constructible::value> struct OptionalBase : OptionalBaseImpl> { OptionalPayload m_payload; constexpr OptionalBase() = default; template::value, bool> = false> constexpr explicit OptionalBase(in_place_t, Args &&... args) : m_payload(in_place, std::forward(args)...) { /* ... */ } template &, Args...>::value, bool> = false> constexpr explicit OptionalBase(in_place_t, std::initializer_list il, Args &&... args) : m_payload(in_place, il, std::forward(args)...) { /* ... */ } constexpr OptionalBase(const OptionalBase &rhs) : m_payload(rhs.m_payload.m_engaged, rhs.m_payload) { /* ... */ } constexpr OptionalBase(OptionalBase &&rhs) : m_payload(rhs.m_payload.m_engaged, std::move(rhs.m_payload)) { /* ... */ } constexpr OptionalBase &operator=(const OptionalBase &) = default; constexpr OptionalBase &operator=(OptionalBase &&) = default; }; template struct OptionalBase : OptionalBaseImpl> { OptionalPayload m_payload; constexpr OptionalBase() = default; template::value, bool> = false> constexpr explicit OptionalBase(in_place_t, Args &&... args) : m_payload(in_place, std::forward(args)...) { /* ... */ } template &, Args...>::value, bool> = false> constexpr explicit OptionalBase(in_place_t, std::initializer_list il, Args &&... args) : m_payload(in_place, il, std::forward(args)...) { /* ... */ } constexpr OptionalBase(const OptionalBase &rhs) : m_payload(rhs.m_payload.m_engaged, rhs.m_payload) { /* ... */ } constexpr OptionalBase(OptionalBase &&rhs) = default; constexpr OptionalBase &operator=(const OptionalBase &) = default; constexpr OptionalBase &operator=(OptionalBase &&) = default; }; template struct OptionalBase : OptionalBaseImpl> { OptionalPayload m_payload; constexpr OptionalBase() = default; template::value, bool> = false> constexpr explicit OptionalBase(in_place_t, Args &&... args) : m_payload(in_place, std::forward(args)...) { /* ... */ } template &, Args...>::value, bool> = false> constexpr explicit OptionalBase(in_place_t, std::initializer_list il, Args &&... args) : m_payload(in_place, il, std::forward(args)...) { /* ... */ } constexpr OptionalBase(const OptionalBase &rhs) = default; constexpr OptionalBase(OptionalBase &&rhs) : m_payload(rhs.m_payload.m_engaged, std::move(rhs.m_payload)) { /* ... */ } constexpr OptionalBase &operator=(const OptionalBase &) = default; constexpr OptionalBase &operator=(OptionalBase &&) = default; }; template struct OptionalBase : OptionalBaseImpl> { OptionalPayload m_payload; constexpr OptionalBase() = default; template::value, bool> = false> constexpr explicit OptionalBase(in_place_t, Args &&... args) : m_payload(in_place, std::forward(args)...) { /* ... */ } template &, Args...>::value, bool> = false> constexpr explicit OptionalBase(in_place_t, std::initializer_list il, Args &&... args) : m_payload(in_place, il, std::forward(args)...) { /* ... */ } constexpr OptionalBase(const OptionalBase &rhs) = default; constexpr OptionalBase(OptionalBase &&rhs) = default; constexpr OptionalBase &operator=(const OptionalBase &) = default; constexpr OptionalBase &operator=(OptionalBase &&) = default; }; } template class optional; namespace impl { template constexpr inline bool ConvertsFromOptional = std::is_constructible &>::value || std::is_constructible &>::value || std::is_constructible &&>::value || std::is_constructible &&>::value || std::is_convertible &, T>::value || std::is_convertible &, T>::value || std::is_convertible &&, T>::value || std::is_convertible &&, T>::value; template constexpr inline bool AssignsFromOptional = std::is_assignable &>::value || std::is_assignable &>::value || std::is_assignable &&>::value || std::is_assignable &&>::value; } template class optional : private impl::OptionalBase, private impl::EnableCopyMove::value, std::is_copy_constructible::value && std::is_copy_assignable::value, std::is_move_constructible::value, std::is_move_constructible::value && std::is_move_assignable::value, optional> { static_assert(!std::is_same, ::ams::util::nullopt_t>::value); static_assert(!std::is_same, ::ams::util::in_place_t>::value); static_assert(!std::is_reference::value); private: using Base = impl::OptionalBase; template static constexpr inline bool IsNotSelf = !std::is_same>::value; template static constexpr inline bool IsNotTag = !std::is_same<::ams::util::in_place_t, std::remove_cvref_t>::value && !std::is_same<::std::in_place_t, std::remove_cvref_t>::value; template using Requires = std::enable_if_t<(Cond && ...), bool>; public: using value_type = T; public: constexpr optional() { /* ... */ } constexpr optional(nullopt_t) { /* ... */ } template, IsNotTag, std::is_constructible::value, std::is_convertible::value> = true> constexpr optional(U &&u) : Base(::ams::util::in_place, std::forward(u)) { /* ... */ } template, IsNotTag, std::is_constructible::value, !std::is_convertible::value> = false> constexpr explicit optional(U &&u) : Base(::ams::util::in_place, std::forward(u)) { /* ... */ } template::value, std::is_constructible::value, std::is_convertible::value, !impl::ConvertsFromOptional> = true> constexpr optional(const optional &u) { if (u) { this->emplace(*u); } } template::value, std::is_constructible::value, !std::is_convertible::value, !impl::ConvertsFromOptional> = false> constexpr explicit optional(const optional &u) { if (u) { this->emplace(*u); } } template::value, std::is_constructible::value, std::is_convertible::value, !impl::ConvertsFromOptional> = true> constexpr optional(optional &&u) { if (u) { this->emplace(std::move(*u)); } } template::value, std::is_constructible::value, !std::is_convertible::value, !impl::ConvertsFromOptional> = false> constexpr explicit optional(optional &&u) { if (u) { this->emplace(std::move(*u)); } } template::value> = false> constexpr explicit optional(in_place_t, Args &&... args) : Base(::ams::util::in_place, std::forward(args)...) { /* ... */ } template &, Args...>::value> = false> constexpr explicit optional(in_place_t, std::initializer_list il, Args &&... args) : Base(::ams::util::in_place, il, std::forward(args)...) { /* ... */ } constexpr optional &operator=(nullopt_t) { this->ResetImpl(); return *this; } template constexpr std::enable_if_t && !(std::is_scalar::value && std::is_same>::value) && std::is_constructible::value && std::is_assignable::value, optional &> operator =(U &&u) { if (this->IsEngagedImpl()) { this->GetImpl() = std::forward(u); } else { this->ConstructImpl(std::forward(u)); } return *this; } template constexpr std::enable_if_t::value && std::is_constructible::value && std::is_assignable::value && !impl::ConvertsFromOptional && !impl::AssignsFromOptional, optional &> operator =(const optional &u) { if (u) { if (this->IsEngagedImpl()) { this->GetImpl() = *u; } else { this->ConstructImpl(*u); } } else { this->ResetImpl(); } return *this; } template constexpr std::enable_if_t::value && std::is_constructible::value && std::is_assignable::value && !impl::ConvertsFromOptional && !impl::AssignsFromOptional, optional &> operator =(optional &&u) { if (u) { if (this->IsEngagedImpl()) { this->GetImpl() = std::move(*u); } else { this->ConstructImpl(std::move(*u)); } } else { this->ResetImpl(); } return *this; } template constexpr std::enable_if_t::value, T &> emplace(Args &&... args) { this->ResetImpl(); this->ConstructImpl(std::forward(args)...); return this->GetImpl(); } template constexpr std::enable_if_t &, Args...>::value, T &> emplace(std::initializer_list il, Args &&... args) { this->ResetImpl(); this->ConstructImpl(il, std::forward(args)...); return this->GetImpl(); } constexpr void swap(optional &rhs) { if (this->IsEngagedImpl() && rhs.IsEngagedImpl()) { std::swap(this->GetImpl(), rhs.GetImpl()); } else if (this->IsEngagedImpl()) { rhs.ConstructImpl(std::move(this->GetImpl())); this->DestructImpl(); } else if (rhs.IsEngagedImpl()) { this->ConstructImpl(std::move(rhs.GetImpl())); rhs.DestructImpl(); } } constexpr ALWAYS_INLINE const T *operator ->() const { return std::addressof(this->GetImpl()); } constexpr ALWAYS_INLINE T *operator ->() { return std::addressof(this->GetImpl()); } constexpr ALWAYS_INLINE const T &operator *() const & { return this->GetImpl(); } constexpr ALWAYS_INLINE T &operator *() & { return this->GetImpl(); } constexpr ALWAYS_INLINE const T &&operator *() const && { return std::move(this->GetImpl()); } constexpr ALWAYS_INLINE T &&operator *() && { return std::move(this->GetImpl()); } constexpr ALWAYS_INLINE explicit operator bool() const { return this->IsEngagedImpl(); } constexpr ALWAYS_INLINE bool has_value() const { return this->IsEngagedImpl(); } constexpr ALWAYS_INLINE const T &value() const & { /* AMS_ASSERT(this->IsEngagedImpl()); */ return this->GetImpl(); } constexpr ALWAYS_INLINE T &value() & { /* AMS_ASSERT(this->IsEngagedImpl()); */ return this->GetImpl(); } constexpr ALWAYS_INLINE const T &&value() const && { /* AMS_ASSERT(this->IsEngagedImpl()); */ return std::move(this->GetImpl()); } constexpr ALWAYS_INLINE T &&value() && { /* AMS_ASSERT(this->IsEngagedImpl()); */ return std::move(this->GetImpl()); } template constexpr T value_or(U &&u) const & { static_assert(std::is_copy_constructible::value); static_assert(std::is_convertible::value); return this->IsEngagedImpl() ? this->GetImpl() : static_cast(std::forward(u)); } template constexpr T value_or(U &&u) && { static_assert(std::is_move_constructible::value); static_assert(std::is_convertible::value); return this->IsEngagedImpl() ? std::move(this->GetImpl()) : static_cast(std::forward(u)); } constexpr void reset() { this->ResetImpl(); } }; namespace impl { template using optional_relop_t = std::enable_if_t::value, bool>; template using optional_eq_t = optional_relop_t() == std::declval())>; template using optional_ne_t = optional_relop_t() != std::declval())>; template using optional_le_t = optional_relop_t() <= std::declval())>; template using optional_ge_t = optional_relop_t() >= std::declval())>; template using optional_lt_t = optional_relop_t() < std::declval())>; template using optional_gt_t = optional_relop_t() > std::declval())>; } template constexpr inline impl::optional_eq_t operator==(const optional &lhs, const optional &rhs) { return static_cast(lhs) == static_cast(rhs) && (!lhs || *lhs == *rhs); } template constexpr inline impl::optional_ne_t operator!=(const optional &lhs, const optional &rhs) { return static_cast(lhs) != static_cast(rhs) || (static_cast(lhs) && *lhs != *rhs); } template constexpr inline impl::optional_lt_t operator< (const optional &lhs, const optional &rhs) { return static_cast(rhs) && (!lhs || *lhs < *rhs); } template constexpr inline impl::optional_gt_t operator> (const optional &lhs, const optional &rhs) { return static_cast(lhs) && (!rhs || *lhs > *rhs); } template constexpr inline impl::optional_le_t operator<=(const optional &lhs, const optional &rhs) { return !lhs || (static_cast(rhs) && *lhs <= *rhs); } template constexpr inline impl::optional_ge_t operator>=(const optional &lhs, const optional &rhs) { return !rhs || (static_cast(lhs) && *lhs >= *rhs); } template U> constexpr inline std::compare_three_way_result_t operator <=>(const optional &lhs, const optional &rhs) { return (lhs && rhs) ? *lhs <=> *rhs : static_cast(lhs) <=> static_cast(rhs); } template constexpr inline bool operator==(const optional &lhs, nullopt_t) { return !lhs; } template constexpr inline std::strong_ordering operator<=>(const optional &lhs, nullopt_t) { return static_cast(lhs) <=> false; } template constexpr inline impl::optional_eq_t operator==(const optional &lhs, const U &rhs) { return lhs && *lhs == rhs; } template constexpr inline impl::optional_eq_t operator==(const U &lhs, const optional &rhs) { return rhs && lhs == *rhs; } template constexpr inline impl::optional_ne_t operator!=(const optional &lhs, const U &rhs) { return !lhs || *lhs != rhs; } template constexpr inline impl::optional_ne_t operator!=(const U &lhs, const optional &rhs) { return !rhs || lhs != *rhs; } template constexpr inline impl::optional_lt_t operator< (const optional &lhs, const U &rhs) { return !lhs || *lhs < rhs; } template constexpr inline impl::optional_lt_t operator< (const U &lhs, const optional &rhs) { return rhs && lhs < *rhs; } template constexpr inline impl::optional_gt_t operator> (const optional &lhs, const U &rhs) { return lhs && *lhs > rhs; } template constexpr inline impl::optional_gt_t operator> (const U &lhs, const optional &rhs) { return !rhs || lhs > *rhs; } template constexpr inline impl::optional_le_t operator<=(const optional &lhs, const U &rhs) { return !lhs || *lhs <= rhs; } template constexpr inline impl::optional_le_t operator<=(const U &lhs, const optional &rhs) { return rhs && lhs <= *rhs; } template constexpr inline impl::optional_ge_t operator>=(const optional &lhs, const U &rhs) { return lhs && *lhs >= rhs; } template constexpr inline impl::optional_ge_t operator>=(const U &lhs, const optional &rhs) { return !rhs || lhs >= *rhs; } namespace impl { template constexpr inline bool IsOptional = false; template constexpr inline bool IsOptional> = true; } template requires (!impl::IsOptional) && std::three_way_comparable_with constexpr inline std::compare_three_way_result_t operator<=>(const optional &lhs, const U &rhs) { return static_cast(lhs) ? *lhs <=> rhs : std::strong_ordering::less; } template constexpr inline std::enable_if_t, T>::value, optional>> make_optional(T && t) { return optional>{ std::forward(t) }; } template constexpr inline std::enable_if_t::value, optional> make_optional(Args &&... args) { return optional{ ::ams::util::in_place, std::forward(args)... }; } template constexpr inline std::enable_if_t &, Args...>::value, optional> make_optional(std::initializer_list il, Args &&... args) { return optional{ ::ams::util::in_place, il, std::forward(args)... }; } template optional(T) -> optional; } namespace std { template constexpr inline enable_if_t && is_swappable_v> swap(::ams::util::optional &lhs, ::ams::util::optional &rhs) noexcept { lhs.swap(rhs); } template constexpr inline enable_if_t && is_swappable_v)> swap(::ams::util::optional &lhs, ::ams::util::optional &rhs) = delete; }