diff options
Diffstat (limited to 'src/core/error.h')
-rw-r--r-- | src/core/error.h | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/src/core/error.h b/src/core/error.h new file mode 100644 index 0000000..3d242b9 --- /dev/null +++ b/src/core/error.h @@ -0,0 +1,233 @@ +#pragma once + +#include <algorithm> +#include <limits> +#include <string> +#include <string_view> +#include <variant> +#include <vector> + +#include <cassert> + +#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<std::string_view, std::string> 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 message() 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<typename T> + bool is_error() const; +}; + +template<typename T> +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<error_info> infos; +public: + error_or<error::code> search_id(const std::string_view& desc) const; + + error_or<error::code> search_or_register_id(const std::string_view& desc, bool is_critical); +}; + +error_registry& get_error_registry(); + +template<typename T> +error::code get_template_id(){ + static error::code id = std::numeric_limits<error::code>::max(); + + if(id == std::numeric_limits<error::code>::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<error::code>::max(); + } + + id = err_or_id.get_value(); + } + + return id; +} +} + +template<typename T> error make_error(const std::string_view& generic){ + error::code id = impl::get_template_id<T>(); + + return error{id, T::is_critical, generic}; +} + +template<typename T> error make_error(){ + error::code id = impl::get_template_id<T>(); + + 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 = "No error has occured"; + static constexpr bool is_critical = false; +}; + +struct critical { + static constexpr std::string_view description = "No error has occured"; + static constexpr bool is_critical = true; +}; + +struct buffer_exhausted { + static constexpr std::string_view description = "Buffer is too small"; + 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; +}; +} + +/** + * 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 <typename T> class error_or; + +class error_or_value { +public: + virtual ~error_or_value() = default; + + template <typename T> error_or<unfix_void<T>> &as() { + return static_cast<error_or<unfix_void<T>> &>(*this); + } + + template <typename T> const error_or<unfix_void<T>> &as() const { + return static_cast<const error_or<unfix_void<T>> &>(*this); + } +}; + +template <typename T> class error_or final : public error_or_value { +private: + std::variant<error, fix_void<T>> value_or_error_; + + static_assert(!std::is_same_v<T, void_t>, + "Don't use internal private types"); + +public: + error_or():value_or_error_{fix_void<T>{}}{} + error_or(const fix_void<T> &value) : value_or_error_{value} {} + + error_or(fix_void<T> &&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<fix_void<T>>(value_or_error_); + } + + bool is_error() const { + return std::holds_alternative<class error>(value_or_error_); + } + + class error &get_error() { + return std::get<class error>(value_or_error_); + } + + const class error &get_error() const { + return std::get<class error>(value_or_error_); + } + + fix_void<T> &get_value() { return std::get<fix_void<T>>(value_or_error_); } + + const fix_void<T> &get_value() const { + return std::get<fix_void<T>>(value_or_error_); + } +}; + +template <typename T> class error_or<error_or<T>> { +private: + error_or() = delete; +}; + +template<typename T> +bool error::is_error() const { + + return error_code_ == impl::get_template_id<T>(); +} + +} // namespace saw |