forstio/source/forstio/error.h

149 lines
3.5 KiB
C++

#pragma once
#include <string>
#include <string_view>
#include <variant>
#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:
enum class code : int16_t {
GenericCritical = -1,
GenericRecoverable = 1,
Disconnected = -99,
Exhausted = -98
};
private:
std::variant<std::string_view, std::string> 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 <typename Formatter>
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 <typename Formatter>
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 <typename Formatter>
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 <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>, "Don't use internal private types");
public:
error_or() = default;
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 &error() {
return std::get<class error>(value_or_error_);
}
const class error &error() const {
return std::get<class error>(value_or_error_);
}
fix_void<T> &value() { return std::get<fix_void<T>>(value_or_error_); }
const fix_void<T> &value() const {
return std::get<fix_void<T>>(value_or_error_);
}
};
template <typename T> class error_or<error_or<T>> {
private:
error_or() = delete;
};
} // namespace saw