From 895b6d0470ff437c658d127ffbd0f1ed7f0ba21b Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 17 Apr 2022 12:17:23 -0700 Subject: [PATCH] optional: add c++23 monadic interface --- .../include/vapours/util/util_optional.hpp | 110 ++++++++++++++++-- 1 file changed, 100 insertions(+), 10 deletions(-) diff --git a/libraries/libvapours/include/vapours/util/util_optional.hpp b/libraries/libvapours/include/vapours/util/util_optional.hpp index 873c1a1b3..01f8ad3a4 100644 --- a/libraries/libvapours/include/vapours/util/util_optional.hpp +++ b/libraries/libvapours/include/vapours/util/util_optional.hpp @@ -32,6 +32,11 @@ namespace ams::util { } }; + template + struct OptionalFunction { + F &m_f; + }; + } struct nullopt_t { @@ -67,6 +72,9 @@ namespace ams::util { template constexpr ALWAYS_INLINE StorageType(std::initializer_list il, Args &&... args) : m_value(il, std::forward(args)...) { /* ... */ } + + template + constexpr ALWAYS_INLINE StorageType(OptionalFunction f, Arg &&arg) : m_value(std::invoke(std::forward(f.m_f), std::forward(arg))) { /* ... */ } }; template @@ -82,6 +90,9 @@ namespace ams::util { template constexpr ALWAYS_INLINE StorageType(std::initializer_list il, Args &&... args) : m_value(il, std::forward(args)...) { /* ... */ } + template + constexpr ALWAYS_INLINE StorageType(OptionalFunction f, Arg &&arg) : m_value(std::invoke(std::forward(f.m_f), std::forward(arg))) { /* ... */ } + constexpr ALWAYS_INLINE ~StorageType() { /* ... */ } }; @@ -137,6 +148,12 @@ namespace ams::util { std::destroy_at(std::addressof(m_payload.m_value)); } + template + constexpr void Apply(impl::OptionalFunction f, Arg &&arg) { + std::construct_at(std::addressof(m_payload), f, std::forward(arg)); + m_engaged = true; + } + constexpr ALWAYS_INLINE T &Get() { return m_payload.m_value; } constexpr ALWAYS_INLINE const T &Get() const { return m_payload.m_value; } @@ -234,6 +251,11 @@ namespace ams::util { constexpr void ResetImpl() { static_cast(this)->m_payload.Reset(); } + template + constexpr void ApplyImpl(OptionalFunction f, Arg &&arg) { + static_cast(this)->m_payload.Apply(f, std::forward(arg)); + } + 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(); } @@ -338,6 +360,11 @@ namespace ams::util { std::is_assignable &>::value || std::is_assignable &&>::value || std::is_assignable &&>::value; + template + constexpr inline bool IsOptional = false; + + template + constexpr inline bool IsOptional> = true; } @@ -509,7 +536,80 @@ namespace ams::util { return this->IsEngagedImpl() ? std::move(this->GetImpl()) : static_cast(std::forward(u)); } + template + constexpr auto and_then(F &&f) & { + using U = typename std::remove_cvref::type>::type; + static_assert(impl::IsOptional::type>); + return this->IsEngagedImpl() ? std::invoke(std::forward(f), **this) : U{}; + } + + template + constexpr auto and_then(F &&f) const & { + using U = typename std::remove_cvref::type>::type; + static_assert(impl::IsOptional::type>); + return this->IsEngagedImpl() ? std::invoke(std::forward(f), **this) : U{}; + } + + template + constexpr auto and_then(F &&f) && { + using U = typename std::remove_cvref::type>::type; + static_assert(impl::IsOptional::type>); + return this->IsEngagedImpl() ? std::invoke(std::forward(f), std::move(**this)) : U{}; + } + + template + constexpr auto and_then(F &&f) const && { + using U = typename std::remove_cvref::type>::type; + static_assert(impl::IsOptional::type>); + return this->IsEngagedImpl() ? std::invoke(std::forward(f), std::move(**this)) : U{}; + } + + template + constexpr auto transform(F &&f) & { + using U = typename std::remove_cvref::type>::type; + return this->IsEngagedImpl() ? optional(impl::OptionalFunction{f}, **this) : optional{}; + } + + template + constexpr auto transform(F &&f) const & { + using U = typename std::remove_cvref::type>::type; + return this->IsEngagedImpl() ? optional(impl::OptionalFunction{f}, **this) : optional{}; + } + + template + constexpr auto transform(F &&f) && { + using U = typename std::remove_cvref::type>::type; + return this->IsEngagedImpl() ? optional(impl::OptionalFunction{f}, std::move(**this)) : optional{}; + } + + template + constexpr auto transform(F &&f) const && { + using U = typename std::remove_cvref::type>::type; + return this->IsEngagedImpl() ? optional(impl::OptionalFunction{f}, std::move(**this)) : optional{}; + } + + template requires std::invocable && std::copy_constructible + constexpr optional or_else(F &&f) const & { + using U = typename std::invoke_result::type; + static_assert(std::same_as, optional>); + return this->IsEngagedImpl() ? *this : std::forward(f)(); + } + + template requires std::invocable && std::move_constructible + constexpr optional or_else(F &&f) && { + using U = typename std::invoke_result::type; + static_assert(std::same_as, optional>); + return this->IsEngagedImpl() ? std::move(*this) : std::forward(f)(); + } + constexpr void reset() { this->ResetImpl(); } + private: + template friend class optional; + + template + constexpr explicit optional(impl::OptionalFunction f, Arg &&arg) { + this->ApplyImpl(f, std::forward(arg)); + } }; namespace impl { @@ -588,16 +688,6 @@ namespace ams::util { 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;