#pragma once #include #include #include #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: using code = uint32_t; private: code error_code_; bool is_critical_; std::variant error_message_; public: error(error::code id, bool is_critical); error(error::code id, bool is_critical, const std::string_view &msg); error(error &&error); SAW_FORBID_COPY(error); error &operator=(error &&) = default; const std::string_view get_message() const; const std::string_view get_category() const; bool failed() const; bool is_critical() const; bool is_recoverable() const; /** * Replaces the copy constructor. We need this since we want to explicitly copy and not implicitly */ error copy_error() const; code get_id() const; template bool is_type() const; }; template class error_or; namespace impl { class error_registry { private: struct error_info { error_info() = delete; error_info(const std::string_view& d_, bool critical_):description{d_}, is_critical{critical_}{} std::string_view description; bool is_critical; }; std::vector infos_; public: error_registry(); SAW_FORBID_COPY(error_registry); SAW_FORBID_MOVE(error_registry); error_or search_category(const error::code& id) const; error_or search_id(const std::string_view& desc) const; error_or search_or_register_id(const std::string_view& desc, bool is_critical); }; error_registry& get_error_registry(); template error::code get_template_id(){ static error::code id = std::numeric_limits::max(); if(id == std::numeric_limits::max()){ auto& reg = get_error_registry(); auto err_or_id = reg.search_or_register_id(T::description, T::is_critical); if(err_or_id.is_error()){ return std::numeric_limits::max(); } id = err_or_id.get_value(); } return id; } } template error make_error(const std::string_view& generic){ error::code id = impl::get_template_id(); return error{id, T::is_critical, generic}; } template error make_error(){ error::code id = impl::get_template_id(); return error{id, T::is_critical}; } error make_error(error::code code, const std::string_view &generic); namespace err { struct no_error { static constexpr std::string_view description = "No error has occured"; static constexpr bool is_critical = false; }; struct recoverable { static constexpr std::string_view description = "Generic recoverable error"; static constexpr bool is_critical = false; }; struct critical { static constexpr std::string_view description = "Generic critical error"; static constexpr bool is_critical = true; }; struct buffer_exhausted { static constexpr std::string_view description = "Buffer Exhausted"; static constexpr bool is_critical = false; }; struct not_found { static constexpr std::string_view description = "Not found"; static constexpr bool is_critical = false; }; struct out_of_memory { static constexpr std::string_view description = "Out of memory"; static constexpr bool is_critical = true; }; struct invalid_state { static constexpr std::string_view description = "Invalid state"; static constexpr bool is_critical = true; }; struct not_supported { static constexpr std::string_view description = "Not supported"; static constexpr bool is_critical = false; }; struct not_implemented { static constexpr std::string_view description = "Not implemented"; static constexpr bool is_critical = true; }; struct already_exists { static constexpr std::string_view description = "Already exists"; static constexpr bool is_critical = false; }; } /** * Shorthand for no error happened */ 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():value_or_error_{fix_void{}}{} 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 &get_error() { return std::get(value_or_error_); } const class error &get_error() const { return std::get(value_or_error_); } fix_void &get_value() { return std::get>(value_or_error_); } const fix_void &get_value() const { return std::get>(value_or_error_); } }; template class error_or> { private: error_or() = delete; }; template bool error::is_type() const { return error_code_ == impl::get_template_id(); } } // namespace saw