Google Cloud C++ Client  1.32.1
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 // http://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 {
27 inline namespace GOOGLE_CLOUD_CPP_NS {
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(Status(StatusCode::kUnknown, "default")) {}
102 
103  StatusOr(StatusOr const&) = default;
104  StatusOr& operator=(StatusOr const&) = default;
105  // NOLINTNEXTLINE(performance-noexcept-move-constructor)
106  StatusOr(StatusOr&&) = default;
107  // NOLINTNEXTLINE(performance-noexcept-move-constructor)
108  StatusOr& operator=(StatusOr&&) = default;
109 
110  /**
111  * Creates a new `StatusOr<T>` holding the error condition @p rhs.
112  *
113  * @par Post-conditions
114  * `ok() == false` and `status() == rhs`.
115  *
116  * @param rhs the status to initialize the object.
117  * @throws std::invalid_argument if `rhs.ok()`. If exceptions are disabled the
118  * program terminates via `google::cloud::Terminate()`
119  */
120  // NOLINTNEXTLINE(google-explicit-constructor)
121  StatusOr(Status rhs) : status_(std::move(rhs)) {
122  if (status_.ok()) {
123  google::cloud::internal::ThrowInvalidArgument(__func__);
124  }
125  }
126 
127  /**
128  * Assigns the given non-OK Status to this `StatusOr<T>`.
129  *
130  * @throws std::invalid_argument if `status.ok()`. If exceptions are disabled
131  * the program terminates via `google::cloud::Terminate()`
132  */
133  StatusOr& operator=(Status status) {
134  *this = StatusOr(std::move(status));
135  return *this;
136  }
137 
138  /**
139  * Assign a `T` (or anything convertible to `T`) into the `StatusOr`.
140  */
141  // Disable this assignment if U==StatusOr<T>. Well, really if U is a
142  // cv-qualified version of StatusOr<T>, so we need to apply std::decay<> to
143  // it first.
144  template <typename U = T>
145  typename std::enable_if< // NOLINT(misc-unconventional-assign-operator)
146  !std::is_same<StatusOr, typename std::decay<U>::type>::value,
147  StatusOr>::type&
148  operator=(U&& rhs) {
149  status_ = Status();
150  value_ = std::forward<U>(rhs);
151  return *this;
152  }
153 
154  /**
155  * Creates a new `StatusOr<T>` holding the value @p rhs.
156  *
157  * @par Post-conditions
158  * `ok() == true` and `value() == rhs`.
159  *
160  * @param rhs the value used to initialize the object.
161  *
162  * @throws only if `T`'s move constructor throws.
163  */
164  // NOLINTNEXTLINE(google-explicit-constructor)
165  StatusOr(T&& rhs) : value_(std::move(rhs)) {}
166 
167  // NOLINTNEXTLINE(google-explicit-constructor)
168  StatusOr(T const& rhs) : value_(rhs) {}
169 
170  bool ok() const { return status_.ok(); }
171  explicit operator bool() const { return status_.ok(); }
172 
173  //@{
174  /**
175  * @name Deference operators.
176  *
177  * @warning Using these operators when `ok() == false` results in undefined
178  * behavior.
179  *
180  * @return All these return a (properly ref and const-qualified) reference to
181  * the underlying value.
182  */
183  T& operator*() & { return *value_; }
184 
185  T const& operator*() const& { return *value_; }
186 
187  T&& operator*() && { return *std::move(value_); }
188 
189  T const&& operator*() const&& { return *std::move(value_); }
190  //@}
191 
192  //@{
193  /**
194  * @name Member access operators.
195  *
196  * @warning Using these operators when `ok() == false` results in undefined
197  * behavior.
198  *
199  * @return All these return a (properly ref and const-qualified) pointer to
200  * the underlying value.
201  */
202  T* operator->() & { return &*value_; }
203 
204  T const* operator->() const& { return &*value_; }
205  //@}
206 
207  //@{
208  /**
209  * @name Value accessors.
210  *
211  * @return All these member functions return a (properly ref and
212  * const-qualified) reference to the underlying value.
213  *
214  * @throws `RuntimeStatusError` with the contents of `status()` if the object
215  * does not contain a value, i.e., if `ok() == false`.
216  */
217  T& value() & {
218  CheckHasValue();
219  return **this;
220  }
221 
222  T const& value() const& {
223  CheckHasValue();
224  return **this;
225  }
226 
227  T&& value() && {
228  CheckHasValue();
229  return std::move(**this);
230  }
231 
232  T const&& value() const&& {
233  CheckHasValue();
234  return std::move(**this);
235  }
236  //@}
237 
238  //@{
239  /**
240  * @name Status accessors.
241  *
242  * @return A reference to the contained `Status`.
243  */
244  Status const& status() const& { return status_; }
245  Status&& status() && { return std::move(status_); }
246  //@}
247 
248  private:
249  void CheckHasValue() const& {
250  if (!ok()) {
251  internal::ThrowStatus(status_);
252  }
253  }
254 
255  // When possible, do not copy the status.
256  void CheckHasValue() && {
257  if (!ok()) {
258  internal::ThrowStatus(std::move(status_));
259  }
260  }
261 
262  Status status_;
263  absl::optional<T> value_;
264 };
265 
266 // Returns true IFF both `StatusOr<T>` objects hold an equal `Status` or an
267 // equal instance of `T`. This function requires that `T` supports equality.
268 template <typename T>
269 bool operator==(StatusOr<T> const& a, StatusOr<T> const& b) {
270  if (!a || !b) return a.status() == b.status();
271  return *a == *b;
272 }
273 
274 // Returns true of `a` and `b` are not equal. See `operator==` docs above for
275 // the definition of equal.
276 template <typename T>
277 bool operator!=(StatusOr<T> const& a, StatusOr<T> const& b) {
278  return !(a == b);
279 }
280 
281 template <typename T>
282 StatusOr<T> make_status_or(T rhs) {
283  return StatusOr<T>(std::move(rhs));
284 }
285 
286 } // namespace GOOGLE_CLOUD_CPP_NS
287 } // namespace cloud
288 } // namespace google
289 
290 #endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STATUS_OR_H