#pragma once #include "asl/status.hpp" #include "asl/maybe_uninit.hpp" #include "asl/hash.hpp" namespace asl { template<is_object T> class status_or { status m_status; maybe_uninit<T> m_value{}; public: // @Todo Convert copy // @Todo Convert move constexpr status_or(const status_or& other) requires copy_constructible<T> : m_status{other.m_status} { if (other.ok()) { m_value.construct_unsafe(other.m_value.as_init_unsafe()); } } constexpr status_or(status_or&& other) requires move_constructible<T> : m_status{ASL_MOVE(other.m_status)} { if (other.ok()) { m_value.construct_unsafe(ASL_MOVE(other.m_value.as_init_unsafe())); } } constexpr status_or& operator=(const status_or& other) requires copyable<T> { if (&other != this) { if (ok()) { if (other.ok()) { m_value.assign_unsafe(other.m_value.as_init_unsafe()); } else { m_value.destroy_unsafe(); } } else if (other.ok()) { m_value.construct_unsafe(other.m_value.as_init_unsafe()); } m_status = other.m_status; } return *this; } constexpr status_or& operator=(status_or&& other) requires moveable<T> { if (&other != this) { if (ok()) { if (other.ok()) { m_value.assign_unsafe(ASL_MOVE(other.m_value.as_init_unsafe())); } else { m_value.destroy_unsafe(); } } else if (other.ok()) { m_value.construct_unsafe(ASL_MOVE(other.m_value.as_init_unsafe())); } m_status = ASL_MOVE(other.m_status); } return *this; } constexpr ~status_or() { if (m_status.ok()) { m_value.destroy_unsafe(); } } // NOLINTNEXTLINE(*-explicit-conversions) constexpr status_or(const status& status) : m_status{status} { ASL_ASSERT_RELEASE(!m_status.ok()); } // NOLINTNEXTLINE(*-explicit-conversions) constexpr status_or(status&& status) : m_status{ASL_MOVE(status)} { ASL_ASSERT_RELEASE(!m_status.ok()); } status_or& operator=(status status) = delete; template<typename U = T> constexpr explicit (!convertible_from<T, U&&>) status_or(U&& value) requires ( constructible_from<T, U&&> && !same_as<un_cvref_t<U>, status_or> && !same_as<un_cvref_t<U>, status> ) : m_status{status_code::ok} , m_value{in_place, ASL_FWD(value)} {} constexpr bool ok() const { return m_status.ok(); } constexpr status_code code() const { return m_status.code(); } constexpr string_view message() const { return m_status.message(); } // @Todo(C++23) Deducing this constexpr const T& value() const& { ASL_ASSERT_RELEASE(ok()); return m_value.as_init_unsafe(); } constexpr T& value() & { ASL_ASSERT_RELEASE(ok()); return m_value.as_init_unsafe(); } constexpr T&& value() && { ASL_ASSERT_RELEASE(ok()); return ASL_MOVE(m_value.as_init_unsafe()); } template<typename U> constexpr T value_or(U&& other_value) const& requires copy_constructible<T> && convertible_from<T, U&&> { return ok() ? value() : static_cast<T>(ASL_FWD(other_value)); } template<typename U> constexpr T value_or(U&& other_value) && requires move_constructible<T> && convertible_from<T, U&&> { return ok() ? ASL_MOVE(value()) : static_cast<T>(ASL_FWD(other_value)); } template<typename H> requires hashable<T> friend H AslHashValue(H h, const status_or& s) { if (s.ok()) { return H::combine(ASL_MOVE(h), s.m_status, s.value()); } return H::combine(ASL_MOVE(h), s.m_status); } }; template<typename T> status_or(T) -> status_or<T>; } // namespace asl