summaryrefslogtreecommitdiff
path: root/src/core/error.h
blob: 3d242b929ea1e36c688f4102004b22fbc76ad834 (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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
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