Google Cloud C++ Client  2.0.0
C++ Client Library for Google Cloud Platform
status_or.h
Go to the documentation of this file.
1 // Copyright 2018 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STATUS_OR_H
16 #define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STATUS_OR_H
17 
18 #include "google/cloud/internal/throw_delegate.h"
19 #include "google/cloud/status.h"
20 #include "google/cloud/version.h"
21 #include "absl/types/optional.h"
22 #include <type_traits>
23 #include <utility>
24 
25 namespace google {
26 namespace cloud {
28 
29 /**
30  * Holds a value or a `Status` indicating why there is no value.
31  *
32  * `StatusOr<T>` represents either a usable `T` value or a `Status` object
33  * explaining why a `T` value is not present. Typical usage of `StatusOr<T>`
34  * looks like usage of a smart pointer, or even a std::optional<T>, in that you
35  * first check its validity using a conversion to bool (or by calling
36  * `StatusOr::ok()`), then you may dereference the object to access the
37  * contained value. It is undefined behavior (UB) to dereference a
38  * `StatusOr<T>` that is not "ok". For example:
39  *
40  * @code
41  * StatusOr<Foo> foo = FetchFoo();
42  * if (!foo) { // Same as !foo.ok()
43  * // handle error and probably look at foo.status()
44  * } else {
45  * foo->DoSomethingFooey(); // UB if !foo
46  * }
47  * @endcode
48  *
49  * Alternatively, you may call the `StatusOr::value()` member function,
50  * which is defined to throw an exception if there is no `T` value, or crash
51  * the program if exceptions are disabled. It is never UB to call
52  * `.value()`.
53  *
54  * @code
55  * StatusOr<Foo> foo = FetchFoo();
56  * foo.value().DoSomethingFooey(); // May throw/crash if there is no value
57  * @endcode
58  *
59  * Functions that can fail will often return a `StatusOr<T>` instead of
60  * returning an error code and taking a `T` out-param, or rather than directly
61  * returning the `T` and throwing an exception on error. StatusOr is used so
62  * that callers can choose whether they want to explicitly check for errors,
63  * crash the program, or throw exceptions. Since constructors do not have a
64  * return value, they should be designed in such a way that they cannot fail by
65  * moving the object's complex initialization logic into a separate factory
66  * function that itself can return a `StatusOr<T>`. For example:
67  *
68  * @code
69  * class Bar {
70  * public:
71  * Bar(Arg arg);
72  * ...
73  * };
74  * StatusOr<Bar> MakeBar() {
75  * ... complicated logic that might fail
76  * return Bar(std::move(arg));
77  * }
78  * @endcode
79  *
80  * `StatusOr<T>` supports equality comparisons if the underlying type `T` does.
81  *
82  * TODO(...) - the current implementation is fairly naive with respect to `T`,
83  * it is unlikely to work correctly for reference types, types without default
84  * constructors, arrays.
85  *
86  * @tparam T the type of the value.
87  */
88 template <typename T>
89 class StatusOr final {
90  public:
91  /**
92  * A `value_type` member for use in generic programming.
93  *
94  * This is analogous to that of `std::optional::value_type`.
95  */
96  using value_type = T;
97 
98  /**
99  * Initializes with an error status (UNKNOWN).
100  */
101  StatusOr() : StatusOr(MakeDefaultStatus()) {}
102 
103  StatusOr(StatusOr const&) = default;
104  StatusOr& operator=(StatusOr const&) = default;
105  // NOLINTNEXTLINE(performance-noexcept-move-constructor)
106  StatusOr(StatusOr&& other)
107  : status_(std::move(other.status_)), value_(std::move(other.value_)) {
108  other.status_ = MakeDefaultStatus();
109  }
110  // NOLINTNEXTLINE(performance-noexcept-move-constructor)
111  StatusOr& operator=(StatusOr&& other) {
112  status_ = std::move(other.status_);
113  value_ = std::move(other.value_);
114  other.status_ = MakeDefaultStatus();
115  return *this;
116  }
117 
118  /**
119  * Creates a new `StatusOr<T>` holding the error condition @p rhs.
120  *
121  * @par Post-conditions
122  * `ok() == false` and `status() == rhs`.
123  *
124  * @param rhs the status to initialize the object.
125  * @throws std::invalid_argument if `rhs.ok()`. If exceptions are disabled the
126  * program terminates via `google::cloud::Terminate()`
127  */
128  // NOLINTNEXTLINE(google-explicit-constructor)
129  StatusOr(Status rhs) : status_(std::move(rhs)) {
130  if (status_.ok()) {
131  google::cloud::internal::ThrowInvalidArgument(__func__);
132  }
133  }
134 
135  /**
136  * Assigns the given non-OK Status to this `StatusOr<T>`.
137  *
138  * @throws std::invalid_argument if `status.ok()`. If exceptions are disabled
139  * the program terminates via `google::cloud::Terminate()`
140  */
141  StatusOr& operator=(Status status) {
142  *this = StatusOr(std::move(status));
143  return *this;
144  }
145 
146  /**
147  * Assign a `T` (or anything convertible to `T`) into the `StatusOr`.
148  */
149  // Disable this assignment if U==StatusOr<T>. Well, really if U is a
150  // cv-qualified version of StatusOr<T>, so we need to apply std::decay<> to
151  // it first.
152  template <typename U = T>
153  typename std::enable_if< // NOLINT(misc-unconventional-assign-operator)
154  !std::is_same<StatusOr, typename std::decay<U>::type>::value,
155  StatusOr>::type&
156  operator=(U&& rhs) {
157  status_ = Status();
158  value_ = std::forward<U>(rhs);
159  return *this;
160  }
161 
162  /**
163  * Creates a new `StatusOr<T>` holding the value @p rhs.
164  *
165  * @par Post-conditions
166  * `ok() == true` and `value() == rhs`.
167  *
168  * @param rhs the value used to initialize the object.
169  *
170  * @throws only if `T`'s move constructor throws.
171  */
172  // NOLINTNEXTLINE(google-explicit-constructor)
173  StatusOr(T&& rhs) : value_(std::move(rhs)) {}
174 
175  // NOLINTNEXTLINE(google-explicit-constructor)
176  StatusOr(T const& rhs) : value_(rhs) {}
177 
178  bool ok() const { return status_.ok(); }
179  explicit operator bool() const { return status_.ok(); }
180 
181  //@{
182  /**
183  * @name Deference operators.
184  *
185  * @warning Using these operators when `ok() == false` results in undefined
186  * behavior.
187  *
188  * @return All these return a (properly ref and const-qualified) reference to
189  * the underlying value.
190  */
191  T& operator*() & { return *value_; }
192 
193  T const& operator*() const& { return *value_; }
194 
195  T&& operator*() && { return *std::move(value_); }
196 
197  T const&& operator*() const&& { return *std::move(value_); }
198  //@}
199 
200  //@{
201  /**
202  * @name Member access operators.
203  *
204  * @warning Using these operators when `ok() == false` results in undefined
205  * behavior.
206  *
207  * @return All these return a (properly ref and const-qualified) pointer to
208  * the underlying value.
209  */
210  T* operator->() & { return &*value_; }
211 
212  T const* operator->() const& { return &*value_; }
213  //@}
214 
215  //@{
216  /**
217  * @name Value accessors.
218  *
219  * @return All these member functions return a (properly ref and
220  * const-qualified) reference to the underlying value.
221  *
222  * @throws `RuntimeStatusError` with the contents of `status()` if the object
223  * does not contain a value, i.e., if `ok() == false`.
224  */
225  T& value() & {
226  CheckHasValue();
227  return **this;
228  }
229 
230  T const& value() const& {
231  CheckHasValue();
232  return **this;
233  }
234 
235  T&& value() && {
236  CheckHasValue();
237  return std::move(**this);
238  }
239 
240  T const&& value() const&& {
241  CheckHasValue();
242  return std::move(**this);
243  }
244  //@}
245 
246  //@{
247  /**
248  * @name Status accessors.
249  *
250  * @return A reference to the contained `Status`.
251  */
252  Status const& status() const& { return status_; }
253  Status&& status() && { return std::move(status_); }
254  //@}
255 
256  private:
257  static Status MakeDefaultStatus() {
258  return Status{StatusCode::kUnknown, "default"};
259  }
260 
261  void CheckHasValue() const& {
262  if (!ok()) {
263  internal::ThrowStatus(status_);
264  }
265  }
266 
267  // When possible, do not copy the status.
268  void CheckHasValue() && {
269  if (!ok()) {
270  internal::ThrowStatus(std::move(status_));
271  }
272  }
273 
274  Status status_;
275  absl::optional<T> value_;
276 };
277 
278 // Returns true IFF both `StatusOr<T>` objects hold an equal `Status` or an
279 // equal instance of `T`. This function requires that `T` supports equality.
280 template <typename T>
281 bool operator==(StatusOr<T> const& a, StatusOr<T> const& b) {
282  if (!a || !b) return a.status() == b.status();
283  return *a == *b;
284 }
285 
286 // Returns true of `a` and `b` are not equal. See `operator==` docs above for
287 // the definition of equal.
288 template <typename T>
289 bool operator!=(StatusOr<T> const& a, StatusOr<T> const& b) {
290  return !(a == b);
291 }
292 
293 template <typename T>
294 StatusOr<T> make_status_or(T rhs) {
295  return StatusOr<T>(std::move(rhs));
296 }
297 
299 } // namespace cloud
300 } // namespace google
301 
302 #endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STATUS_OR_H