diff options
author | Minijackson <minijackson@riseup.net> | 2018-03-07 12:37:59 +0100 |
---|---|---|
committer | Minijackson <minijackson@riseup.net> | 2018-03-08 16:29:10 +0100 |
commit | 22e745e2cb5949c6a529af076d1471ace79dff13 (patch) | |
tree | f2dc2844f17a02e65fe58d80adfcb0d98f7f1585 /framework/src/errors.h | |
parent | f4c8da01dbd769c6203dd63389f0bfda91e0163f (diff) | |
download | kube-22e745e2cb5949c6a529af076d1471ace79dff13.tar.gz kube-22e745e2cb5949c6a529af076d1471ace79dff13.zip |
First implementation of the Expected monad
Diffstat (limited to 'framework/src/errors.h')
-rw-r--r-- | framework/src/errors.h | 246 |
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. | ||
9 | template <typename Error> | ||
10 | class Unexpected | ||
11 | { | ||
12 | |||
13 | static_assert(!std::is_same<Error, void>::value, "Cannot have an Unexpected void"); | ||
14 | |||
15 | public: | ||
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 | |||
45 | private: | ||
46 | Error mValue; | ||
47 | }; | ||
48 | |||
49 | template <class Error> | ||
50 | Unexpected<typename std::decay<Error>::type> makeUnexpected(Error &&e) | ||
51 | { | ||
52 | return Unexpected<typename std::decay<Error>::type>(std::forward<Error>(e)); | ||
53 | } | ||
54 | |||
55 | template <typename Error> | ||
56 | bool operator==(const Unexpected<Error> &lhs, const Unexpected<Error> &rhs) | ||
57 | { | ||
58 | return lhs.value() == rhs.value(); | ||
59 | } | ||
60 | |||
61 | template <typename Error> | ||
62 | bool operator!=(const Unexpected<Error> &lhs, const Unexpected<Error> &rhs) | ||
63 | { | ||
64 | return lhs.value() != rhs.value(); | ||
65 | } | ||
66 | |||
67 | namespace detail { | ||
68 | |||
69 | namespace tags { | ||
70 | struct Expected | ||
71 | {}; | ||
72 | struct Unexpected | ||
73 | {}; | ||
74 | } // namespace tags | ||
75 | |||
76 | // Write functions here when storage related and when Type != void | ||
77 | template <typename Error, typename Type> | ||
78 | struct StorageBase | ||
79 | { | ||
80 | protected: | ||
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 | ||
157 | template <typename Error> | ||
158 | struct StorageBase<Error, void> | ||
159 | { | ||
160 | protected: | ||
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 | ||
174 | template <typename Error, typename Type> | ||
175 | struct Storage : StorageBase<Error, Type> | ||
176 | { | ||
177 | protected: | ||
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 | ||
183 | template <typename Error, typename Type> | ||
184 | struct 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 | ||
215 | template <typename Error> | ||
216 | struct 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 | ||
234 | template <typename Error, typename Type = void> | ||
235 | class Expected : public detail::ExpectedBase<Error, Type> | ||
236 | { | ||
237 | static_assert(!std::is_same<Error, void>::value, "Expected with void Error is not implemented"); | ||
238 | |||
239 | public: | ||
240 | using detail::ExpectedBase<Error, Type>::ExpectedBase; | ||
241 | |||
242 | constexpr const Error &error() const & | ||
243 | { | ||
244 | return this->mError.value(); | ||
245 | } | ||
246 | }; | ||