summaryrefslogtreecommitdiffstats
path: root/framework/src
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src')
-rw-r--r--framework/src/errors.h246
1 files changed, 246 insertions, 0 deletions
diff --git a/framework/src/errors.h b/framework/src/errors.h
new file mode 100644
index 00000000..f6c6cfab
--- /dev/null
+++ b/framework/src/errors.h
@@ -0,0 +1,246 @@
1#pragma once
2
3#include <type_traits>
4
5// A somewhat implementation of the expected monad, proposed here:
6// https://isocpp.org/files/papers/n4015.pdf
7
8// A class used to differentiate errors and values when they are of the same type.
9template <typename Error>
10class Unexpected
11{
12
13 static_assert(!std::is_same<Error, void>::value, "Cannot have an Unexpected void");
14
15public:
16 Unexpected() = delete;
17
18 constexpr explicit Unexpected(const Error &error) : mValue(error) {}
19 constexpr explicit Unexpected(Error &&error) : mValue(std::move(error)) {}
20
21 // For implicit conversions when doing makeUnexpected(other)
22 template <typename Other>
23 constexpr explicit Unexpected(const Unexpected<Other> &error) : mValue(error.value()) {}
24 template <typename Other>
25 constexpr explicit Unexpected(Unexpected<Other> &&error) : mValue(std::move(error.value())) {}
26
27 constexpr const Error &value() const &
28 {
29 return mValue;
30 }
31 Error &value() &
32 {
33 return mValue;
34 }
35
36 constexpr const Error &&value() const &&
37 {
38 return std::move(mValue);
39 }
40 Error &&value() &&
41 {
42 return std::move(mValue);
43 }
44
45private:
46 Error mValue;
47};
48
49template <class Error>
50Unexpected<typename std::decay<Error>::type> makeUnexpected(Error &&e)
51{
52 return Unexpected<typename std::decay<Error>::type>(std::forward<Error>(e));
53}
54
55template <typename Error>
56bool operator==(const Unexpected<Error> &lhs, const Unexpected<Error> &rhs)
57{
58 return lhs.value() == rhs.value();
59}
60
61template <typename Error>
62bool operator!=(const Unexpected<Error> &lhs, const Unexpected<Error> &rhs)
63{
64 return lhs.value() != rhs.value();
65}
66
67namespace detail {
68
69namespace tags {
70struct Expected
71{};
72struct Unexpected
73{};
74} // namespace tags
75
76// Write functions here when storage related and when Type != void
77template <typename Error, typename Type>
78struct StorageBase
79{
80protected:
81 // Rule of 5 {{{
82
83 constexpr void copyPartFromOther(const StorageBase &other)
84 {
85 if (isValue) {
86 mValue = other.mValue;
87 } else {
88 mError = other.mError;
89 }
90 }
91
92 void movePartFromOther(StorageBase &&other)
93 {
94 if (isValue) {
95 mValue = std::move(other.mValue);
96 } else {
97 mError = std::move(other.mError);
98 }
99 }
100
101 StorageBase(const StorageBase &other) : isValue(other.isValue)
102 {
103 copyPartFromOther(other);
104 }
105
106 StorageBase(StorageBase &&other) : isValue(other.isValue)
107 {
108 movePartFromOther(std::move(other));
109 }
110
111 constexpr StorageBase &operator=(const StorageBase &other)
112 {
113 isValue = other.isValue;
114 copyPartFromOther(other);
115 return *this;
116 }
117
118 constexpr StorageBase &operator=(StorageBase &&other)
119 {
120 isValue = other.isValue;
121 movePartFromOther(other);
122 return *this;
123 }
124
125 ~StorageBase()
126 {
127 if (isValue) {
128 mValue.~Type();
129 } else {
130 mError.~Unexpected<Error>();
131 }
132 }
133
134 // }}}
135
136 template <typename... Args>
137 constexpr StorageBase(tags::Expected, Args &&... args)
138 : mValue(std::forward<Args>(args)...), isValue(true)
139 {
140 }
141
142 template <typename... Args>
143 constexpr StorageBase(tags::Unexpected, Args &&... args)
144 : mError(std::forward<Args>(args)...), isValue(false)
145 {
146 }
147
148 union
149 {
150 Unexpected<Error> mError;
151 Type mValue;
152 };
153 bool isValue;
154};
155
156// Write functions here when storage related and when Type == void
157template <typename Error>
158struct StorageBase<Error, void>
159{
160protected:
161 constexpr StorageBase(tags::Expected) : isValue(true) {}
162
163 template <typename... Args>
164 constexpr StorageBase(tags::Unexpected, Args &&... args)
165 : mError(std::forward<Args>(args)...), isValue(false)
166 {
167 }
168
169 Unexpected<Error> mError;
170 bool isValue;
171};
172
173// Write functions here when storage related, whether Type is void or not
174template <typename Error, typename Type>
175struct Storage : StorageBase<Error, Type>
176{
177protected:
178 // Forward the construction to StorageBase
179 using StorageBase<Error, Type>::StorageBase;
180};
181
182// Write functions here when dev API related and when Type != void
183template <typename Error, typename Type>
184struct ExpectedBase : detail::Storage<Error, Type>
185{
186 constexpr ExpectedBase() : detail::Storage<Error, Type>(detail::tags::Expected{}) {}
187
188 template <typename OtherError>
189 constexpr ExpectedBase(const Unexpected<OtherError> &error)
190 : detail::Storage<Error, Type>(detail::tags::Unexpected{}, error)
191 {
192 }
193 template <typename OtherError>
194 constexpr ExpectedBase(Unexpected<OtherError> &&error)
195 : detail::Storage<Error, Type>(detail::tags::Unexpected{}, std::move(error))
196 {
197 }
198
199 constexpr ExpectedBase(const Type &value)
200 : detail::Storage<Error, Type>(detail::tags::Expected{}, value)
201 {
202 }
203 constexpr ExpectedBase(Type &&value)
204 : detail::Storage<Error, Type>(detail::tags::Expected{}, std::move(value))
205 {
206 }
207
208 constexpr const Type &value() const &
209 {
210 return this->mValue;
211 }
212};
213
214// Write functions here when dev API related and when Type == void
215template <typename Error>
216struct ExpectedBase<Error, void> : detail::Storage<Error, void>
217{
218 // Rewrite constructors for unexpected because Expected doesn't have direct access to it.
219 template <typename OtherError>
220 constexpr ExpectedBase(const Unexpected<OtherError> &error)
221 : detail::Storage<Error, void>(detail::tags::Unexpected{}, error)
222 {
223 }
224 template <typename OtherError>
225 constexpr ExpectedBase(Unexpected<OtherError> &&error)
226 : detail::Storage<Error, void>(detail::tags::Unexpected{}, std::move(error))
227 {
228 }
229};
230
231} // namespace detail
232
233// Write functions here when dev API related, whether Type is void or not
234template <typename Error, typename Type = void>
235class Expected : public detail::ExpectedBase<Error, Type>
236{
237 static_assert(!std::is_same<Error, void>::value, "Expected with void Error is not implemented");
238
239public:
240 using detail::ExpectedBase<Error, Type>::ExpectedBase;
241
242 constexpr const Error &error() const &
243 {
244 return this->mError.value();
245 }
246};