|
| 1 | +// Copyright 2019 Google Inc. All rights reserved |
| 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_GAX_STATUS_OR_H_ |
| 16 | +#define GOOGLE_GAX_STATUS_OR_H_ |
| 17 | + |
| 18 | +#include <cstdlib> |
| 19 | +#include <iostream> |
| 20 | +#include <memory> |
| 21 | + |
| 22 | +#include "status.h" |
| 23 | + |
| 24 | +namespace google { |
| 25 | +namespace gax { |
| 26 | + |
| 27 | +/** |
| 28 | + * Holds a value or a `Status` indicating why there is no value. |
| 29 | + * |
| 30 | + * `StatusOr<T>` represents either a usable `T` value or a `Status` object |
| 31 | + * explaining why a `T` value is not present. Typical usage of `StatusOr<T>` |
| 32 | + * looks like usage of a smart pointer, or even a std::optional<T>, in that you |
| 33 | + * first check its validity using a conversion to bool (or by calling |
| 34 | + * `StatusOr::ok()`), then you may dereference the object to access the |
| 35 | + * contained value. It is undefined behavior (UB) to dereference a |
| 36 | + * `StatusOr<T>` that is not "ok". For example: |
| 37 | + * |
| 38 | + * @code |
| 39 | + * StatusOr<Foo> foo = FetchFoo(); |
| 40 | + * if (!foo) { // Same as !foo.ok() |
| 41 | + * // handle error and probably look at foo.status() |
| 42 | + * } else { |
| 43 | + * foo->DoSomethingFooey(); // UB if !foo |
| 44 | + * } |
| 45 | + * @endcode |
| 46 | + * |
| 47 | + * Alternatively, you may call the `StatusOr::value()` member function, |
| 48 | + * which will invoke `std::abort()` if `!StatusOr::ok()`. |
| 49 | + * |
| 50 | + * @code |
| 51 | + * StatusOr<Foo> foo = FetchFoo(); |
| 52 | + * foo.value().DoSomethingFooey(); // May throw/crash if there is no value |
| 53 | + * @endcode |
| 54 | + * |
| 55 | + * Functions that can fail will often return a `StatusOr<T>` instead of |
| 56 | + * returning an error code and taking a `T` out-param, or rather than directly |
| 57 | + * returning the `T` and throwing an exception on error. StatusOr is used so |
| 58 | + * that callers can choose whether they want to explicitly check for errors, |
| 59 | + * crash the program, or throw exceptions. Since constructors do not have a |
| 60 | + * return value, they should be designed in such a way that they cannot fail by |
| 61 | + * moving the object's complex initialization logic into a separate factory |
| 62 | + * function that itself can return a `StatusOr<T>`. For example: |
| 63 | + * |
| 64 | + * @code |
| 65 | + * class Bar { |
| 66 | + * public: |
| 67 | + * Bar(Arg arg); |
| 68 | + * ... |
| 69 | + * }; |
| 70 | + * StatusOr<Bar> MakeBar() { |
| 71 | + * ... complicated logic that might fail |
| 72 | + * return Bar(std::move(arg)); |
| 73 | + * } |
| 74 | + * @endcode |
| 75 | + * |
| 76 | + * TODO(...) - the current implementation is fairly naive with respect to `T`, |
| 77 | + * it is unlikely to work correctly for reference types, arrays, and so forth. |
| 78 | + * |
| 79 | + * @tparam T the type of the value. |
| 80 | + */ |
| 81 | +template<typename T> |
| 82 | +class StatusOr final { |
| 83 | + public: |
| 84 | + /** |
| 85 | + * StatusOr is not default constructible. |
| 86 | + * |
| 87 | + * There is no good definition of a default StatusOr: |
| 88 | + * it can't default construct T because T may not have a default constructor, |
| 89 | + * and using an unknown error code and generic message is not helpful. |
| 90 | + */ |
| 91 | + StatusOr() = delete; |
| 92 | + |
| 93 | + /** |
| 94 | + * Creates a new `StatusOr<T>` holding the error condition @p rhs. |
| 95 | + * Creating a StatusOr from an OK status is not permitted |
| 96 | + * and invokes `std::abort()`. |
| 97 | + * |
| 98 | + * @par Post-conditions |
| 99 | + * `ok() == false` and `status() == rhs`. |
| 100 | + * |
| 101 | + * @param rhs the status to initialize the object. |
| 102 | + */ |
| 103 | + StatusOr(Status rhs) : status_(std::move(rhs)) { |
| 104 | + if(status_.IsOk()){ |
| 105 | + std::cerr << "Constructing StatusOr<T> from OK status is not allowed" << std::endl; |
| 106 | + std::abort(); |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | + /** |
| 111 | + * Creates a new `StatusOr<T>` holding the value @p rhs. |
| 112 | + * |
| 113 | + * @par Post-conditions |
| 114 | + * `ok() == true` and `value() == rhs`. |
| 115 | + * |
| 116 | + * @param rhs the value used to initialize the object. |
| 117 | + */ |
| 118 | + StatusOr(T const& rhs) : status_() { |
| 119 | + new (&value_) T(rhs); |
| 120 | + } |
| 121 | + |
| 122 | + StatusOr(T&& rhs) : status_() { |
| 123 | + new (&value_) T(std::move(rhs)); |
| 124 | + } |
| 125 | + |
| 126 | + StatusOr(StatusOr const& rhs) : status_(rhs.status_) { |
| 127 | + if(ok()) { |
| 128 | + new (&value_) T(rhs.value_); |
| 129 | + } |
| 130 | + } |
| 131 | + |
| 132 | + StatusOr(StatusOr&& rhs) : status_(std::move(rhs.status_)) { |
| 133 | + if(ok()) { |
| 134 | + new (&value_) T(std::move(rhs.value_)); |
| 135 | + } |
| 136 | + } |
| 137 | + |
| 138 | + ~StatusOr() { |
| 139 | + if(ok()) { |
| 140 | + value_.~T(); |
| 141 | + } |
| 142 | + } |
| 143 | + |
| 144 | + /** |
| 145 | + * @name Status accessors. |
| 146 | + * |
| 147 | + * @return All these member functions return the (properly ref and |
| 148 | + * const-qualified) status. Iff the object contains a value then |
| 149 | + * `status().ok() == true`. |
| 150 | + */ |
| 151 | + Status const& status() const { return status_; } |
| 152 | + |
| 153 | + inline bool ok() const { return status_.IsOk(); } |
| 154 | + explicit inline operator bool() const { return ok(); } |
| 155 | + |
| 156 | + /** |
| 157 | + * @name Deference operators. |
| 158 | + * |
| 159 | + * @warning Using these operators when `ok() == false` results in undefined |
| 160 | + * behavior. |
| 161 | + * |
| 162 | + * @return All these return a (properly ref and const-qualified) reference to |
| 163 | + * the underlying value. |
| 164 | + */ |
| 165 | + T& operator*() & { return value_; } |
| 166 | + T const& operator*() const& { return value_; } |
| 167 | + T&& operator*() && { return std::move(value_); } |
| 168 | + |
| 169 | + /** |
| 170 | + * @name Member access operators. |
| 171 | + * |
| 172 | + * @warning Using these operators when `ok() == false` results in undefined |
| 173 | + * behavior. |
| 174 | + * |
| 175 | + * @return All these return a (properly ref and const-qualified) pointer to |
| 176 | + * the underlying value. |
| 177 | + */ |
| 178 | + T* operator->() & { return &value_; } |
| 179 | + T const* operator->() const& { return &value_; } |
| 180 | + |
| 181 | + /** |
| 182 | + * @name Value accessors. |
| 183 | + * |
| 184 | + * @return All these member functions return a (properly ref and |
| 185 | + * const-qualified) reference to the underlying value. |
| 186 | + * |
| 187 | + * Aborts the program if `!ok()` |
| 188 | + */ |
| 189 | + T& value() & { |
| 190 | + check_value(); |
| 191 | + return **this; |
| 192 | + } |
| 193 | + |
| 194 | + T const& value() const& { |
| 195 | + check_value(); |
| 196 | + return **this; |
| 197 | + } |
| 198 | + |
| 199 | + T&& value() && { |
| 200 | + check_value(); |
| 201 | + return std::move(**this); |
| 202 | + } |
| 203 | + |
| 204 | + private: |
| 205 | + void check_value() const { |
| 206 | + if(!ok()) { |
| 207 | + std::cerr << status_ << std::endl; |
| 208 | + std::abort(); |
| 209 | + } |
| 210 | + } |
| 211 | + |
| 212 | + Status const status_; |
| 213 | + union{ |
| 214 | + T value_; |
| 215 | + }; |
| 216 | +}; |
| 217 | + |
| 218 | +} // namespace gax |
| 219 | +} // namespace google |
| 220 | + |
| 221 | +#endif // GOOGLE_GAX_STATUS_OR_H_ |
0 commit comments