#pragma once #include "asl/integers.hpp" #include "asl/string_view.hpp" #include "asl/format.hpp" namespace asl { class Formatter; enum class status_code : uint8_t { ok = 0, unknown = 1, internal = 2, runtime = 3, invalid_argument = 4, }; class status { void* m_payload{}; static constexpr void* status_to_payload(status_code code) { return code == status_code::ok ? nullptr : bit_cast((static_cast(code) << 1) | 1); } static constexpr status_code payload_to_status(void* payload) { return static_cast(bit_cast(payload) >> 1); } constexpr bool is_inline() const { return m_payload == nullptr || (bit_cast(m_payload) & 1) != 0; } constexpr status_code code_inline() const { ASL_ASSERT(is_inline()); if (m_payload == nullptr) { return status_code::ok; } return payload_to_status(m_payload); } status_code code_internal() const; string_view message_internal() const; void ref(); void unref(); public: constexpr ~status() { if (!is_inline()) { unref(); } } explicit constexpr status(status_code code) : m_payload{status_to_payload(code)} {} status(status_code code, string_view msg); status(status_code code, string_view fmt, span args); constexpr status(const status& other) : m_payload{other.m_payload} { if (!is_inline()) { ref(); } } constexpr status(status&& other) : m_payload{exchange(other.m_payload, status_to_payload(other.code()))} {} constexpr status& operator=(const status& other) { if (&other != this) { if (!is_inline()) { unref(); } m_payload = other.m_payload; if (!is_inline()) { ref(); } } return *this; } constexpr status& operator=(status&& other) { if (&other != this) { if (!is_inline()) { unref(); } m_payload = exchange(other.m_payload, status_to_payload(other.code())); } return *this; } constexpr bool ok() const { return m_payload == nullptr; } constexpr status_code code() const { return is_inline() ? code_inline() : code_internal(); } constexpr string_view message() const { if (!is_inline()) { return message_internal(); } return {}; } constexpr status&& throw_status() && { return ASL_MOVE(*this); } friend void AslFormat(Formatter& f, const status&); template friend H AslHashValue(H h, const status& s) { if (s.is_inline()) { return H::combine(ASL_MOVE(h), s.code()); } return H::combine(ASL_MOVE(h), s.code(), s.message()); } }; static constexpr status ok() { return status{status_code::ok}; } #define ASL_DEFINE_ERROR_(type) \ static constexpr status type##_error() { return status{status_code::type}; } \ static inline status type##_error(string_view sv) { return status{status_code::type, sv}; } \ template \ [[maybe_unused]] static status type##_error(string_view fmt, const Args&... args) \ { \ format_internals::type_erased_arg type_erased_args[] = { \ format_internals::type_erased_arg(args)... \ }; \ return status{status_code::type, fmt, type_erased_args}; \ } ASL_DEFINE_ERROR_(unknown) ASL_DEFINE_ERROR_(internal) ASL_DEFINE_ERROR_(runtime) ASL_DEFINE_ERROR_(invalid_argument) #define ASL_TRY(VALUE) if (VALUE.ok()) {} else { return ASL_MOVE(VALUE).throw_status(); } } // namespace asl