#pragma once #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 code); Error(std::string &&msg, Error::Code code); Error(Error &&error); SAW_FORBID_COPY(Error); Error &operator=(Error &&) = default; const std::string_view message() const; bool failed() const; bool isCritical() const; bool isRecoverable() const; Error copyError() const; Code code() const; }; Error makeError(const std::string_view &generic, Error::Code c); template Error makeError(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 criticalError(const std::string_view &generic, Error::Code c = Error::Code::GenericCritical); template Error criticalError(const Formatter &formatter, const std::string_view &generic, Error::Code c = Error::Code::GenericCritical) { return makeError(formatter, c, generic); } Error recoverableError(const std::string_view &generic, Error::Code c = Error::Code::GenericRecoverable); template Error recoverableError(const Formatter &formatter, const std::string_view &generic, Error::Code c = Error::Code::GenericRecoverable) { return makeError(formatter, c, generic); } Error noError(); /** * Exception alternative. Since I code without exceptions this class is * essentially a kind of exception replacement. */ template class ErrorOr; class ErrorOrValue { public: virtual ~ErrorOrValue() = default; template ErrorOr> &as() { return static_cast> &>(*this); } template const ErrorOr> &as() const { return static_cast> &>(*this); } }; template class ErrorOr final : public ErrorOrValue { private: std::variant> value_or_error; static_assert(!std::is_same_v, "Don't use internal private types"); public: ErrorOr() = default; ErrorOr(const FixVoid &value) : value_or_error{value} {} ErrorOr(FixVoid &&value) : value_or_error{std::move(value)} {} ErrorOr(const Error &error) : value_or_error{error} {} ErrorOr(Error &&error) : value_or_error{std::move(error)} {} bool isValue() const { return std::holds_alternative>(value_or_error); } bool isError() const { return std::holds_alternative(value_or_error); } Error &error() { return std::get(value_or_error); } const Error &error() const { return std::get(value_or_error); } FixVoid &value() { return std::get>(value_or_error); } const FixVoid &value() const { return std::get>(value_or_error); } }; template class ErrorOr> { private: ErrorOr() = delete; }; } // namespace saw