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