150 lines
3.5 KiB
C++
150 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_t>,
|
|
"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
|