Google Cloud C++ Client  0.4.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 // 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 <type_traits>
21 #include <utility>
22 
23 namespace google {
24 namespace cloud {
25 inline namespace GOOGLE_CLOUD_CPP_NS {
26 /**
27  * Holds a value or a `Status` indicating why there is no value.
28  *
29  * `StatusOr<T>` represents either a usable `T` value or a `Status` object
30  * explaining why a `T` value is not present. Typical usage of `StatusOr<T>`
31  * looks like usage of a smart pointer, or even a std::optional<T>, in that you
32  * first check its validity using a conversion to bool (or by calling
33  * `StatusOr::ok()`), then you may dereference the object to access the
34  * contained value. It is undefined behavior (UB) to dereference a
35  * `StatusOr<T>` that is not "ok". For example:
36  *
37  * @code
38  * StatusOr<Foo> foo = FetchFoo();
39  * if (!foo) { // Same as !foo.ok()
40  * // handle error and probably look at foo.status()
41  * } else {
42  * foo->DoSomethingFooey(); // UB if !foo
43  * }
44  * @endcode
45  *
46  * Alternatively, you may call the `StatusOr::value()` member function,
47  * which is defined to throw an exception if there is no `T` value, or crash
48  * the program if exceptions are disabled. It is never UB to call
49  * `.value()`.
50  *
51  * @code
52  * StatusOr<Foo> foo = FetchFoo();
53  * foo.value().DoSomethingFooey(); // May throw/crash if there is no value
54  * @endcode
55  *
56  * Functions that can fail will often return a `StatusOr<T>` instead of
57  * returning an error code and taking a `T` out-param, or rather than directly
58  * returning the `T` and throwing an exception on error. StatusOr is used so
59  * that callers can choose whether they want to explicitly check for errors,
60  * crash the program, or throw exceptions. Since constructors do not have a
61  * return value, they should be designed in such a way that they cannot fail by
62  * moving the object's complex initialization logic into a separate factory
63  * function that itself can return a `StatusOr<T>`. For example:
64  *
65  * @code
66  * class Bar {
67  * public:
68  * Bar(Arg arg);
69  * ...
70  * };
71  * StatusOr<Bar> MakeBar() {
72  * ... complicated logic that might fail
73  * return Bar(std::move(arg));
74  * }
75  * @endcode
76  *
77  * TODO(...) - the current implementation is fairly naive with respect to `T`,
78  * it is unlikely to work correctly for reference types, types without default
79  * constructors, arrays.
80  *
81  * @tparam T the type of the value.
82  */
83 template <typename T>
84 class StatusOr final {
85  public:
86  /**
87  * Initializes with an error status (UNKNOWN).
88  *
89  * TODO(#548) - currently storage::Status does not define the status codes,
90  * they are simply integers, usually HTTP status codes. We need to map to
91  * the well-defined set of status codes.
92  */
93  StatusOr() : StatusOr(Status(StatusCode::kUnknown, "default")) {}
94 
95  /**
96  * Creates a new `StatusOr<T>` holding the error condition @p rhs.
97  *
98  * @par Post-conditions
99  * `ok() == false` and `status() == rhs`.
100  *
101  * @param rhs the status to initialize the object.
102  * @throws std::invalid_argument if `rhs.ok()`. If exceptions are disabled the
103  * program terminates via `google::cloud::Terminate()`
104  */
105  // NOLINTNEXTLINE(google-explicit-constructor)
106  StatusOr(Status rhs) : status_(std::move(rhs)) {
107  if (status_.ok()) {
108  google::cloud::internal::ThrowInvalidArgument(__func__);
109  }
110  }
111 
112  StatusOr(StatusOr&& rhs) : status_(std::move(rhs.status_)) {
113  if (status_.ok()) {
114  new (&value_) T(std::move(*rhs));
115  }
116  }
117 
118  StatusOr& operator=(StatusOr&& rhs) {
119  // There may be shorter ways to express this, but this is fairly readable,
120  // and should be reasonably efficient. Note that we must avoid destructing
121  // the destination and/or default initializing it unless really needed.
122  if (!ok()) {
123  if (!rhs.ok()) {
124  status_ = std::move(rhs.status_);
125  return *this;
126  }
127  new (&value_) T(std::move(*rhs));
128  status_ = Status();
129  return *this;
130  }
131  if (!rhs.ok()) {
132  value_.~T();
133  status_ = std::move(rhs.status_);
134  return *this;
135  }
136  **this = *std::move(rhs);
137  status_ = Status();
138  return *this;
139  }
140 
141  StatusOr(StatusOr const& rhs) : status_(rhs.status_) {
142  if (status_.ok()) {
143  new (&value_) T(*rhs);
144  }
145  }
146 
147  StatusOr& operator=(StatusOr const& rhs) {
148  // There may be shorter ways to express this, but this is fairly readable,
149  // and should be reasonably efficient. Note that we must avoid destructing
150  // the destination and/or default initializing it unless really needed.
151  if (!ok()) {
152  if (!rhs.ok()) {
153  status_ = rhs.status_;
154  return *this;
155  }
156  new (&value_) T(*rhs);
157  status_ = rhs.status_;
158  return *this;
159  }
160  if (!rhs.ok()) {
161  value_.~T();
162  status_ = rhs.status_;
163  return *this;
164  }
165  **this = *rhs;
166  status_ = rhs.status_;
167  return *this;
168  }
169 
170  ~StatusOr() {
171  if (ok()) {
172  value_.~T();
173  }
174  }
175 
176  /**
177  * Assign a `T` (or anything convertible to `T`) into the `StatusOr`.
178  */
179  // Disable this assignment if U==StatusOr<T>. Well, really if U is a
180  // cv-qualified version of StatusOr<T>, so we need to apply std::decay<> to
181  // it first.
182  template <typename U = T>
183  typename std::enable_if<
184  not std::is_same<StatusOr, typename std::decay<U>::type>::value,
185  StatusOr>::type&
186  operator=(U&& rhs) {
187  // There may be shorter ways to express this, but this is fairly readable,
188  // and should be reasonably efficient. Note that we must avoid destructing
189  // the destination and/or default initializing it unless really needed.
190  if (!ok()) {
191  new (&value_) T(std::forward<U>(rhs));
192  status_ = Status();
193  return *this;
194  }
195  **this = std::forward<U>(rhs);
196  status_ = Status();
197  return *this;
198  }
199 
200  /**
201  * Creates a new `StatusOr<T>` holding the value @p rhs.
202  *
203  * @par Post-conditions
204  * `ok() == true` and `value() == rhs`.
205  *
206  * @param rhs the value used to initialize the object.
207  *
208  * @throws only if `T`'s move constructor throws.
209  */
210  // NOLINTNEXTLINE(google-explicit-constructor)
211  StatusOr(T&& rhs) : status_() { new (&value_) T(std::move(rhs)); }
212 
213  // NOLINTNEXTLINE(google-explicit-constructor)
214  StatusOr(T const& rhs) : status_() { new (&value_) T(rhs); }
215 
216  bool ok() const { return status_.ok(); }
217  explicit operator bool() const { return status_.ok(); }
218 
219  //@{
220  /**
221  * @name Deference operators.
222  *
223  * @warning Using these operators when `ok() == false` results in undefined
224  * behavior.
225  *
226  * @return All these return a (properly ref and const-qualified) reference to
227  * the underlying value.
228  */
229  T& operator*() & { return value_; }
230 
231  T const& operator*() const& { return value_; }
232 
233  T&& operator*() && { return std::move(value_); }
234 
235 #if GOOGLE_CLOUD_CPP_HAVE_CONST_REF_REF
236  T const&& operator*() const&& { return std::move(value_); }
237 #endif // GOOGLE_CLOUD_CPP_HAVE_CONST_REF_REF
238  //@}
239 
240  //@{
241  /**
242  * @name Member access operators.
243  *
244  * @warning Using these operators when `ok() == false` results in undefined
245  * behavior.
246  *
247  * @return All these return a (properly ref and const-qualified) pointer to
248  * the underlying value.
249  */
250  T* operator->() & { return &value_; }
251 
252  T const* operator->() const& { return &value_; }
253  //@}
254 
255  //@{
256  /**
257  * @name Value accessors.
258  *
259  * @return All these member functions return a (properly ref and
260  * const-qualified) reference to the underlying value.
261  *
262  * @throws `RuntimeStatusError` with the contents of `status()` if the object
263  * does not contain a value, i.e., if `ok() == false`.
264  */
265  T& value() & {
266  CheckHasValue();
267  return **this;
268  }
269 
270  T const& value() const& {
271  CheckHasValue();
272  return **this;
273  }
274 
275  T&& value() && {
276  CheckHasValue();
277  return std::move(**this);
278  }
279 
280 #if GOOGLE_CLOUD_CPP_HAVE_CONST_REF_REF
281  T const&& value() const&& {
282  CheckHasValue();
283  return std::move(**this);
284  }
285 #endif // GOOGLE_CLOUD_CPP_HAVE_CONST_REF_REF
286  //@}
287 
288  //@{
289  /**
290  * @name Status accessors.
291  *
292  * @return All these member functions return the (properly ref and
293  * const-qualified) status. If the object contains a value then
294  * `status().ok() == true`.
295  */
296  Status& status() & { return status_; }
297  Status const& status() const& { return status_; }
298  Status&& status() && { return std::move(status_); }
299 #if GOOGLE_CLOUD_CPP_HAVE_CONST_REF_REF
300  Status const&& status() const&& { return std::move(status_); }
301 #endif // GOOGLE_CLOUD_CPP_HAVE_CONST_REF_REF
302  //@}
303 
304  private:
305  void CheckHasValue() const& {
306  if (!ok()) {
307  internal::ThrowStatus(status_);
308  }
309  }
310 
311  // When possible, do not copy the status.
312  void CheckHasValue() && {
313  if (!ok()) {
314  internal::ThrowStatus(std::move(status_));
315  }
316  }
317 
318  Status status_;
319  union {
320  T value_;
321  };
322 };
323 
324 template <typename T>
325 StatusOr<T> make_status_or(T rhs) {
326  return StatusOr<T>(std::move(rhs));
327 }
328 
329 } // namespace GOOGLE_CLOUD_CPP_NS
330 } // namespace cloud
331 } // namespace google
332 
333 #endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STATUS_OR_H_
StatusOr(StatusOr &&rhs)
Definition: status_or.h:112
T const & value() const &
Definition: status_or.h:270
StatusOr()
Initializes with an error status (UNKNOWN).
Definition: status_or.h:93
#define GOOGLE_CLOUD_CPP_NS
Definition: version.h:24
Reports error code and details from a remote request.
Definition: status.h:64
Contains all the Google Cloud C++ Library APIs.
Definition: iam_bindings.cc:21
StatusOr(T &&rhs)
Creates a new StatusOr<T> holding the value rhs.
Definition: status_or.h:211
StatusOr(Status rhs)
Creates a new StatusOr<T> holding the error condition rhs.
Definition: status_or.h:106
StatusOr< T > make_status_or(T rhs)
Definition: status_or.h:325
std::enable_if< not std::is_same< StatusOr, typename std::decay< U >::type >::value, StatusOr >::type & operator=(U &&rhs)
Assign a T (or anything convertible to T) into the StatusOr.
Definition: status_or.h:186
T const * operator->() const &
Definition: status_or.h:252
Status(StatusCode status_code, std::string message)
Definition: status.h:68
Status const & status() const &
Definition: status_or.h:297
StatusOr(StatusOr const &rhs)
Definition: status_or.h:141
StatusOr & operator=(StatusOr &&rhs)
Definition: status_or.h:118
StatusOr & operator=(StatusOr const &rhs)
Definition: status_or.h:147
T const & operator*() const &
Definition: status_or.h:231
bool ok() const
Definition: status.h:71
StatusCode
Well-known status codes with grpc::StatusCode-compatible values.
Definition: status.h:32