Asylo
statusor.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright 2017 Asylo authors
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #ifndef ASYLO_UTIL_STATUSOR_H_
20 #define ASYLO_UTIL_STATUSOR_H_
21 
22 #include <type_traits>
23 
24 #include "absl/base/attributes.h"
25 #include "absl/meta/type_traits.h"
26 #include "asylo/util/logging.h"
27 #include "asylo/util/cleanup.h"
28 #include "asylo/util/status.h"
29 #include "asylo/util/status_error_space.h"
30 
31 namespace asylo {
32 
33 #ifdef NDEBUG
34 ABSL_CONST_INIT extern const char kValueMoveConstructorMsg[];
35 ABSL_CONST_INIT extern const char kValueMoveAssignmentMsg[];
36 ABSL_CONST_INIT extern const char kValueOrDieMovedMsg[];
37 ABSL_CONST_INIT extern const char kStatusMoveConstructorMsg[];
38 ABSL_CONST_INIT extern const char kStatusMoveAssignmentMsg[];
39 #else
40 ABSL_CONST_INIT extern const char kValueMoveConstructorMsg[];
41 ABSL_CONST_INIT extern const char kValueMoveAssignmentMsg[];
42 ABSL_CONST_INIT extern const char kStatusMoveConstructorMsg[];
43 ABSL_CONST_INIT extern const char kValueOrDieMovedMsg[];
44 ABSL_CONST_INIT extern const char kStatusMoveAssignmentMsg[];
45 #endif
46 
47 /// A class for representing either a usable value, or an error.
48 ///
49 /// A StatusOr object either contains a value of type `T` or a Status object
50 /// explaining why such a value is not present. The type `T` must be
51 /// copy-constructible and/or move-constructible.
52 ///
53 /// The state of a StatusOr object may be determined by calling ok() or
54 /// status(). The ok() method returns true if the object contains a valid value.
55 /// The status() method returns the internal Status object. A StatusOr object
56 /// that contains a valid value will return an OK Status for a call to status().
57 ///
58 /// A value of type `T` may be extracted from a StatusOr object through a call
59 /// to ValueOrDie(). This function should only be called if a call to ok()
60 /// returns true. Sample usage:
61 ///
62 /// ```
63 /// asylo::StatusOr<Foo> result = CalculateFoo();
64 /// if (result.ok()) {
65 /// Foo foo = result.ValueOrDie();
66 /// foo->DoSomethingCool();
67 /// } else {
68 /// LOG(ERROR) << result.status();
69 /// }
70 /// ```
71 ///
72 /// If `T` is a move-only type, like `std::unique_ptr<>`, then the value should
73 /// only be extracted after invoking `std::move()` on the StatusOr object.
74 /// Sample usage:
75 ///
76 /// ```
77 /// asylo::StatusOr<std::unique_ptr<Foo>> result = CalculateFoo();
78 /// if (result.ok()) {
79 /// std::unique_ptr<Foo> foo = std::move(result).ValueOrDie();
80 /// foo->DoSomethingCool();
81 /// } else {
82 /// LOG(ERROR) << result.status();
83 /// }
84 /// ```
85 ///
86 /// StatusOr is provided for the convenience of implementing functions that
87 /// return some value but may fail during execution. For instance, consider a
88 /// function with the following signature:
89 ///
90 /// ```
91 /// asylo::Status CalculateFoo(int *output);
92 /// ```
93 ///
94 /// This function may instead be written as:
95 ///
96 /// ```
97 /// asylo::StatusOr<int> CalculateFoo();
98 /// ```
99 template <class T>
100 class StatusOr {
101  template <typename U>
102  friend class StatusOr;
103 
104  // A traits class that determines whether a type U is implicitly convertible
105  // from a type V. If it is convertible, then the `value` member of this class
106  // is statically set to true, otherwise it is statically set to false.
107  template <class U, typename V>
108  struct is_implicitly_constructible
109  : absl::conjunction<std::is_constructible<U, V>,
110  std::is_convertible<V, U> > {};
111 
112  public:
113  /// Constructs a StatusOr object that contains a non-OK status.
114  /// The non-OK status has an error code of -1. This is a non-standard POSIX
115  /// error code and is used in this context to indicate an unknown error.
116  ///
117  /// This constructor is marked `explicit` to prevent attempts to `return {}`
118  /// from a function with a return type of, for example,
119  /// `StatusOr<std::vector<int>>`. While `return {}` seems like it would return
120  /// an empty vector, it will actually invoke the default constructor of
121  /// StatusOr.
122  explicit StatusOr()
123  : variant_(Status(error::GoogleError::UNKNOWN, "Unknown error")),
124  has_value_(false) {}
125 
126  ~StatusOr() {
127  if (has_value_) {
128  variant_.value_.~T();
129  } else {
130  variant_.status_.~Status();
131  }
132  }
133 
134  /// Constructs a StatusOr object with the given non-OK Status object. All
135  /// calls to ValueOrDie() on this object will abort. The given `status` must
136  /// not be an OK status, otherwise this constructor will abort.
137  ///
138  /// This constructor is not declared explicit so that a function with a return
139  /// type of `StatusOr<T>` can return a Status object, and the status will be
140  /// implicitly converted to the appropriate return type as a matter of
141  /// convenience.
142  ///
143  /// \param status The non-OK Status object to initalize to.
144  StatusOr(const Status &status) : variant_(status), has_value_(false) {
145  if (status.ok()) {
146  LOG(FATAL) << "Cannot instantiate StatusOr with Status::OkStatus()";
147  }
148  }
149 
150  /// Constructs a StatusOr object that contains `value`. The resulting object
151  /// is considered to have an OK status. The wrapped element can be accessed
152  /// with ValueOrDie().
153  ///
154  /// This constructor is made implicit so that a function with a return type of
155  /// `StatusOr<T>` can return an object of type `U &&`, implicitly converting
156  /// it to a `StatusOr<T>` object.
157  ///
158  /// Note that `T` must be implicitly constructible from `U`, and `U` must not
159  /// be a (cv-qualified) Status or Status-reference type. Due to C++
160  /// reference-collapsing rules and perfect-forwarding semantics, this
161  /// constructor matches invocations that pass `value` either as a const
162  /// reference or as an rvalue reference. Since StatusOr needs to work for both
163  /// reference and rvalue-reference types, the constructor uses perfect
164  /// forwarding to avoid invalidating arguments that were passed by reference.
165  /// See http://thbecker.net/articles/rvalue_references/section_08.html for
166  /// additional details.
167  ///
168  /// \param value The value to initialize to.
169  template <typename U,
170  typename E = typename std::enable_if<
172  !std::is_same<typename std::remove_reference<
173  typename std::remove_cv<U>::type>::type,
174  Status>::value>::type>
175  StatusOr(U &&value) : variant_(std::forward<U>(value)), has_value_(true) {}
176 
177  /// Copy constructor.
178  ///
179  /// This constructor needs to be explicitly defined because the presence of
180  /// the move-assignment operator deletes the default copy constructor. In such
181  /// a scenario, since the deleted copy constructor has stricter binding rules
182  /// than the templated copy constructor, the templated constructor cannot act
183  /// as a copy constructor, and any attempt to copy-construct a `StatusOr`
184  /// object results in a compilation error.
185  ///
186  /// \param other The value to copy from.
187  StatusOr(const StatusOr &other) : has_value_(other.has_value_) {
188  if (has_value_) {
189  new (&variant_) variant(other.variant_.value_);
190  } else {
191  new (&variant_) variant(other.variant_.status_);
192  }
193  }
194 
195  /// Templatized constructor that constructs a `StatusOr<T>` from a const
196  /// reference to a `StatusOr<U>`.
197  ///
198  /// `T` must be implicitly constructible from `const U &`.
199  ///
200  /// \param other The value to copy from.
201  template <typename U,
202  typename E = typename std::enable_if<
204  StatusOr(const StatusOr<U> &other) : has_value_(other.has_value_) {
205  if (has_value_) {
206  new (&variant_) variant(other.variant_.value_);
207  } else {
208  new (&variant_) variant(other.variant_.status_);
209  }
210  }
211 
212  /// Copy-assignment operator.
213  ///
214  /// \param other The StatusOr object to copy.
215  StatusOr &operator=(const StatusOr &other) {
216  // Check for self-assignment.
217  if (this == &other) {
218  return *this;
219  }
220 
221  // Construct the variant object using the variant object of the source.
222  if (other.has_value_) {
223  AssignValue(other.variant_.value_);
224  } else {
225  AssignStatus(other.variant_.status_);
226  }
227  return *this;
228  }
229 
230  /// Templatized constructor which constructs a `StatusOr<T>` by moving the
231  /// contents of a `StatusOr<U>`. `T` must be implicitly constructible from `U
232  /// &&`.
233  ///
234  /// Sets `other` to contain a non-OK status with a`StatusError::MOVED`
235  /// error code.
236  ///
237  /// \param other The StatusOr object to move from and set to a non-OK status.
238  template <typename U, typename E = typename std::enable_if<
240  StatusOr(StatusOr<U> &&other) : has_value_(other.has_value_) {
241  if (has_value_) {
242  new (&variant_) variant(std::move(other.variant_.value_));
243  other.OverwriteValueWithStatus(
244  Status(error::StatusError::MOVED, kValueMoveConstructorMsg));
245  } else {
246  new (&variant_) variant(std::move(other.variant_.status_));
247 #ifndef NDEBUG
248  // The other.variant_.status_ gets moved and invalidated with a Status-
249  // specific error message above. To aid debugging, set the status to a
250  // StatusOr-specific error message.
251  other.variant_.status_ =
252  Status(error::StatusError::MOVED, kStatusMoveConstructorMsg);
253 #endif
254  }
255  }
256 
257  /// Move-assignment operator.
258  ///
259  /// Sets `other` to contain a non-OK status with a `StatusError::MOVED` error
260  /// code.
261  ///
262  /// \param other The StatusOr object to assign from and set to a non-OK
263  /// status.
265  // Check for self-assignment.
266  if (this == &other) {
267  return *this;
268  }
269 
270  // Construct the variant object using the variant object of the donor.
271  if (other.has_value_) {
272  AssignValue(std::move(other.variant_.value_));
273  other.OverwriteValueWithStatus(
274  Status(error::StatusError::MOVED, kValueMoveAssignmentMsg));
275  } else {
276  AssignStatus(std::move(other.variant_.status_));
277 #ifndef NDEBUG
278  // The other.variant_.status_ gets moved and invalidated with a Status-
279  // specific error message above. To aid debugging, set the status to a
280  // StatusOr-specific error message.
281  other.variant_.status_ =
282  Status(error::StatusError::MOVED, kStatusMoveAssignmentMsg);
283 #endif
284  }
285 
286  return *this;
287  }
288 
289  /// Indicates whether the object contains a `T` value.
290  ///
291  /// \return True if this StatusOr object's status is OK (i.e. a call to ok()
292  /// returns true). If this function returns true, then it is safe to access
293  /// the wrapped element through a call to ValueOrDie().
294  bool ok() const { return has_value_; }
295 
296  /// Gets the stored status object, or an OK status if a `T` value is stored.
297  ///
298  /// \return The stored non-OK status object, or an OK status if this object
299  /// has a value.
300  Status status() const { return ok() ? Status::OkStatus() : variant_.status_; }
301 
302  /// Gets the stored `T` value.
303  ///
304  /// This method should only be called if this StatusOr object's status is OK
305  /// (i.e. a call to ok() returns true), otherwise this call will abort.
306  ///
307  /// \return The stored `T` value.
308  const T &ValueOrDie() const & {
309  if (!ok()) {
310  LOG(FATAL)
311  << "Object does not have a usable value, instead contains status: "
312  << status();
313  }
314  return variant_.value_;
315  }
316 
317  /// Gets a mutable reference to the stored `T` value.
318  ///
319  /// This method should only be called if this StatusOr 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.
323  T &ValueOrDie() & {
324  if (!ok()) {
325  LOG(FATAL)
326  << "Object does not have a usable value, instead contains status: "
327  << status();
328  }
329  return variant_.value_;
330  }
331 
332  /// Moves and returns the internally-stored `T` value.
333  ///
334  /// This method should only be called if this StatusOr object's status is OK
335  /// (i.e. a call to ok() returns true), otherwise this call will abort. The
336  /// StatusOr object is invalidated after this call and will be updated to
337  /// contain a non-OK status with a `StatusError::MOVED` error code.
338  ///
339  /// \return The stored `T` value.
340  T ValueOrDie() && {
341  if (!ok()) {
342  LOG(FATAL)
343  << "Object does not have a usable value, instead contains status: "
344  << status();
345  }
346 
347  // Invalidate this StatusOr object before returning control to caller.
348  Cleanup set_moved_status([this] {
349  OverwriteValueWithStatus(
350  Status(error::StatusError::MOVED, kValueOrDieMovedMsg));
351  });
352  return std::move(variant_.value_);
353  }
354 
355  private:
356  // Resets the |variant_| member to contain |status|.
357  template <class U>
358  void AssignStatus(U &&status) {
359  if (ok()) {
360  OverwriteValueWithStatus(std::forward<U>(status));
361  } else {
362  // Reuse the existing Status object. has_value_ is already false.
363  variant_.status_ = std::forward<U>(status);
364  }
365  }
366 
367  // Under the assumption that |this| is currently holding a value, resets the
368  // |variant_| member to contain |status| and sets |has_value_| to indicate
369  // that |this| does not have a value. Destroys the existing |variant_| member.
370  template <class U>
371  void OverwriteValueWithStatus(U &&status) {
372 #ifndef NDEBUG
373  if (!ok()) {
374  LOG(FATAL) << "Object does not have a value to change from";
375  }
376 #endif
377  variant_.value_.~T();
378  new (&variant_) variant(std::forward<U>(status));
379  has_value_ = false;
380  }
381 
382  // Resets the |variant_| member to contain the |value| and sets |has_value_|
383  // to indicate that the StatusOr object has a value. Destroys the existing
384  // |variant_| member.
385  template <class U>
386  void AssignValue(U &&value) {
387  if (ok()) {
388  // We cannot assume that T is move-assignable.
389  variant_.value_.~T();
390  } else {
391  variant_.status_.~Status();
392  }
393  new (&variant_) variant(std::forward<U>(value));
394  has_value_ = true;
395  }
396 
397  union variant {
398  // A non-OK status.
399  Status status_;
400 
401  // An element of type T.
402  T value_;
403 
404  variant() {}
405 
406  variant(const Status &status) : status_(status) {}
407 
408  variant(Status &&status) : status_(std::move(status)) {}
409 
410  template <typename U, typename E = typename std::enable_if<
411  is_implicitly_constructible<T, U>::value>::type>
412  variant(U &&value) : value_(std::forward<U>(value)) {}
413 
414  // This destructor must be explicitly defined because it is deleted due to
415  // the variant type having non-static data members with non-trivial
416  // destructors.
417  ~variant() {}
418  };
419 
420  // One of: a non-OK status or an element of type T.
421  variant variant_;
422 
423  // Indicates the active member of the variant_ member.
424  //
425  // A value of true indicates that value_ is the active member of variant_.
426  //
427  // A value of false indicates that status_ is the active member of variant_.
428  bool has_value_;
429 };
430 
431 } // namespace asylo
432 
433 #endif // ASYLO_UTIL_STATUSOR_H_
const T & ValueOrDie() const &
Gets the stored T value.
Definition: statusor.h:308
StatusOr & operator=(const StatusOr &other)
Copy-assignment operator.
Definition: statusor.h:215
StatusOr(StatusOr< U > &&other)
Templatized constructor which constructs a StatusOr<T> by moving the contents of a StatusOr<U>...
Definition: statusor.h:240
StatusOr()
Constructs a StatusOr object that contains a non-OK status.
Definition: statusor.h:122
Status status() const
Gets the stored status object, or an OK status if a T value is stored.
Definition: statusor.h:300
friend class StatusOr
Definition: statusor.h:102
StatusOr(U &&value)
Constructs a StatusOr object that contains value.
Definition: statusor.h:175
ABSL_CONST_INIT const char kStatusMoveAssignmentMsg[]
T & ValueOrDie() &
Gets a mutable reference to the stored T value.
Definition: statusor.h:323
StatusOr(const StatusOr< U > &other)
Templatized constructor that constructs a StatusOr<T> from a const reference to a StatusOr<U>...
Definition: statusor.h:204
StatusOr(const StatusOr &other)
Copy constructor.
Definition: statusor.h:187
StatusOr & operator=(StatusOr &&other)
Move-assignment operator.
Definition: statusor.h:264
T ValueOrDie() &&
Moves and returns the internally-stored T value.
Definition: statusor.h:340
~StatusOr()
Definition: statusor.h:126
StatusOr(const Status &status)
Constructs a StatusOr object with the given non-OK Status object.
Definition: statusor.h:144
bool ok() const
Indicates whether the object contains a T value.
Definition: statusor.h:294