summaryrefslogtreecommitdiff
path: root/src/core/error.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/error.h')
-rw-r--r--src/core/error.h233
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