/*
* 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