1 // Copyright 2020 The Abseil Authors. 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 #ifndef ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_ 15 #define ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_ 16 17 #include <type_traits> 18 #include <utility> 19 20 #include "absl/base/attributes.h" 21 #include "absl/meta/type_traits.h" 22 #include "absl/status/status.h" 23 #include "absl/utility/utility.h" 24 25 namespace absl { 26 ABSL_NAMESPACE_BEGIN 27 28 template <typename T> 29 class ABSL_MUST_USE_RESULT StatusOr; 30 31 namespace internal_statusor { 32 33 // Detects whether `U` has conversion operator to `StatusOr<T>`, i.e. `operator 34 // StatusOr<T>()`. 35 template <typename T, typename U, typename = void> 36 struct HasConversionOperatorToStatusOr : std::false_type {}; 37 38 template <typename T, typename U> 39 void test(char (*)[sizeof(std::declval<U>().operator absl::StatusOr<T>())]); 40 41 template <typename T, typename U> 42 struct HasConversionOperatorToStatusOr<T, U, decltype(test<T, U>(0))> 43 : std::true_type {}; 44 45 // Detects whether `T` is constructible or convertible from `StatusOr<U>`. 46 template <typename T, typename U> 47 using IsConstructibleOrConvertibleFromStatusOr = 48 absl::disjunction<std::is_constructible<T, StatusOr<U>&>, 49 std::is_constructible<T, const StatusOr<U>&>, 50 std::is_constructible<T, StatusOr<U>&&>, 51 std::is_constructible<T, const StatusOr<U>&&>, 52 std::is_convertible<StatusOr<U>&, T>, 53 std::is_convertible<const StatusOr<U>&, T>, 54 std::is_convertible<StatusOr<U>&&, T>, 55 std::is_convertible<const StatusOr<U>&&, T>>; 56 57 // Detects whether `T` is constructible or convertible or assignable from 58 // `StatusOr<U>`. 59 template <typename T, typename U> 60 using IsConstructibleOrConvertibleOrAssignableFromStatusOr = 61 absl::disjunction<IsConstructibleOrConvertibleFromStatusOr<T, U>, 62 std::is_assignable<T&, StatusOr<U>&>, 63 std::is_assignable<T&, const StatusOr<U>&>, 64 std::is_assignable<T&, StatusOr<U>&&>, 65 std::is_assignable<T&, const StatusOr<U>&&>>; 66 67 // Detects whether direct initializing `StatusOr<T>` from `U` is ambiguous, i.e. 68 // when `U` is `StatusOr<V>` and `T` is constructible or convertible from `V`. 69 template <typename T, typename U> 70 struct IsDirectInitializationAmbiguous 71 : public absl::conditional_t< 72 std::is_same<absl::remove_cvref_t<U>, U>::value, std::false_type, 73 IsDirectInitializationAmbiguous<T, absl::remove_cvref_t<U>>> {}; 74 75 template <typename T, typename V> 76 struct IsDirectInitializationAmbiguous<T, absl::StatusOr<V>> 77 : public IsConstructibleOrConvertibleFromStatusOr<T, V> {}; 78 79 // Checks against the constraints of the direction initialization, i.e. when 80 // `StatusOr<T>::StatusOr(U&&)` should participate in overload resolution. 81 template <typename T, typename U> 82 using IsDirectInitializationValid = absl::disjunction< 83 // Short circuits if T is basically U. 84 std::is_same<T, absl::remove_cvref_t<U>>, 85 absl::negation<absl::disjunction< 86 std::is_same<absl::StatusOr<T>, absl::remove_cvref_t<U>>, 87 std::is_same<absl::Status, absl::remove_cvref_t<U>>, 88 std::is_same<absl::in_place_t, absl::remove_cvref_t<U>>, 89 IsDirectInitializationAmbiguous<T, U>>>>; 90 91 // This trait detects whether `StatusOr<T>::operator=(U&&)` is ambiguous, which 92 // is equivalent to whether all the following conditions are met: 93 // 1. `U` is `StatusOr<V>`. 94 // 2. `T` is constructible and assignable from `V`. 95 // 3. `T` is constructible and assignable from `U` (i.e. `StatusOr<V>`). 96 // For example, the following code is considered ambiguous: 97 // (`T` is `bool`, `U` is `StatusOr<bool>`, `V` is `bool`) 98 // StatusOr<bool> s1 = true; // s1.ok() && s1.ValueOrDie() == true 99 // StatusOr<bool> s2 = false; // s2.ok() && s2.ValueOrDie() == false 100 // s1 = s2; // ambiguous, `s1 = s2.ValueOrDie()` or `s1 = bool(s2)`? 101 template <typename T, typename U> 102 struct IsForwardingAssignmentAmbiguous 103 : public absl::conditional_t< 104 std::is_same<absl::remove_cvref_t<U>, U>::value, std::false_type, 105 IsForwardingAssignmentAmbiguous<T, absl::remove_cvref_t<U>>> {}; 106 107 template <typename T, typename U> 108 struct IsForwardingAssignmentAmbiguous<T, absl::StatusOr<U>> 109 : public IsConstructibleOrConvertibleOrAssignableFromStatusOr<T, U> {}; 110 111 // Checks against the constraints of the forwarding assignment, i.e. whether 112 // `StatusOr<T>::operator(U&&)` should participate in overload resolution. 113 template <typename T, typename U> 114 using IsForwardingAssignmentValid = absl::disjunction< 115 // Short circuits if T is basically U. 116 std::is_same<T, absl::remove_cvref_t<U>>, 117 absl::negation<absl::disjunction< 118 std::is_same<absl::StatusOr<T>, absl::remove_cvref_t<U>>, 119 std::is_same<absl::Status, absl::remove_cvref_t<U>>, 120 std::is_same<absl::in_place_t, absl::remove_cvref_t<U>>, 121 IsForwardingAssignmentAmbiguous<T, U>>>>; 122 123 class Helper { 124 public: 125 // Move type-agnostic error handling to the .cc. 126 static void HandleInvalidStatusCtorArg(Status*); 127 ABSL_ATTRIBUTE_NORETURN static void Crash(const absl::Status& status); 128 }; 129 130 // Construct an instance of T in `p` through placement new, passing Args... to 131 // the constructor. 132 // This abstraction is here mostly for the gcc performance fix. 133 template <typename T, typename... Args> 134 ABSL_ATTRIBUTE_NONNULL(1) void PlacementNew(void* p, Args&&... args) { 135 new (p) T(std::forward<Args>(args)...); 136 } 137 138 // Helper base class to hold the data and all operations. 139 // We move all this to a base class to allow mixing with the appropriate 140 // TraitsBase specialization. 141 template <typename T> 142 class StatusOrData { 143 template <typename U> 144 friend class StatusOrData; 145 146 public: 147 StatusOrData() = delete; 148 149 StatusOrData(const StatusOrData& other) { 150 if (other.ok()) { 151 MakeValue(other.data_); 152 MakeStatus(); 153 } else { 154 MakeStatus(other.status_); 155 } 156 } 157 158 StatusOrData(StatusOrData&& other) noexcept { 159 if (other.ok()) { 160 MakeValue(std::move(other.data_)); 161 MakeStatus(); 162 } else { 163 MakeStatus(std::move(other.status_)); 164 } 165 } 166 167 template <typename U> 168 explicit StatusOrData(const StatusOrData<U>& other) { 169 if (other.ok()) { 170 MakeValue(other.data_); 171 MakeStatus(); 172 } else { 173 MakeStatus(other.status_); 174 } 175 } 176 177 template <typename U> 178 explicit StatusOrData(StatusOrData<U>&& other) { 179 if (other.ok()) { 180 MakeValue(std::move(other.data_)); 181 MakeStatus(); 182 } else { 183 MakeStatus(std::move(other.status_)); 184 } 185 } 186 187 template <typename... Args> 188 explicit StatusOrData(absl::in_place_t, Args&&... args) 189 : data_(std::forward<Args>(args)...) { 190 MakeStatus(); 191 } 192 193 explicit StatusOrData(const T& value) : data_(value) { 194 MakeStatus(); 195 } 196 explicit StatusOrData(T&& value) : data_(std::move(value)) { 197 MakeStatus(); 198 } 199 200 template <typename U, 201 absl::enable_if_t<std::is_constructible<absl::Status, U&&>::value, 202 int> = 0> 203 explicit StatusOrData(U&& v) : status_(std::forward<U>(v)) { 204 EnsureNotOk(); 205 } 206 207 StatusOrData& operator=(const StatusOrData& other) { 208 if (this == &other) return *this; 209 if (other.ok()) 210 Assign(other.data_); 211 else 212 AssignStatus(other.status_); 213 return *this; 214 } 215 216 StatusOrData& operator=(StatusOrData&& other) { 217 if (this == &other) return *this; 218 if (other.ok()) 219 Assign(std::move(other.data_)); 220 else 221 AssignStatus(std::move(other.status_)); 222 return *this; 223 } 224 225 ~StatusOrData() { 226 if (ok()) { 227 status_.~Status(); 228 data_.~T(); 229 } else { 230 status_.~Status(); 231 } 232 } 233 234 template <typename U> 235 void Assign(U&& value) { 236 if (ok()) { 237 data_ = std::forward<U>(value); 238 } else { 239 MakeValue(std::forward<U>(value)); 240 status_ = OkStatus(); 241 } 242 } 243 244 template <typename U> 245 void AssignStatus(U&& v) { 246 Clear(); 247 status_ = static_cast<absl::Status>(std::forward<U>(v)); 248 EnsureNotOk(); 249 } 250 251 bool ok() const { return status_.ok(); } 252 253 protected: 254 // status_ will always be active after the constructor. 255 // We make it a union to be able to initialize exactly how we need without 256 // waste. 257 // Eg. in the copy constructor we use the default constructor of Status in 258 // the ok() path to avoid an extra Ref call. 259 union { 260 Status status_; 261 }; 262 263 // data_ is active iff status_.ok()==true 264 struct Dummy {}; 265 union { 266 // When T is const, we need some non-const object we can cast to void* for 267 // the placement new. dummy_ is that object. 268 Dummy dummy_; 269 T data_; 270 }; 271 272 void Clear() { 273 if (ok()) data_.~T(); 274 } 275 276 void EnsureOk() const { 277 if (ABSL_PREDICT_FALSE(!ok())) Helper::Crash(status_); 278 } 279 280 void EnsureNotOk() { 281 if (ABSL_PREDICT_FALSE(ok())) Helper::HandleInvalidStatusCtorArg(&status_); 282 } 283 284 // Construct the value (ie. data_) through placement new with the passed 285 // argument. 286 template <typename... Arg> 287 void MakeValue(Arg&&... arg) { 288 internal_statusor::PlacementNew<T>(&dummy_, std::forward<Arg>(arg)...); 289 } 290 291 // Construct the status (ie. status_) through placement new with the passed 292 // argument. 293 template <typename... Args> 294 void MakeStatus(Args&&... args) { 295 internal_statusor::PlacementNew<Status>(&status_, 296 std::forward<Args>(args)...); 297 } 298 }; 299 300 // Helper base classes to allow implicitly deleted constructors and assignment 301 // operators in `StatusOr`. For example, `CopyCtorBase` will explicitly delete 302 // the copy constructor when T is not copy constructible and `StatusOr` will 303 // inherit that behavior implicitly. 304 template <typename T, bool = std::is_copy_constructible<T>::value> 305 struct CopyCtorBase { 306 CopyCtorBase() = default; 307 CopyCtorBase(const CopyCtorBase&) = default; 308 CopyCtorBase(CopyCtorBase&&) = default; 309 CopyCtorBase& operator=(const CopyCtorBase&) = default; 310 CopyCtorBase& operator=(CopyCtorBase&&) = default; 311 }; 312 313 template <typename T> 314 struct CopyCtorBase<T, false> { 315 CopyCtorBase() = default; 316 CopyCtorBase(const CopyCtorBase&) = delete; 317 CopyCtorBase(CopyCtorBase&&) = default; 318 CopyCtorBase& operator=(const CopyCtorBase&) = default; 319 CopyCtorBase& operator=(CopyCtorBase&&) = default; 320 }; 321 322 template <typename T, bool = std::is_move_constructible<T>::value> 323 struct MoveCtorBase { 324 MoveCtorBase() = default; 325 MoveCtorBase(const MoveCtorBase&) = default; 326 MoveCtorBase(MoveCtorBase&&) = default; 327 MoveCtorBase& operator=(const MoveCtorBase&) = default; 328 MoveCtorBase& operator=(MoveCtorBase&&) = default; 329 }; 330 331 template <typename T> 332 struct MoveCtorBase<T, false> { 333 MoveCtorBase() = default; 334 MoveCtorBase(const MoveCtorBase&) = default; 335 MoveCtorBase(MoveCtorBase&&) = delete; 336 MoveCtorBase& operator=(const MoveCtorBase&) = default; 337 MoveCtorBase& operator=(MoveCtorBase&&) = default; 338 }; 339 340 template <typename T, bool = std::is_copy_constructible<T>::value&& 341 std::is_copy_assignable<T>::value> 342 struct CopyAssignBase { 343 CopyAssignBase() = default; 344 CopyAssignBase(const CopyAssignBase&) = default; 345 CopyAssignBase(CopyAssignBase&&) = default; 346 CopyAssignBase& operator=(const CopyAssignBase&) = default; 347 CopyAssignBase& operator=(CopyAssignBase&&) = default; 348 }; 349 350 template <typename T> 351 struct CopyAssignBase<T, false> { 352 CopyAssignBase() = default; 353 CopyAssignBase(const CopyAssignBase&) = default; 354 CopyAssignBase(CopyAssignBase&&) = default; 355 CopyAssignBase& operator=(const CopyAssignBase&) = delete; 356 CopyAssignBase& operator=(CopyAssignBase&&) = default; 357 }; 358 359 template <typename T, bool = std::is_move_constructible<T>::value&& 360 std::is_move_assignable<T>::value> 361 struct MoveAssignBase { 362 MoveAssignBase() = default; 363 MoveAssignBase(const MoveAssignBase&) = default; 364 MoveAssignBase(MoveAssignBase&&) = default; 365 MoveAssignBase& operator=(const MoveAssignBase&) = default; 366 MoveAssignBase& operator=(MoveAssignBase&&) = default; 367 }; 368 369 template <typename T> 370 struct MoveAssignBase<T, false> { 371 MoveAssignBase() = default; 372 MoveAssignBase(const MoveAssignBase&) = default; 373 MoveAssignBase(MoveAssignBase&&) = default; 374 MoveAssignBase& operator=(const MoveAssignBase&) = default; 375 MoveAssignBase& operator=(MoveAssignBase&&) = delete; 376 }; 377 378 ABSL_ATTRIBUTE_NORETURN void ThrowBadStatusOrAccess(absl::Status status); 379 380 } // namespace internal_statusor 381 ABSL_NAMESPACE_END 382 } // namespace absl 383 384 #endif // ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_ 385