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`. Due to C++
161  /// reference-collapsing rules and perfect-forwarding semantics, this
162  /// constructor matches invocations that pass `value` either as a const
163  /// reference or as an rvalue reference. See
164  /// http://thbecker.net/articles/rvalue_references/section_08.html for
165  /// additional details.
166  ///
167  /// \param value The value to initialize to.
168  template <typename U, typename E = typename std::enable_if<
170  StatusOr(U &&value) : variant_(std::forward<U>(value)), has_value_(true) {
171 
172  }
173 
174  /// Copy constructor.
175  ///
176  /// This constructor needs to be explicitly defined because the presence of
177  /// the move-assignment operator deletes the default copy constructor. In such
178  /// a scenario, since the deleted copy constructor has stricter binding rules
179  /// than the templated copy constructor, the templated constructor cannot act
180  /// as a copy constructor, and any attempt to copy-construct a `StatusOr`
181  /// object results in a compilation error.
182  ///
183  /// \param other The value to copy from.
184  StatusOr(const StatusOr &other) : has_value_(other.has_value_) {
185  if (has_value_) {
186  new (&variant_) variant(other.variant_.value_);
187  } else {
188  new (&variant_) variant(other.variant_.status_);
189  }
190  }
191 
192  /// Templatized constructor that constructs a `StatusOr<T>` from a const
193  /// reference to a `StatusOr<U>`.
194  ///
195  /// `T` must be implicitly constructible from `const U &`.
196  ///
197  /// \param other The value to copy from.
198  template <typename U,
199  typename E = typename std::enable_if<
201  StatusOr(const StatusOr<U> &other) : has_value_(other.has_value_) {
202  if (has_value_) {
203  new (&variant_) variant(other.variant_.value_);
204  } else {
205  new (&variant_) variant(other.variant_.status_);
206  }
207  }
208 
209  /// Copy-assignment operator.
210  ///
211  /// \param other The StatusOr object to copy.
212  StatusOr &operator=(const StatusOr &other) {
213  // Check for self-assignment.
214  if (this == &other) {
215  return *this;
216  }
217 
218  // Construct the variant object using the variant object of the source.
219  if (other.has_value_) {
220  AssignValue(other.variant_.value_);
221  } else {
222  AssignStatus(other.variant_.status_);
223  }
224  return *this;
225  }
226 
227  /// Templatized constructor which constructs a `StatusOr<T>` by moving the
228  /// contents of a `StatusOr<U>`. `T` must be implicitly constructible from `U
229  /// &&`.
230  ///
231  /// Sets `other` to contain a non-OK status with a`StatusError::MOVED`
232  /// error code.
233  ///
234  /// \param other The StatusOr object to move from and set to a non-OK status.
235  template <typename U, typename E = typename std::enable_if<
237  StatusOr(StatusOr<U> &&other) : has_value_(other.has_value_) {
238  if (has_value_) {
239  new (&variant_) variant(std::move(other.variant_.value_));
240  other.OverwriteValueWithStatus(
241  Status(error::StatusError::MOVED, kValueMoveConstructorMsg));
242  } else {
243  new (&variant_) variant(std::move(other.variant_.status_));
244 #ifndef NDEBUG
245  // The other.variant_.status_ gets moved and invalidated with a Status-
246  // specific error message above. To aid debugging, set the status to a
247  // StatusOr-specific error message.
248  other.variant_.status_ =
249  Status(error::StatusError::MOVED, kStatusMoveConstructorMsg);
250 #endif
251  }
252  }
253 
254  /// Move-assignment operator.
255  ///
256  /// Sets `other` to contain a non-OK status with a `StatusError::MOVED` error
257  /// code.
258  ///
259  /// \param other The StatusOr object to assign from and set to a non-OK
260  /// status.
262  // Check for self-assignment.
263  if (this == &other) {
264  return *this;
265  }
266 
267  // Construct the variant object using the variant object of the donor.
268  if (other.has_value_) {
269  AssignValue(std::move(other.variant_.value_));
270  other.OverwriteValueWithStatus(
271  Status(error::StatusError::MOVED, kValueMoveAssignmentMsg));
272  } else {
273  AssignStatus(std::move(other.variant_.status_));
274 #ifndef NDEBUG
275  // The other.variant_.status_ gets moved and invalidated with a Status-
276  // specific error message above. To aid debugging, set the status to a
277  // StatusOr-specific error message.
278  other.variant_.status_ =
279  Status(error::StatusError::MOVED, kStatusMoveAssignmentMsg);
280 #endif
281  }
282 
283  return *this;
284  }
285 
286  /// Indicates whether the object contains a `T` value.
287  ///
288  /// \return True if this StatusOr object's status is OK (i.e. a call to ok()
289  /// returns true). If this function returns true, then it is safe to access
290  /// the wrapped element through a call to ValueOrDie().
291  bool ok() const { return has_value_; }
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  Status status() const { return ok() ? Status::OkStatus() : variant_.status_; }
298 
299  /// Gets the stored `T` value.
300  ///
301  /// This method should only be called if this StatusOr 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 (!ok()) {
307  LOG(FATAL) << "Object does not have a usable value";
308  }
309  return variant_.value_;
310  }
311 
312  /// Gets a mutable reference to the stored `T` value.
313  ///
314  /// This method should only be called if this StatusOr object's status is OK
315  /// (i.e. a call to ok() returns true), otherwise this call will abort.
316  ///
317  /// \return The stored `T` value.
318  T &ValueOrDie() & {
319  if (!ok()) {
320  LOG(FATAL) << "Object does not have a usable value";
321  }
322  return variant_.value_;
323  }
324 
325  /// Moves and returns the internally-stored `T` value.
326  ///
327  /// This method should only be called if this StatusOr object's status is OK
328  /// (i.e. a call to ok() returns true), otherwise this call will abort. The
329  /// StatusOr object is invalidated after this call and will be updated to
330  /// contain a non-OK status with a `StatusError::MOVED` error code.
331  ///
332  /// \return The stored `T` value.
333  T ValueOrDie() && {
334  if (!ok()) {
335  LOG(FATAL) << "Object does not have a usable value";
336  }
337  T tmp(std::move(variant_.value_));
338 
339  // Invalidate this StatusOr object.
340  OverwriteValueWithStatus(
341  Status(error::StatusError::MOVED, kValueOrDieMovedMsg));
342  return std::move(tmp);
343  }
344 
345  private:
346  // Resets the |variant_| member to contain |status|.
347  template <class U>
348  void AssignStatus(U &&status) {
349  if (ok()) {
350  OverwriteValueWithStatus(std::forward<U>(status));
351  } else {
352  // Reuse the existing Status object. has_value_ is already false.
353  variant_.status_ = std::forward<U>(status);
354  }
355  }
356 
357  // Under the assumption that |this| is currently holding a value, resets the
358  // |variant_| member to contain |status| and sets |has_value_| to indicate
359  // that |this| does not have a value. Destroys the existing |variant_| member.
360  template <class U>
361  void OverwriteValueWithStatus(U &&status) {
362 #ifndef NDEBUG
363  if (!ok()) {
364  LOG(FATAL) << "Object does not have a value to change from";
365  }
366 #endif
367  variant_.value_.~T();
368  new (&variant_) variant(std::forward<U>(status));
369  has_value_ = false;
370  }
371 
372  // Resets the |variant_| member to contain the |value| and sets |has_value_|
373  // to indicate that the StatusOr object has a value. Destroys the existing
374  // |variant_| member.
375  template <class U>
376  void AssignValue(U &&value) {
377  if (ok()) {
378  // We cannot assume that T is move-assignable.
379  variant_.value_.~T();
380  } else {
381  variant_.status_.~Status();
382  }
383  new (&variant_) variant(std::forward<U>(value));
384  has_value_ = true;
385  }
386 
387  union variant {
388  // A non-OK status.
389  Status status_;
390 
391  // An element of type T.
392  T value_;
393 
394  variant() {}
395 
396  variant(const Status &status) : status_(status) {}
397 
398  variant(Status &&status) : status_(std::move(status)) {}
399 
400  template <typename U, typename E = typename std::enable_if<
401  is_implicitly_constructible<T, U>::value>::type>
402  variant(U &&value) : value_(std::forward<U>(value)) {}
403 
404  // This destructor must be explicitly defined because it is deleted due to
405  // the variant type having non-static data members with non-trivial
406  // destructors.
407  ~variant() {}
408  };
409 
410  // One of: a non-OK status or an element of type T.
411  variant variant_;
412 
413  // Indicates the active member of the variant_ member.
414  //
415  // A value of true indicates that value_ is the active member of variant_.
416  //
417  // A value of false indicates that status_ is the active member of variant_.
418  bool has_value_;
419 };
420 
421 } // namespace asylo
422 
423 #endif // ASYLO_UTIL_STATUSOR_H_
const T & ValueOrDie() const &
Gets the stored T value.
Definition: statusor.h:305
constexpr char kValueMoveAssignmentMsg[]
Definition: statusor.h:40
StatusOr & operator=(const StatusOr &other)
Copy-assignment operator.
Definition: statusor.h:212
StatusOr(StatusOr< U > &&other)
Templatized constructor which constructs a StatusOr<T> by moving the contents of a StatusOr<U>...
Definition: statusor.h:237
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:297
friend class StatusOr
Definition: statusor.h:104
StatusOr(U &&value)
Constructs a StatusOr object that contains value.
Definition: statusor.h:170
T & ValueOrDie() &
Gets a mutable reference to the stored T value.
Definition: statusor.h:318
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:201
StatusOr(const StatusOr &other)
Copy constructor.
Definition: statusor.h:184
StatusOr & operator=(StatusOr &&other)
Move-assignment operator.
Definition: statusor.h:261
T ValueOrDie() &&
Moves and returns the internally-stored T value.
Definition: statusor.h:333
~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:291
constexpr char kStatusMoveConstructorMsg[]
Definition: statusor.h:42
constexpr char kValueOrDieMovedMsg[]
Definition: statusor.h:44