#pragma once #include "deimos/core/base.h" #include "deimos/core/format.h" namespace deimos { // NOLINTNEXTLINE(performance-enum-size) enum class StatusCode : uint32_t { kOk = 0, kUnknown, kInvalidArgument, kUnimplemented, kInternal, }; StringView StatusCodeToString(StatusCode code); class Status { uintptr_t m_rep; constexpr bool IsInline() const { return (m_rep & 1U) == 1U; } static constexpr uintptr_t CodeToRep(StatusCode code) { return ((uint32_t)code << 1U) | 1U; } void Ref() const; void Unref() const; StatusCode RepCode() const; public: constexpr Status() : Status(StatusCode::kOk) {} constexpr explicit Status(StatusCode code) : m_rep{CodeToRep(code)} {} Status(StatusCode code, StringView message); Status(const Status& other) : m_rep{other.m_rep} { Ref(); } Status(Status&& other) : m_rep{std::exchange(other.m_rep, CodeToRep(StatusCode::kOk))} {} Status& operator=(const Status& other) { if (this != &other) { Unref(); this->m_rep = other.m_rep; Ref(); } return *this; } Status& operator=(Status&& other) { if (this != &other) { Unref(); this->m_rep = std::exchange(other.m_rep, CodeToRep(StatusCode::kOk)); } return *this; } ~Status() { Unref(); } constexpr bool ok() const { return m_rep == CodeToRep(StatusCode::kOk); } StatusCode code() const { if (IsInline()) { return (StatusCode)(m_rep >> 1U); } return RepCode(); } friend void DeimosFormat(IWriter*, const Status&); }; inline Status UnknownError(StringView message = {}) { return Status(StatusCode::kUnknown, message); } inline Status InvalidArgumentError(StringView message = {}) { return Status(StatusCode::kInvalidArgument, message); } inline Status UnimplementedError(StringView message = {}) { return Status(StatusCode::kUnimplemented, message); } inline Status InternalError(StringView message = {}) { return Status(StatusCode::kInternal, message); } namespace statusor_internals { }; template class StatusOr { deimos_StaticAssert(!std::is_constructible_v); struct Dummy{}; Status m_status; union { Dummy m_dummy; T m_value; }; void Clear() { if constexpr (!std::is_trivially_destructible_v) { if (m_status.ok()) { (&m_value)->~T(); } } } public: explicit StatusOr() requires std::is_default_constructible_v : m_value{} {} template StatusOr(Args&&... args) requires std::is_constructible_v : // NOLINT m_value{std::forward(args)...} {} StatusOr(const Status& status) : m_status{status} // NOLINT { Expects(!m_status.ok()); if (m_status.ok()) { m_status = InternalError("StatusOr constructed from OK"); } } StatusOr(Status&& status) : m_status{std::move(status)} // NOLINT { Expects(!m_status.ok()); if (m_status.ok()) { m_status = InternalError("StatusOr constructed from OK"); } } StatusOr(const StatusOr& other) requires std::is_copy_constructible_v : m_status{other.m_status} { if (m_status.ok()) { new (&m_value) T(other.m_value); } } StatusOr(StatusOr&& other) requires std::is_move_constructible_v : m_status{std::move(other.m_status)} { if (m_status.ok()) { new (&m_value) T(std::move(other.m_value)); } } ~StatusOr() { Clear(); } StatusOr& operator=(const StatusOr& other) requires std::is_copy_assignable_v { if (this != &other) { if (m_status.ok() && other.m_status.ok()) { m_value = other.m_value; } else if (!m_status.ok() && other.m_status.ok()) { new (&m_value) T(other.m_value); } else if (m_status.ok() && !other.m_status.ok()) { Clear(); } m_status = other.m_status; } return *this; } StatusOr& operator=(StatusOr&& other) requires std::is_move_assignable_v { if (this != &other) { if (m_status.ok() && other.m_status.ok()) { m_value = std::move(other.m_value); } else if (!m_status.ok() && other.m_status.ok()) { new (&m_value) T(std::move(other.m_value)); } else if (m_status.ok() && !other.m_status.ok()) { Clear(); } m_status = other.m_status; } return *this; } constexpr bool ok() const { return m_status.ok(); } constexpr StatusCode code() const { return m_status.code(); } constexpr const Status& status() const { return m_status; } constexpr const T& value() const & { Expects(m_status.ok()); return m_value; } constexpr T& value() & { Expects(m_status.ok()); return m_value; } constexpr T&& value() && { Expects(m_status.ok()); return std::move(m_value); } friend void DeimosFormat(IWriter* writer, const StatusOr& status) { DeimosFormat(writer, status.m_status); } }; } // namespace deimos