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