#pragma once #include "asl/status.hpp" #include "asl/maybe_uninit.hpp" #include "asl/hash.hpp" namespace asl { template class status_or { status m_status; maybe_uninit m_value{}; public: // @Todo Convert copy // @Todo Convert move constexpr status_or(const status_or& other) requires copy_constructible : 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 : 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 { 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 { 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 constexpr explicit (!convertible_from) status_or(U&& value) requires ( constructible_from && !same_as, status_or> && !same_as, 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(); } constexpr status&& throw_status() && { return ASL_MOVE(m_status); } // @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 constexpr T value_or(U&& other_value) const& requires copy_constructible && convertible_from { return ok() ? value() : static_cast(ASL_FWD(other_value)); } template constexpr T value_or(U&& other_value) && requires move_constructible && convertible_from { return ok() ? ASL_MOVE(value()) : static_cast(ASL_FWD(other_value)); } friend void AslFormat(Formatter& f, const status_or& status) { AslFormat(f, status.m_status); } template requires hashable 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 status_or(T) -> status_or; } // namespace asl