]>
Commit | Line | Data |
---|---|---|
1d09f67e TL |
1 | // |
2 | // Copyright 2017 Asylo authors | |
3 | // | |
4 | // Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | // you may not use this file except in compliance with the License. | |
6 | // You may obtain a copy of the License at | |
7 | // | |
8 | // http://www.apache.org/licenses/LICENSE-2.0 | |
9 | // | |
10 | // Unless required by applicable law or agreed to in writing, software | |
11 | // distributed under the License is distributed on an "AS IS" BASIS, | |
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | // See the License for the specific language governing permissions and | |
14 | // limitations under the License. | |
15 | // | |
16 | ||
17 | // Adapted from Asylo | |
18 | ||
19 | #pragma once | |
20 | ||
21 | #include <cstddef> | |
22 | #include <new> | |
23 | #include <string> | |
24 | #include <type_traits> | |
25 | #include <utility> | |
26 | ||
27 | #include "arrow/status.h" | |
28 | #include "arrow/util/aligned_storage.h" | |
29 | #include "arrow/util/compare.h" | |
30 | ||
31 | namespace arrow { | |
32 | ||
33 | template <typename> | |
34 | struct EnsureResult; | |
35 | ||
36 | namespace internal { | |
37 | ||
38 | ARROW_EXPORT void DieWithMessage(const std::string& msg); | |
39 | ||
40 | ARROW_EXPORT void InvalidValueOrDie(const Status& st); | |
41 | ||
42 | } // namespace internal | |
43 | ||
44 | /// A class for representing either a usable value, or an error. | |
45 | /// | |
46 | /// A Result object either contains a value of type `T` or a Status object | |
47 | /// explaining why such a value is not present. The type `T` must be | |
48 | /// copy-constructible and/or move-constructible. | |
49 | /// | |
50 | /// The state of a Result object may be determined by calling ok() or | |
51 | /// status(). The ok() method returns true if the object contains a valid value. | |
52 | /// The status() method returns the internal Status object. A Result object | |
53 | /// that contains a valid value will return an OK Status for a call to status(). | |
54 | /// | |
55 | /// A value of type `T` may be extracted from a Result object through a call | |
56 | /// to ValueOrDie(). This function should only be called if a call to ok() | |
57 | /// returns true. Sample usage: | |
58 | /// | |
59 | /// ``` | |
60 | /// arrow::Result<Foo> result = CalculateFoo(); | |
61 | /// if (result.ok()) { | |
62 | /// Foo foo = result.ValueOrDie(); | |
63 | /// foo.DoSomethingCool(); | |
64 | /// } else { | |
65 | /// ARROW_LOG(ERROR) << result.status(); | |
66 | /// } | |
67 | /// ``` | |
68 | /// | |
69 | /// If `T` is a move-only type, like `std::unique_ptr<>`, then the value should | |
70 | /// only be extracted after invoking `std::move()` on the Result object. | |
71 | /// Sample usage: | |
72 | /// | |
73 | /// ``` | |
74 | /// arrow::Result<std::unique_ptr<Foo>> result = CalculateFoo(); | |
75 | /// if (result.ok()) { | |
76 | /// std::unique_ptr<Foo> foo = std::move(result).ValueOrDie(); | |
77 | /// foo->DoSomethingCool(); | |
78 | /// } else { | |
79 | /// ARROW_LOG(ERROR) << result.status(); | |
80 | /// } | |
81 | /// ``` | |
82 | /// | |
83 | /// Result is provided for the convenience of implementing functions that | |
84 | /// return some value but may fail during execution. For instance, consider a | |
85 | /// function with the following signature: | |
86 | /// | |
87 | /// ``` | |
88 | /// arrow::Status CalculateFoo(int *output); | |
89 | /// ``` | |
90 | /// | |
91 | /// This function may instead be written as: | |
92 | /// | |
93 | /// ``` | |
94 | /// arrow::Result<int> CalculateFoo(); | |
95 | /// ``` | |
96 | template <class T> | |
97 | class ARROW_MUST_USE_TYPE Result : public util::EqualityComparable<Result<T>> { | |
98 | template <typename U> | |
99 | friend class Result; | |
100 | ||
101 | static_assert(!std::is_same<T, Status>::value, | |
102 | "this assert indicates you have probably made a metaprogramming error"); | |
103 | ||
104 | public: | |
105 | using ValueType = T; | |
106 | ||
107 | /// Constructs a Result object that contains a non-OK status. | |
108 | /// | |
109 | /// This constructor is marked `explicit` to prevent attempts to `return {}` | |
110 | /// from a function with a return type of, for example, | |
111 | /// `Result<std::vector<int>>`. While `return {}` seems like it would return | |
112 | /// an empty vector, it will actually invoke the default constructor of | |
113 | /// Result. | |
114 | explicit Result() noexcept // NOLINT(runtime/explicit) | |
115 | : status_(Status::UnknownError("Uninitialized Result<T>")) {} | |
116 | ||
117 | ~Result() noexcept { Destroy(); } | |
118 | ||
119 | /// Constructs a Result object with the given non-OK Status object. All | |
120 | /// calls to ValueOrDie() on this object will abort. The given `status` must | |
121 | /// not be an OK status, otherwise this constructor will abort. | |
122 | /// | |
123 | /// This constructor is not declared explicit so that a function with a return | |
124 | /// type of `Result<T>` can return a Status object, and the status will be | |
125 | /// implicitly converted to the appropriate return type as a matter of | |
126 | /// convenience. | |
127 | /// | |
128 | /// \param status The non-OK Status object to initialize to. | |
129 | Result(const Status& status) noexcept // NOLINT(runtime/explicit) | |
130 | : status_(status) { | |
131 | if (ARROW_PREDICT_FALSE(status.ok())) { | |
132 | internal::DieWithMessage(std::string("Constructed with a non-error status: ") + | |
133 | status.ToString()); | |
134 | } | |
135 | } | |
136 | ||
137 | /// Constructs a Result object that contains `value`. The resulting object | |
138 | /// is considered to have an OK status. The wrapped element can be accessed | |
139 | /// with ValueOrDie(). | |
140 | /// | |
141 | /// This constructor is made implicit so that a function with a return type of | |
142 | /// `Result<T>` can return an object of type `U &&`, implicitly converting | |
143 | /// it to a `Result<T>` object. | |
144 | /// | |
145 | /// Note that `T` must be implicitly constructible from `U`, and `U` must not | |
146 | /// be a (cv-qualified) Status or Status-reference type. Due to C++ | |
147 | /// reference-collapsing rules and perfect-forwarding semantics, this | |
148 | /// constructor matches invocations that pass `value` either as a const | |
149 | /// reference or as an rvalue reference. Since Result needs to work for both | |
150 | /// reference and rvalue-reference types, the constructor uses perfect | |
151 | /// forwarding to avoid invalidating arguments that were passed by reference. | |
152 | /// See http://thbecker.net/articles/rvalue_references/section_08.html for | |
153 | /// additional details. | |
154 | /// | |
155 | /// \param value The value to initialize to. | |
156 | template <typename U, | |
157 | typename E = typename std::enable_if< | |
158 | std::is_constructible<T, U>::value && std::is_convertible<U, T>::value && | |
159 | !std::is_same<typename std::remove_reference< | |
160 | typename std::remove_cv<U>::type>::type, | |
161 | Status>::value>::type> | |
162 | Result(U&& value) noexcept { // NOLINT(runtime/explicit) | |
163 | ConstructValue(std::forward<U>(value)); | |
164 | } | |
165 | ||
166 | /// Constructs a Result object that contains `value`. The resulting object | |
167 | /// is considered to have an OK status. The wrapped element can be accessed | |
168 | /// with ValueOrDie(). | |
169 | /// | |
170 | /// This constructor is made implicit so that a function with a return type of | |
171 | /// `Result<T>` can return an object of type `T`, implicitly converting | |
172 | /// it to a `Result<T>` object. | |
173 | /// | |
174 | /// \param value The value to initialize to. | |
175 | // NOTE `Result(U&& value)` above should be sufficient, but some compilers | |
176 | // fail matching it. | |
177 | Result(T&& value) noexcept { // NOLINT(runtime/explicit) | |
178 | ConstructValue(std::move(value)); | |
179 | } | |
180 | ||
181 | /// Copy constructor. | |
182 | /// | |
183 | /// This constructor needs to be explicitly defined because the presence of | |
184 | /// the move-assignment operator deletes the default copy constructor. In such | |
185 | /// a scenario, since the deleted copy constructor has stricter binding rules | |
186 | /// than the templated copy constructor, the templated constructor cannot act | |
187 | /// as a copy constructor, and any attempt to copy-construct a `Result` | |
188 | /// object results in a compilation error. | |
189 | /// | |
190 | /// \param other The value to copy from. | |
191 | Result(const Result& other) noexcept : status_(other.status_) { | |
192 | if (ARROW_PREDICT_TRUE(status_.ok())) { | |
193 | ConstructValue(other.ValueUnsafe()); | |
194 | } | |
195 | } | |
196 | ||
197 | /// Templatized constructor that constructs a `Result<T>` from a const | |
198 | /// reference to a `Result<U>`. | |
199 | /// | |
200 | /// `T` must be implicitly constructible from `const U &`. | |
201 | /// | |
202 | /// \param other The value to copy from. | |
203 | template <typename U, typename E = typename std::enable_if< | |
204 | std::is_constructible<T, const U&>::value && | |
205 | std::is_convertible<U, T>::value>::type> | |
206 | Result(const Result<U>& other) noexcept : status_(other.status_) { | |
207 | if (ARROW_PREDICT_TRUE(status_.ok())) { | |
208 | ConstructValue(other.ValueUnsafe()); | |
209 | } | |
210 | } | |
211 | ||
212 | /// Copy-assignment operator. | |
213 | /// | |
214 | /// \param other The Result object to copy. | |
215 | Result& operator=(const Result& other) noexcept { | |
216 | // Check for self-assignment. | |
217 | if (ARROW_PREDICT_FALSE(this == &other)) { | |
218 | return *this; | |
219 | } | |
220 | Destroy(); | |
221 | status_ = other.status_; | |
222 | if (ARROW_PREDICT_TRUE(status_.ok())) { | |
223 | ConstructValue(other.ValueUnsafe()); | |
224 | } | |
225 | return *this; | |
226 | } | |
227 | ||
228 | /// Templatized constructor which constructs a `Result<T>` by moving the | |
229 | /// contents of a `Result<U>`. `T` must be implicitly constructible from `U | |
230 | /// &&`. | |
231 | /// | |
232 | /// Sets `other` to contain a non-OK status with a`StatusError::Invalid` | |
233 | /// error code. | |
234 | /// | |
235 | /// \param other The Result object to move from and set to a non-OK status. | |
236 | template <typename U, | |
237 | typename E = typename std::enable_if<std::is_constructible<T, U&&>::value && | |
238 | std::is_convertible<U, T>::value>::type> | |
239 | Result(Result<U>&& other) noexcept { | |
240 | if (ARROW_PREDICT_TRUE(other.status_.ok())) { | |
241 | status_ = std::move(other.status_); | |
242 | ConstructValue(other.MoveValueUnsafe()); | |
243 | } else { | |
244 | // If we moved the status, the other status may become ok but the other | |
245 | // value hasn't been constructed => crash on other destructor. | |
246 | status_ = other.status_; | |
247 | } | |
248 | } | |
249 | ||
250 | /// Move-assignment operator. | |
251 | /// | |
252 | /// Sets `other` to an invalid state.. | |
253 | /// | |
254 | /// \param other The Result object to assign from and set to a non-OK | |
255 | /// status. | |
256 | Result& operator=(Result&& other) noexcept { | |
257 | // Check for self-assignment. | |
258 | if (ARROW_PREDICT_FALSE(this == &other)) { | |
259 | return *this; | |
260 | } | |
261 | Destroy(); | |
262 | if (ARROW_PREDICT_TRUE(other.status_.ok())) { | |
263 | status_ = std::move(other.status_); | |
264 | ConstructValue(other.MoveValueUnsafe()); | |
265 | } else { | |
266 | // If we moved the status, the other status may become ok but the other | |
267 | // value hasn't been constructed => crash on other destructor. | |
268 | status_ = other.status_; | |
269 | } | |
270 | return *this; | |
271 | } | |
272 | ||
273 | /// Compare to another Result. | |
274 | bool Equals(const Result& other) const { | |
275 | if (ARROW_PREDICT_TRUE(status_.ok())) { | |
276 | return other.status_.ok() && ValueUnsafe() == other.ValueUnsafe(); | |
277 | } | |
278 | return status_ == other.status_; | |
279 | } | |
280 | ||
281 | /// Indicates whether the object contains a `T` value. Generally instead | |
282 | /// of accessing this directly you will want to use ASSIGN_OR_RAISE defined | |
283 | /// below. | |
284 | /// | |
285 | /// \return True if this Result object's status is OK (i.e. a call to ok() | |
286 | /// returns true). If this function returns true, then it is safe to access | |
287 | /// the wrapped element through a call to ValueOrDie(). | |
288 | constexpr bool ok() const { return status_.ok(); } | |
289 | ||
290 | /// \brief Equivalent to ok(). | |
291 | // operator bool() const { return ok(); } | |
292 | ||
293 | /// Gets the stored status object, or an OK status if a `T` value is stored. | |
294 | /// | |
295 | /// \return The stored non-OK status object, or an OK status if this object | |
296 | /// has a value. | |
297 | constexpr const Status& status() const { return status_; } | |
298 | ||
299 | /// Gets the stored `T` value. | |
300 | /// | |
301 | /// This method should only be called if this Result object's status is OK | |
302 | /// (i.e. a call to ok() returns true), otherwise this call will abort. | |
303 | /// | |
304 | /// \return The stored `T` value. | |
305 | const T& ValueOrDie() const& { | |
306 | if (ARROW_PREDICT_FALSE(!ok())) { | |
307 | internal::InvalidValueOrDie(status_); | |
308 | } | |
309 | return ValueUnsafe(); | |
310 | } | |
311 | const T& operator*() const& { return ValueOrDie(); } | |
312 | const T* operator->() const { return &ValueOrDie(); } | |
313 | ||
314 | /// Gets a mutable reference to the stored `T` value. | |
315 | /// | |
316 | /// This method should only be called if this Result object's status is OK | |
317 | /// (i.e. a call to ok() returns true), otherwise this call will abort. | |
318 | /// | |
319 | /// \return The stored `T` value. | |
320 | T& ValueOrDie() & { | |
321 | if (ARROW_PREDICT_FALSE(!ok())) { | |
322 | internal::InvalidValueOrDie(status_); | |
323 | } | |
324 | return ValueUnsafe(); | |
325 | } | |
326 | T& operator*() & { return ValueOrDie(); } | |
327 | T* operator->() { return &ValueOrDie(); } | |
328 | ||
329 | /// Moves and returns the internally-stored `T` value. | |
330 | /// | |
331 | /// This method should only be called if this Result object's status is OK | |
332 | /// (i.e. a call to ok() returns true), otherwise this call will abort. The | |
333 | /// Result object is invalidated after this call and will be updated to | |
334 | /// contain a non-OK status. | |
335 | /// | |
336 | /// \return The stored `T` value. | |
337 | T ValueOrDie() && { | |
338 | if (ARROW_PREDICT_FALSE(!ok())) { | |
339 | internal::InvalidValueOrDie(status_); | |
340 | } | |
341 | return MoveValueUnsafe(); | |
342 | } | |
343 | T operator*() && { return std::move(*this).ValueOrDie(); } | |
344 | ||
345 | /// Helper method for implementing Status returning functions in terms of semantically | |
346 | /// equivalent Result returning functions. For example: | |
347 | /// | |
348 | /// Status GetInt(int *out) { return GetInt().Value(out); } | |
349 | template <typename U, typename E = typename std::enable_if< | |
350 | std::is_constructible<U, T>::value>::type> | |
351 | Status Value(U* out) && { | |
352 | if (!ok()) { | |
353 | return status(); | |
354 | } | |
355 | *out = U(MoveValueUnsafe()); | |
356 | return Status::OK(); | |
357 | } | |
358 | ||
359 | /// Move and return the internally stored value or alternative if an error is stored. | |
360 | T ValueOr(T alternative) && { | |
361 | if (!ok()) { | |
362 | return alternative; | |
363 | } | |
364 | return MoveValueUnsafe(); | |
365 | } | |
366 | ||
367 | /// Retrieve the value if ok(), falling back to an alternative generated by the provided | |
368 | /// factory | |
369 | template <typename G> | |
370 | T ValueOrElse(G&& generate_alternative) && { | |
371 | if (ok()) { | |
372 | return MoveValueUnsafe(); | |
373 | } | |
374 | return generate_alternative(); | |
375 | } | |
376 | ||
377 | /// Apply a function to the internally stored value to produce a new result or propagate | |
378 | /// the stored error. | |
379 | template <typename M> | |
380 | typename EnsureResult<decltype(std::declval<M&&>()(std::declval<T&&>()))>::type Map( | |
381 | M&& m) && { | |
382 | if (!ok()) { | |
383 | return status(); | |
384 | } | |
385 | return std::forward<M>(m)(MoveValueUnsafe()); | |
386 | } | |
387 | ||
388 | /// Apply a function to the internally stored value to produce a new result or propagate | |
389 | /// the stored error. | |
390 | template <typename M> | |
391 | typename EnsureResult<decltype(std::declval<M&&>()(std::declval<const T&>()))>::type | |
392 | Map(M&& m) const& { | |
393 | if (!ok()) { | |
394 | return status(); | |
395 | } | |
396 | return std::forward<M>(m)(ValueUnsafe()); | |
397 | } | |
398 | ||
399 | /// Cast the internally stored value to produce a new result or propagate the stored | |
400 | /// error. | |
401 | template <typename U, typename E = typename std::enable_if< | |
402 | std::is_constructible<U, T>::value>::type> | |
403 | Result<U> As() && { | |
404 | if (!ok()) { | |
405 | return status(); | |
406 | } | |
407 | return U(MoveValueUnsafe()); | |
408 | } | |
409 | ||
410 | /// Cast the internally stored value to produce a new result or propagate the stored | |
411 | /// error. | |
412 | template <typename U, typename E = typename std::enable_if< | |
413 | std::is_constructible<U, const T&>::value>::type> | |
414 | Result<U> As() const& { | |
415 | if (!ok()) { | |
416 | return status(); | |
417 | } | |
418 | return U(ValueUnsafe()); | |
419 | } | |
420 | ||
421 | constexpr const T& ValueUnsafe() const& { return *storage_.get(); } | |
422 | ||
423 | #if __cpp_constexpr >= 201304L // non-const constexpr | |
424 | constexpr T& ValueUnsafe() & { return *storage_.get(); } | |
425 | #else | |
426 | T& ValueUnsafe() & { return *storage_.get(); } | |
427 | #endif | |
428 | ||
429 | T ValueUnsafe() && { return MoveValueUnsafe(); } | |
430 | ||
431 | T MoveValueUnsafe() { return std::move(*storage_.get()); } | |
432 | ||
433 | private: | |
434 | Status status_; // pointer-sized | |
435 | internal::AlignedStorage<T> storage_; | |
436 | ||
437 | template <typename U> | |
438 | void ConstructValue(U&& u) noexcept { | |
439 | storage_.construct(std::forward<U>(u)); | |
440 | } | |
441 | ||
442 | void Destroy() noexcept { | |
443 | if (ARROW_PREDICT_TRUE(status_.ok())) { | |
444 | static_assert(offsetof(Result<T>, status_) == 0, | |
445 | "Status is guaranteed to be at the start of Result<>"); | |
446 | storage_.destroy(); | |
447 | } | |
448 | } | |
449 | }; | |
450 | ||
451 | #define ARROW_ASSIGN_OR_RAISE_IMPL(result_name, lhs, rexpr) \ | |
452 | auto&& result_name = (rexpr); \ | |
453 | ARROW_RETURN_IF_(!(result_name).ok(), (result_name).status(), ARROW_STRINGIFY(rexpr)); \ | |
454 | lhs = std::move(result_name).ValueUnsafe(); | |
455 | ||
456 | #define ARROW_ASSIGN_OR_RAISE_NAME(x, y) ARROW_CONCAT(x, y) | |
457 | ||
458 | /// \brief Execute an expression that returns a Result, extracting its value | |
459 | /// into the variable defined by `lhs` (or returning a Status on error). | |
460 | /// | |
461 | /// Example: Assigning to a new value: | |
462 | /// ARROW_ASSIGN_OR_RAISE(auto value, MaybeGetValue(arg)); | |
463 | /// | |
464 | /// Example: Assigning to an existing value: | |
465 | /// ValueType value; | |
466 | /// ARROW_ASSIGN_OR_RAISE(value, MaybeGetValue(arg)); | |
467 | /// | |
468 | /// WARNING: ARROW_ASSIGN_OR_RAISE expands into multiple statements; | |
469 | /// it cannot be used in a single statement (e.g. as the body of an if | |
470 | /// statement without {})! | |
471 | /// | |
472 | /// WARNING: ARROW_ASSIGN_OR_RAISE `std::move`s its right operand. If you have | |
473 | /// an lvalue Result which you *don't* want to move out of cast appropriately. | |
474 | /// | |
475 | /// WARNING: ARROW_ASSIGN_OR_RAISE is not a single expression; it will not | |
476 | /// maintain lifetimes of all temporaries in `rexpr` (e.g. | |
477 | /// `ARROW_ASSIGN_OR_RAISE(auto x, MakeTemp().GetResultRef());` | |
478 | /// will most likely segfault)! | |
479 | #define ARROW_ASSIGN_OR_RAISE(lhs, rexpr) \ | |
480 | ARROW_ASSIGN_OR_RAISE_IMPL(ARROW_ASSIGN_OR_RAISE_NAME(_error_or_value, __COUNTER__), \ | |
481 | lhs, rexpr); | |
482 | ||
483 | namespace internal { | |
484 | ||
485 | template <typename T> | |
486 | inline const Status& GenericToStatus(const Result<T>& res) { | |
487 | return res.status(); | |
488 | } | |
489 | ||
490 | template <typename T> | |
491 | inline Status GenericToStatus(Result<T>&& res) { | |
492 | return std::move(res).status(); | |
493 | } | |
494 | ||
495 | } // namespace internal | |
496 | ||
497 | template <typename T, typename R = typename EnsureResult<T>::type> | |
498 | R ToResult(T t) { | |
499 | return R(std::move(t)); | |
500 | } | |
501 | ||
502 | template <typename T> | |
503 | struct EnsureResult { | |
504 | using type = Result<T>; | |
505 | }; | |
506 | ||
507 | template <typename T> | |
508 | struct EnsureResult<Result<T>> { | |
509 | using type = Result<T>; | |
510 | }; | |
511 | ||
512 | } // namespace arrow |