Google Cloud C++ Client 2.10.1
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
35 * you 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.
38 *
39 * It is undefined behavior (UB) to dereference a `StatusOr<T>` that is not
40 * "ok". For example:
41 *
42 * @code
43 * StatusOr<Foo> foo = FetchFoo();
44 * if (!foo) { // Same as !foo.ok()
45 * // handle error and probably look at foo.status()
46 * } else {
47 * foo->DoSomethingFooey(); // UB if !foo
48 * }
49 * @endcode
50 *
51 * Alternatively, you may call the `StatusOr::value()` member function,
52 * which is defined to: (1) throw an exception if there is no `T` value, or (2)
53 * crash the program if exceptions are disabled. It is never UB to call
54 * `value()`.
55 *
56 * @code
57 * StatusOr<Foo> foo = FetchFoo();
58 * foo.value().DoSomethingFooey(); // May throw/crash if there is no value
59 * @endcode
60 *
61 * Functions that can fail will often return a `StatusOr<T>` instead of
62 * returning an error code and taking a `T` out-param, or rather than directly
63 * returning the `T` and throwing an exception on error. `StatusOr<T>` is used
64 * so that callers can choose whether they want to explicitly check for errors,
65 * crash the program, or throw exceptions.
66 *
67 * Since constructors do not have a return value, they should be designed in
68 * such a way that they cannot fail by moving the object's complex
69 * initialization logic into a separate factory function that itself can return
70 * a `StatusOr<T>`. For example:
71 *
72 * @code
73 * class Bar {
74 * public:
75 * Bar(Arg arg);
76 * ...
77 * };
78 * StatusOr<Bar> MakeBar() {
79 * ... complicated logic that might fail
80 * return Bar(std::move(arg));
81 * }
82 * @endcode
83 *
84 * `StatusOr<T>` supports equality comparisons if the underlying type `T` does.
85 *
86 * @tparam T the type of the value.
87 */
88template <typename T>
89class StatusOr final {
90 public:
91 static_assert(!std::is_reference<T>::value,
92 "StatusOr<T> requires T to **not** be a reference type");
93
94 /**
95 * A `value_type` member for use in generic programming.
96 *
97 * This is analogous to that of `std::optional::value_type`.
98 */
99 using value_type = T;
100
101 /**
102 * Initializes with an error status (`StatusCode::kUnknown`).
103 */
104 StatusOr() : StatusOr(MakeDefaultStatus()) {}
105
106 StatusOr(StatusOr const&) = default;
107 StatusOr& operator=(StatusOr const&) = default;
108 // NOLINTNEXTLINE(performance-noexcept-move-constructor)
109 StatusOr(StatusOr&& other)
110 : status_(std::move(other.status_)), value_(std::move(other.value_)) {
111 other.status_ = MakeDefaultStatus();
112 }
113 // NOLINTNEXTLINE(performance-noexcept-move-constructor)
114 StatusOr& operator=(StatusOr&& other) {
115 status_ = std::move(other.status_);
116 value_ = std::move(other.value_);
117 other.status_ = MakeDefaultStatus();
118 return *this;
119 }
120
121 /**
122 * Creates a new `StatusOr<T>` holding the error condition @p rhs.
123 *
124 * @par Post-conditions
125 * `ok() == false` and `status() == rhs`.
126 *
127 * @param rhs the status to initialize the object.
128 * @throws std::invalid_argument if `rhs.ok()`. If exceptions are disabled the
129 * program terminates via `google::cloud::Terminate()`
130 */
131 // NOLINTNEXTLINE(google-explicit-constructor)
132 StatusOr(Status rhs) : status_(std::move(rhs)) {
133 if (status_.ok()) {
134 google::cloud::internal::ThrowInvalidArgument(__func__);
135 }
136 }
137
138 /**
139 * Assigns the given non-OK Status to this `StatusOr<T>`.
140 *
141 * @throws std::invalid_argument if `status.ok()`. If exceptions are disabled
142 * the program terminates via `google::cloud::Terminate()`
143 */
144 StatusOr& operator=(Status status) {
145 *this = StatusOr(std::move(status));
146 return *this;
147 }
148
149 /**
150 * Assign a `T` (or anything convertible to `T`) into the `StatusOr`.
151 */
152 // Disable this assignment if U==StatusOr<T>. Well, really if U is a
153 // cv-qualified version of StatusOr<T>, so we need to apply std::decay<> to
154 // it first.
155 template <typename U = T>
156 typename std::enable_if< // NOLINT(misc-unconventional-assign-operator)
157 !std::is_same<StatusOr, typename std::decay<U>::type>::value,
158 StatusOr>::type&
159 operator=(U&& rhs) {
160 status_ = Status();
161 value_ = std::forward<U>(rhs);
162 return *this;
163 }
164
165 /**
166 * Creates a new `StatusOr<T>` holding the value @p rhs.
167 *
168 * @par Post-conditions
169 * `ok() == true` and `value() == rhs`.
170 *
171 * @param rhs the value used to initialize the object.
172 *
173 * @throws only if `T`'s move constructor throws.
174 */
175 // NOLINTNEXTLINE(google-explicit-constructor)
176 StatusOr(T&& rhs) : value_(std::move(rhs)) {}
177
178 // NOLINTNEXTLINE(google-explicit-constructor)
179 StatusOr(T const& rhs) : value_(rhs) {}
180
181 /// Returns `true` when `this` holds a value.
182 bool ok() const { return status_.ok(); }
183
184 /// Returns `true` when `this` holds a value.
185 explicit operator bool() const { return status_.ok(); }
186
187 ///@{
188 /**
189 * @name Deference operators.
190 *
191 * @warning Using these operators when `ok() == false` results in undefined
192 * behavior.
193 *
194 * @return All these return a (properly ref and const-qualified) reference to
195 * the underlying value.
196 */
197 T& operator*() & { return *value_; }
198
199 T const& operator*() const& { return *value_; }
200
201 T&& operator*() && { return *std::move(value_); }
202
203 T const&& operator*() const&& { return *std::move(value_); }
204 ///@}
205
206 ///@{
207 /**
208 * @name Member access operators.
209 *
210 * @warning Using these operators when `ok() == false` results in undefined
211 * behavior.
212 *
213 * @return All these return a (properly ref and const-qualified) pointer to
214 * the underlying value.
215 */
216 T* operator->() & { return &*value_; }
217
218 T const* operator->() const& { return &*value_; }
219 ///@}
220
221 ///@{
222 /**
223 * @name Value accessors.
224 *
225 * @return All these member functions return a (properly ref and
226 * const-qualified) reference to the underlying value.
227 *
228 * @throws `RuntimeStatusError` with the contents of `status()` if the object
229 * does not contain a value, i.e., if `ok() == false`.
230 */
231 T& value() & {
232 CheckHasValue();
233 return **this;
234 }
235
236 T const& value() const& {
237 CheckHasValue();
238 return **this;
239 }
240
241 T&& value() && {
242 CheckHasValue();
243 return std::move(**this);
244 }
245
246 T const&& value() const&& {
247 CheckHasValue();
248 return std::move(**this);
249 }
250 ///@}
251
252 ///@{
253 /**
254 * @name Status accessors.
255 *
256 * @return A reference to the contained `Status`.
257 */
258 Status const& status() const& { return status_; }
259 Status&& status() && { return std::move(status_); }
260 ///@}
261
262 private:
263 static Status MakeDefaultStatus() {
264 return Status{StatusCode::kUnknown, "default"};
265 }
266
267 void CheckHasValue() const& {
268 if (!ok()) {
269 internal::ThrowStatus(status_);
270 }
271 }
272
273 // When possible, do not copy the status.
274 void CheckHasValue() && {
275 if (!ok()) {
276 internal::ThrowStatus(std::move(status_));
277 }
278 }
279
280 Status status_;
281 absl::optional<T> value_;
282};
283
284// Returns true IFF both `StatusOr<T>` objects hold an equal `Status` or an
285// equal instance of `T`. This function requires that `T` supports equality.
286template <typename T>
287bool operator==(StatusOr<T> const& a, StatusOr<T> const& b) {
288 if (!a || !b) return a.status() == b.status();
289 return *a == *b;
290}
291
292// Returns true of `a` and `b` are not equal. See `operator==` docs above for
293// the definition of equal.
294template <typename T>
295bool operator!=(StatusOr<T> const& a, StatusOr<T> const& b) {
296 return !(a == b);
297}
298
299template <typename T>
300StatusOr<T> make_status_or(T rhs) {
301 return StatusOr<T>(std::move(rhs));
302}
303
305} // namespace cloud
306} // namespace google
307
308#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STATUS_OR_H
StatusOr(T const &rhs)
Definition: status_or.h:179
T && operator*() &&
Definition: status_or.h:201
T const * operator->() const &
Definition: status_or.h:218
T const && operator*() const &&
Definition: status_or.h:203
StatusOr(StatusOr &&other)
Definition: status_or.h:109
T const && value() const &&
Definition: status_or.h:246
Status && status() &&
Definition: status_or.h:259
StatusOr & operator=(Status status)
Assigns the given non-OK Status to this StatusOr<T>.
Definition: status_or.h:144
operator bool() const
Returns true when this holds a value.
Definition: status_or.h:185
Status const & status() const &
Definition: status_or.h:258
StatusOr(StatusOr const &)=default
T & operator*() &
Definition: status_or.h:197
T * operator->() &
Definition: status_or.h:216
StatusOr()
Initializes with an error status (StatusCode::kUnknown).
Definition: status_or.h:104
T const & operator*() const &
Definition: status_or.h:199
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:159
StatusOr(Status rhs)
Creates a new StatusOr<T> holding the error condition rhs.
Definition: status_or.h:132
T const & value() const &
Definition: status_or.h:236
StatusOr & operator=(StatusOr const &)=default
T && value() &&
Definition: status_or.h:241
StatusOr(T &&rhs)
Creates a new StatusOr<T> holding the value rhs.
Definition: status_or.h:176
StatusOr & operator=(StatusOr &&other)
Definition: status_or.h:114
T & value() &
Definition: status_or.h:231
bool ok() const
Returns true when this holds a value.
Definition: status_or.h:182
Represents success or an error with info about the error.
Definition: status.h:295
bool ok() const
Returns true if the status code is StatusCode::kOk.
Definition: status.h:328
Status & operator=(Status &&) noexcept
Status()
Default constructor, initializes to StatusCode::kOk.
Status(StatusCode code, std::string message, ErrorInfo info={})
Construct from a status code, message and (optional) error info.
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:300
StatusCode
Well-known status codes with grpc::StatusCode-compatible values.
Definition: status.h:35
@ kUnknown
kUnknown (gRPC code UNKNOWN) indicates an unknown error occurred.
bool operator==(StatusOr< T > const &a, StatusOr< T > const &b)
Definition: status_or.h:287
bool operator!=(StatusOr< T > const &a, StatusOr< T > const &b)
Definition: status_or.h:295
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