forstio/source/forstio/error.h

142 lines
3.4 KiB
C
Raw Normal View History

2020-07-28 01:23:38 +02:00
#pragma once
#include <string>
2021-03-13 22:22:01 +01:00
#include <string_view>
2020-07-28 01:23:38 +02:00
#include <variant>
2020-08-25 19:51:45 +02:00
#include "common.h"
2021-12-29 19:26:22 +01:00
namespace saw {
2020-12-04 22:13:31 +01:00
/**
* 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.
*/
2020-08-09 02:03:09 +02:00
class Error {
2021-06-04 23:21:25 +02:00
public:
enum class Code : int16_t {
GenericCritical = -1,
GenericRecoverable = 1,
Disconnected = -99,
Exhausted = -98
2021-06-04 23:21:25 +02:00
};
private:
2021-03-13 22:22:01 +01:00
std::variant<std::string_view, std::string> error_message;
Code error_;
2020-08-09 02:03:09 +02:00
public:
Error();
Error(const std::string_view &msg, Error::Code code);
Error(std::string &&msg, Error::Code code);
2020-08-20 00:36:21 +02:00
Error(Error &&error);
2021-12-29 19:49:20 +01:00
SAW_FORBID_COPY(Error);
2021-03-13 22:22:01 +01:00
2020-08-20 00:36:21 +02:00
Error &operator=(Error &&) = default;
2020-08-09 02:03:09 +02:00
2021-03-13 22:22:01 +01:00
const std::string_view message() const;
bool failed() const;
2020-08-09 02:03:09 +02:00
bool isCritical() const;
bool isRecoverable() const;
2021-03-13 22:22:01 +01:00
Error copyError() const;
2021-06-04 23:21:25 +02:00
Code code() const;
2020-08-09 02:03:09 +02:00
};
Error makeError(const std::string_view &generic, Error::Code c);
2021-03-13 22:22:01 +01:00
template <typename Formatter>
Error makeError(const Formatter &formatter, Error::Code code,
2021-03-17 11:24:18 +01:00
const std::string_view &generic) {
2021-03-13 22:22:01 +01:00
try {
std::string error_msg = formatter();
return Error{std::move(error_msg), code};
} catch (std::bad_alloc &) {
return Error{generic, code};
}
}
Error criticalError(const std::string_view &generic,
Error::Code c = Error::Code::GenericCritical);
2021-03-13 22:22:01 +01:00
template <typename Formatter>
Error criticalError(const Formatter &formatter, const std::string_view &generic,
Error::Code c = Error::Code::GenericCritical) {
return makeError(formatter, c, generic);
2021-03-13 22:22:01 +01:00
}
Error recoverableError(const std::string_view &generic,
Error::Code c = Error::Code::GenericRecoverable);
2021-03-13 22:22:01 +01:00
template <typename Formatter>
Error recoverableError(const Formatter &formatter,
const std::string_view &generic,
Error::Code c = Error::Code::GenericRecoverable) {
return makeError(formatter, c, generic);
2021-03-13 22:22:01 +01:00
}
2020-08-09 02:03:09 +02:00
Error noError();
2020-12-04 22:13:31 +01:00
/**
* Exception alternative. Since I code without exceptions this class is
* essentially a kind of exception replacement.
*/
2020-08-20 00:36:21 +02:00
template <typename T> class ErrorOr;
2020-08-09 02:03:09 +02:00
class ErrorOrValue {
public:
virtual ~ErrorOrValue() = default;
2020-08-18 22:38:36 +02:00
template <typename T> ErrorOr<UnfixVoid<T>> &as() {
return static_cast<ErrorOr<UnfixVoid<T>> &>(*this);
2020-08-18 22:38:36 +02:00
}
template <typename T> const ErrorOr<UnfixVoid<T>> &as() const {
return static_cast<const ErrorOr<UnfixVoid<T>> &>(*this);
2020-08-18 22:38:36 +02:00
}
2020-08-09 02:03:09 +02:00
};
template <typename T> class ErrorOr final : public ErrorOrValue {
private:
2021-06-20 13:28:59 +02:00
std::variant<Error, FixVoid<T>> value_or_error;
2020-08-28 16:59:25 +02:00
static_assert(!std::is_same_v<T, Void>, "Don't use internal private types");
public:
2020-08-18 22:38:36 +02:00
ErrorOr() = default;
2021-05-02 21:09:50 +02:00
ErrorOr(const FixVoid<T> &value) : value_or_error{value} {}
2020-08-09 02:03:09 +02:00
2021-05-02 21:09:50 +02:00
ErrorOr(FixVoid<T> &&value) : value_or_error{std::move(value)} {}
2020-08-09 02:03:09 +02:00
ErrorOr(const Error &error) : value_or_error{error} {}
ErrorOr(Error &&error) : value_or_error{std::move(error)} {}
2020-08-09 02:03:09 +02:00
bool isValue() const {
return std::holds_alternative<FixVoid<T>>(value_or_error);
}
2020-08-09 02:03:09 +02:00
bool isError() const {
return std::holds_alternative<Error>(value_or_error);
}
2020-08-09 02:03:09 +02:00
2020-08-28 16:59:25 +02:00
Error &error() { return std::get<Error>(value_or_error); }
const Error &error() const { return std::get<Error>(value_or_error); }
2020-08-09 02:03:09 +02:00
2021-05-02 21:09:50 +02:00
FixVoid<T> &value() { return std::get<FixVoid<T>>(value_or_error); }
2020-08-09 02:03:09 +02:00
2021-05-02 21:09:50 +02:00
const FixVoid<T> &value() const {
return std::get<FixVoid<T>>(value_or_error);
}
2020-08-09 02:03:09 +02:00
};
2020-08-25 19:51:45 +02:00
2021-06-20 13:28:59 +02:00
template <typename T> class ErrorOr<ErrorOr<T>> {
private:
ErrorOr() = delete;
};
2021-12-29 19:26:22 +01:00
} // namespace saw