#pragma once #include #include #include #include #include "common.h" namespace saw { /** * Utility class for generating errors. Has a base distinction between * critical and recoverable errors. Additional code ids can be provided to the * constructor if additional distinctions are necessary. */ class error { public: enum class code : int16_t { GenericCritical = -1, GenericRecoverable = 1, Disconnected = -99, Exhausted = -98 }; private: std::variant error_message_; code error_; public: error(); error(const std::string_view &msg, error::code id); error(std::string &&msg, error::code id); error(error &&error); SAW_FORBID_COPY(error); error &operator=(error &&) = default; const std::string_view message() const; bool failed() const; bool is_critical() const; bool is_recoverable() const; error copy_error() const; code id() const; }; error make_error(const std::string_view &generic, error::code c); template error make_error(const Formatter &formatter, error::code code, const std::string_view &generic) { try { std::string error_msg = formatter(); return error{std::move(error_msg), code}; } catch (std::bad_alloc &) { return error{generic, code}; } } error critical_error(const std::string_view &generic, error::code c = error::code::GenericCritical); template error critical_error(const Formatter &formatter, const std::string_view &generic, error::code c = error::code::GenericCritical) { return make_error(formatter, c, generic); } error recoverable_error(const std::string_view &generic, error::code c = error::code::GenericRecoverable); template error recoverable_error(const Formatter &formatter, const std::string_view &generic, error::code c = error::code::GenericRecoverable) { return make_error(formatter, c, generic); } error no_error(); /** * Exception alternative. Since I code without exceptions this class is * essentially a kind of exception replacement. */ template class error_or; class error_or_value { public: virtual ~error_or_value() = default; template error_or> &as() { return static_cast> &>(*this); } template const error_or> &as() const { return static_cast> &>(*this); } }; template class error_or final : public error_or_value { private: std::variant> value_or_error_; static_assert(!std::is_same_v, "Don't use internal private types"); public: error_or() = default; error_or(const fix_void &value) : value_or_error_{value} {} error_or(fix_void &&value) : value_or_error_{std::move(value)} {} error_or(const error &error) : value_or_error_{error} {} error_or(error &&error) : value_or_error_{std::move(error)} {} bool is_value() const { return std::holds_alternative>(value_or_error_); } bool is_error() const { return std::holds_alternative(value_or_error_); } class error &error() { return std::get(value_or_error_); } const class error &error() const { return std::get(value_or_error_); } fix_void &value() { return std::get>(value_or_error_); } const fix_void &value() const { return std::get>(value_or_error_); } }; template class error_or> { private: error_or() = delete; }; } // namespace saw