Google Cloud C++ Client 2.8.0
C++ Client Library for Google Cloud Platform
Loading...
Searching...
No Matches
status_or.h
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
25namespace google {
26namespace 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 */
88template <typename T>
89class 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.
280template <typename T>
281bool 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.
288template <typename T>
289bool operator!=(StatusOr<T> const& a, StatusOr<T> const& b) {
290 return !(a == b);
291}
292
293template <typename T>
294StatusOr<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
StatusOr(T const &rhs)
Definition: status_or.h:176
T && operator*() &&
Definition: status_or.h:195
T const * operator->() const &
Definition: status_or.h:212
T const && operator*() const &&
Definition: status_or.h:197
StatusOr(StatusOr &&other)
Definition: status_or.h:106
T const && value() const &&
Definition: status_or.h:240
Status && status() &&
Definition: status_or.h:253
StatusOr & operator=(Status status)
Assigns the given non-OK Status to this StatusOr<T>.
Definition: status_or.h:141
operator bool() const
Definition: status_or.h:179
Status const & status() const &
Definition: status_or.h:252
StatusOr(StatusOr const &)=default
T & operator*() &
Definition: status_or.h:191
T * operator->() &
Definition: status_or.h:210
StatusOr()
Initializes with an error status (UNKNOWN).
Definition: status_or.h:101
T const & operator*() const &
Definition: status_or.h:193
std::enable_if<!std::is_same< StatusOr, typenamestd::decay< U >::type >::value, StatusOr >::type & operator=(U &&rhs)
Assign a T (or anything convertible to T) into the StatusOr.
Definition: status_or.h:156
StatusOr(Status rhs)
Creates a new StatusOr<T> holding the error condition rhs.
Definition: status_or.h:129
T const & value() const &
Definition: status_or.h:230
StatusOr & operator=(StatusOr const &)=default
T && value() &&
Definition: status_or.h:235
StatusOr(T &&rhs)
Creates a new StatusOr<T> holding the value rhs.
Definition: status_or.h:173
StatusOr & operator=(StatusOr &&other)
Definition: status_or.h:111
T & value() &
Definition: status_or.h:225
bool ok() const
Definition: status_or.h:178
Represents success or an error with info about the error.
Definition: status.h:111
bool ok() const
Definition: status.h:127
Status & operator=(Status &&) noexcept
Status(StatusCode code, std::string message, ErrorInfo info={})
Constructs a Status with the given code and message.
Status(Status &&) noexcept
Contains all the Google Cloud C++ Library APIs.
Definition: async_operation.h:23
StatusOr< T > make_status_or(T rhs)
Definition: status_or.h:294
StatusCode
Well-known status codes with grpc::StatusCode-compatible values.
Definition: status.h:35
bool operator==(StatusOr< T > const &a, StatusOr< T > const &b)
Definition: status_or.h:281
bool operator!=(StatusOr< T > const &a, StatusOr< T > const &b)
Definition: status_or.h:289
Definition: async_operation.h:22
#define GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
Definition: version.h:45
#define GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
Definition: version.h:43