summaryrefslogtreecommitdiff
path: root/forstio/core/error.h
blob: d8a5ae27c1814a1a9e879dca1f252e064435eabb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#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