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