master
h 349 lines 10.3 KB
Raw
1 // Copyright (C) Microsoft Corporation. All rights reserved.
2 #pragma once
3
4 #include <optional>
5
6 #define P9_EXPECTED_STD std::
7
8 #include "result_macros.h"
9
10 namespace util {
11
12 // Represents an error condition with the specified type.
13 template <typename E>
14 struct Unexpected
15 {
16 // Creates anew instance with the specified error value.
17 // N.B. This constructor allows initialization without specifying the template argument.
18 Unexpected(const E& value) : Value{value}
19 {
20 }
21
22 E Value;
23 };
24
25 namespace details {
26
27 template <typename T, typename... Args>
28 void ConstructInPlace(T& value, Args&&... args)
29 {
30 new (P9_EXPECTED_STD addressof(value)) T(P9_EXPECTED_STD forward<Args>(args)...);
31 }
32
33 // Storage for the BasicExpected class for types that are trivially destructible.
34 template <typename T, typename E>
35 struct TrivialExpectedStorage
36 {
37 // Creates a new instance with a default initialized value.
38 constexpr TrivialExpectedStorage() : HasValue{true}, Value{}
39 {
40 }
41
42 // Creates a new instance by invoking a constructor on the value type.
43 template <typename... Args>
44 constexpr TrivialExpectedStorage(P9_EXPECTED_STD in_place_t, Args&&... args) :
45 HasValue{true}, Value{P9_EXPECTED_STD forward<Args>(args)...}
46 {
47 }
48
49 // Creates a new instance with the specified error.
50 constexpr TrivialExpectedStorage(const Unexpected<E>& error) : HasValue{false}, Error{error.Value}
51 {
52 }
53
54 // Move-constructs a new instance from an existing instance.
55 constexpr TrivialExpectedStorage(TrivialExpectedStorage&& other) : HasValue{other.HasValue}, NoInit{}
56 {
57 if (other.HasValue)
58 {
59 ConstructInPlace(Value, P9_EXPECTED_STD move(other.Value));
60 }
61 else
62 {
63 ConstructInPlace(Error, P9_EXPECTED_STD move(other.Error));
64 }
65 }
66
67 // Move-assigns the value of an existing instance.
68 TrivialExpectedStorage& operator=(TrivialExpectedStorage&& other)
69 {
70 if (P9_EXPECTED_STD addressof(other) != this)
71 {
72 if (other.HasValue)
73 {
74 if (HasValue)
75 {
76 Value = P9_EXPECTED_STD move(other.Value);
77 }
78 else
79 {
80 ConstructInPlace(Value, P9_EXPECTED_STD move(other.Value));
81 }
82 }
83 else
84 {
85 if (HasValue)
86 {
87 ConstructInPlace(Error, P9_EXPECTED_STD move(other.Error));
88 }
89 else
90 {
91 Error = P9_EXPECTED_STD move(other.Error);
92 }
93 }
94
95 HasValue = other.HasValue;
96 }
97
98 return *this;
99 }
100
101 // Default destructor, because the value type is trivially destructible.
102 ~TrivialExpectedStorage() = default;
103
104 bool HasValue;
105
106 union
107 {
108 T Value;
109 E Error;
110 char NoInit;
111 };
112 };
113
114 // Storage for the BasicExpected class for types that are not trivially destructible.
115 template <typename T, typename E>
116 struct NonTrivialExpectedStorage
117 {
118 // Creates a new instance with a default initialized value.
119 constexpr NonTrivialExpectedStorage() : HasValue{true}, Value{}
120 {
121 }
122
123 // Creates a new instance by invoking a constructor on the value type.
124 template <typename... Args>
125 constexpr NonTrivialExpectedStorage(P9_EXPECTED_STD in_place_t, Args&&... args) :
126 HasValue{true}, Value{P9_EXPECTED_STD forward<Args>(args)...}
127 {
128 }
129
130 // Creates a new instance with the specified error.
131 constexpr NonTrivialExpectedStorage(const Unexpected<E>& error) : HasValue{false}, Error{error.Value}
132 {
133 }
134
135 // Move-constructs a new instance from an existing instance.
136 constexpr NonTrivialExpectedStorage(NonTrivialExpectedStorage&& other) : HasValue{other.HasValue}, NoInit{}
137 {
138 if (other.HasValue)
139 {
140 ConstructInPlace(Value, P9_EXPECTED_STD move(other.Value));
141 }
142 else
143 {
144 ConstructInPlace(Error, P9_EXPECTED_STD move(other.Error));
145 }
146 }
147
148 // Move-assigns the value of an existing instance.
149 NonTrivialExpectedStorage& operator=(NonTrivialExpectedStorage&& other)
150 {
151 if (P9_EXPECTED_STD addressof(other) != this)
152 {
153 if (other.HasValue)
154 {
155 if (HasValue)
156 {
157 Value = P9_EXPECTED_STD move(other.Value);
158 }
159 else
160 {
161 Error.~E();
162 ConstructInPlace(Value, P9_EXPECTED_STD move(other.Value));
163 }
164 }
165 else
166 {
167 if (HasValue)
168 {
169 Value.~T();
170 ConstructInPlace(Error, P9_EXPECTED_STD move(other.Error));
171 }
172 else
173 {
174 Error = P9_EXPECTED_STD move(other.Error);
175 }
176 }
177
178 HasValue = other.HasValue;
179 }
180
181 return *this;
182 }
183
184 // Explicitly destructs either the value or the error.
185 ~NonTrivialExpectedStorage()
186 {
187 if (HasValue)
188 {
189 Value.~T();
190 }
191 else
192 {
193 Error.~E();
194 }
195 }
196
197 bool HasValue;
198
199 union
200 {
201 T Value;
202 E Error;
203 char NoInit;
204 };
205 };
206
207 // Class that helps to select between the storage for trivially and non-trivially destructible
208 // types.
209 template <typename T, typename E>
210 struct ExpectedStorage
211 {
212 using Type = P9_EXPECTED_STD conditional_t<
213 P9_EXPECTED_STD is_trivially_destructible<T>::value && P9_EXPECTED_STD is_trivially_destructible<E>::value,
214 TrivialExpectedStorage<T, E>,
215 NonTrivialExpectedStorage<T, E>>;
216 };
217
218 } // namespace details
219
220 // This is a simple implementation of the proposed standard std::excepted type, which can be
221 // used as the return type for functions that return either a value or an error.
222 // N.B. This is named BasicExpected so that consumers can create an Expected typedef with their
223 // most common error type.
224 template <typename T, typename E>
225 class BasicExpected
226 {
227 public:
228 // Creates a new instance with a default initialized value.
229 constexpr BasicExpected() = default;
230
231 // Creates a new instance holding the specified value.
232 constexpr BasicExpected(const T& value) : m_storage{P9_EXPECTED_STD in_place, value}
233 {
234 }
235
236 // Creates a new instance moving the specified value.
237 constexpr BasicExpected(T&& value) : m_storage{P9_EXPECTED_STD in_place, P9_EXPECTED_STD move(value)}
238 {
239 }
240
241 // Creates a new instance holding the specified error.
242 constexpr BasicExpected(const Unexpected<E>& error) : m_storage{error}
243 {
244 }
245
246 // Creates a new instance moving the specified error.
247 constexpr BasicExpected(Unexpected<E>&& error) : m_storage{P9_EXPECTED_STD move(error)}
248 {
249 }
250
251 // Move-constructs a new instance from an existing instance.
252 constexpr BasicExpected(BasicExpected&& other) : m_storage{P9_EXPECTED_STD move(other.m_storage)}
253 {
254 }
255
256 // Move-assigns from an existing instance.
257 BasicExpected& operator=(BasicExpected&& other)
258 {
259 if (P9_EXPECTED_STD addressof(other) != this)
260 {
261 m_storage = P9_EXPECTED_STD move(other.m_storage);
262 }
263
264 return *this;
265 }
266
267 // Tests whether or not the instance holds a value.
268 explicit operator bool() const
269 {
270 return m_storage.HasValue;
271 }
272
273 // Gets a reference to the contained value.
274 T& Get()
275 {
276 FAIL_FAST_IF(!m_storage.HasValue);
277 return m_storage.Value;
278 }
279
280 // Gets a const-reference to the contained value.
281 const T& Get() const
282 {
283 FAIL_FAST_IF(!m_storage.HasValue);
284 return m_storage.Value;
285 }
286
287 // Accesses members of the contained value.
288 // N.B. Behavior is undefined if the instance does not contain a value.
289 T* operator->()
290 {
291 return P9_EXPECTED_STD addressof(m_storage.Value);
292 }
293
294 // Accesses members of the contained value.
295 // N.B. Behavior is undefined if the instance does not contain a value.
296 const T* operator->() const
297 {
298 return P9_EXPECTED_STD addressof(m_storage.Value);
299 }
300
301 // Gets a reference to the contained value.
302 // N.B. Behavior is undefined if the instance does not contain a value.
303 T& operator*()
304 {
305 return m_storage.Value;
306 }
307
308 // Gets a const-reference to the contained value.
309 // N.B. Behavior is undefined if the instance does not contain a value.
310 const T& operator*() const
311 {
312 return m_storage.Value;
313 }
314
315 // Gets the contained error value.
316 // N.B. Behavior is undefined if the instance contains a value.
317 const E& Error() const
318 {
319 return m_storage.Error;
320 }
321
322 // Gets the contained error value if the instance contains an error.
323 // N.B. This function allows the RETURN_ macros to work without accessing their argument twice.
324 // N.B. This creates a copy of the error value, but since the error value is usually expected
325 // to be a trivial type (e.g. int) that should not be a problem.
326 P9_EXPECTED_STD optional<E> OptionalError() const
327 {
328 if (m_storage.HasValue)
329 {
330 return {};
331 }
332
333 return m_storage.Error;
334 }
335
336 // Gets the contained error value, wrapped in an Unexpected struct.
337 // N.B. This makes it easy to return the error in a function that also returns a BasicExpected type.
338 // N.B. Behavior is undefined if the instance contains a value.
339 Unexpected<E> Unexpected() const
340 {
341 FAIL_FAST_IF(m_storage.HasValue);
342 return m_storage.Error;
343 }
344
345 private:
346 typename details::ExpectedStorage<T, E>::Type m_storage;
347 };
348
349 } // namespace util