123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435 |
- // Copyright 2020 The Abseil Authors.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // https://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #ifndef ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
- #define ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
- #include <cstdint>
- #include <type_traits>
- #include <utility>
- #include "absl/base/attributes.h"
- #include "absl/base/nullability.h"
- #include "absl/meta/type_traits.h"
- #include "absl/status/status.h"
- #include "absl/strings/string_view.h"
- #include "absl/utility/utility.h"
- namespace absl {
- ABSL_NAMESPACE_BEGIN
- template <typename T>
- class ABSL_MUST_USE_RESULT StatusOr;
- namespace internal_statusor {
- // Detects whether `U` has conversion operator to `StatusOr<T>`, i.e. `operator
- // StatusOr<T>()`.
- template <typename T, typename U, typename = void>
- struct HasConversionOperatorToStatusOr : std::false_type {};
- template <typename T, typename U>
- void test(char (*)[sizeof(std::declval<U>().operator absl::StatusOr<T>())]);
- template <typename T, typename U>
- struct HasConversionOperatorToStatusOr<T, U, decltype(test<T, U>(0))>
- : std::true_type {};
- // Detects whether `T` is constructible or convertible from `StatusOr<U>`.
- template <typename T, typename U>
- using IsConstructibleOrConvertibleFromStatusOr =
- absl::disjunction<std::is_constructible<T, StatusOr<U>&>,
- std::is_constructible<T, const StatusOr<U>&>,
- std::is_constructible<T, StatusOr<U>&&>,
- std::is_constructible<T, const StatusOr<U>&&>,
- std::is_convertible<StatusOr<U>&, T>,
- std::is_convertible<const StatusOr<U>&, T>,
- std::is_convertible<StatusOr<U>&&, T>,
- std::is_convertible<const StatusOr<U>&&, T>>;
- // Detects whether `T` is constructible or convertible or assignable from
- // `StatusOr<U>`.
- template <typename T, typename U>
- using IsConstructibleOrConvertibleOrAssignableFromStatusOr =
- absl::disjunction<IsConstructibleOrConvertibleFromStatusOr<T, U>,
- std::is_assignable<T&, StatusOr<U>&>,
- std::is_assignable<T&, const StatusOr<U>&>,
- std::is_assignable<T&, StatusOr<U>&&>,
- std::is_assignable<T&, const StatusOr<U>&&>>;
- // Detects whether direct initializing `StatusOr<T>` from `U` is ambiguous, i.e.
- // when `U` is `StatusOr<V>` and `T` is constructible or convertible from `V`.
- template <typename T, typename U>
- struct IsDirectInitializationAmbiguous
- : public absl::conditional_t<
- std::is_same<absl::remove_cvref_t<U>, U>::value, std::false_type,
- IsDirectInitializationAmbiguous<T, absl::remove_cvref_t<U>>> {};
- template <typename T, typename V>
- struct IsDirectInitializationAmbiguous<T, absl::StatusOr<V>>
- : public IsConstructibleOrConvertibleFromStatusOr<T, V> {};
- // Checks against the constraints of the direction initialization, i.e. when
- // `StatusOr<T>::StatusOr(U&&)` should participate in overload resolution.
- template <typename T, typename U>
- using IsDirectInitializationValid = absl::disjunction<
- // Short circuits if T is basically U.
- std::is_same<T, absl::remove_cvref_t<U>>,
- absl::negation<absl::disjunction<
- std::is_same<absl::StatusOr<T>, absl::remove_cvref_t<U>>,
- std::is_same<absl::Status, absl::remove_cvref_t<U>>,
- std::is_same<absl::in_place_t, absl::remove_cvref_t<U>>,
- IsDirectInitializationAmbiguous<T, U>>>>;
- // This trait detects whether `StatusOr<T>::operator=(U&&)` is ambiguous, which
- // is equivalent to whether all the following conditions are met:
- // 1. `U` is `StatusOr<V>`.
- // 2. `T` is constructible and assignable from `V`.
- // 3. `T` is constructible and assignable from `U` (i.e. `StatusOr<V>`).
- // For example, the following code is considered ambiguous:
- // (`T` is `bool`, `U` is `StatusOr<bool>`, `V` is `bool`)
- // StatusOr<bool> s1 = true; // s1.ok() && s1.ValueOrDie() == true
- // StatusOr<bool> s2 = false; // s2.ok() && s2.ValueOrDie() == false
- // s1 = s2; // ambiguous, `s1 = s2.ValueOrDie()` or `s1 = bool(s2)`?
- template <typename T, typename U>
- struct IsForwardingAssignmentAmbiguous
- : public absl::conditional_t<
- std::is_same<absl::remove_cvref_t<U>, U>::value, std::false_type,
- IsForwardingAssignmentAmbiguous<T, absl::remove_cvref_t<U>>> {};
- template <typename T, typename U>
- struct IsForwardingAssignmentAmbiguous<T, absl::StatusOr<U>>
- : public IsConstructibleOrConvertibleOrAssignableFromStatusOr<T, U> {};
- // Checks against the constraints of the forwarding assignment, i.e. whether
- // `StatusOr<T>::operator(U&&)` should participate in overload resolution.
- template <typename T, typename U>
- using IsForwardingAssignmentValid = absl::disjunction<
- // Short circuits if T is basically U.
- std::is_same<T, absl::remove_cvref_t<U>>,
- absl::negation<absl::disjunction<
- std::is_same<absl::StatusOr<T>, absl::remove_cvref_t<U>>,
- std::is_same<absl::Status, absl::remove_cvref_t<U>>,
- std::is_same<absl::in_place_t, absl::remove_cvref_t<U>>,
- IsForwardingAssignmentAmbiguous<T, U>>>>;
- class Helper {
- public:
- // Move type-agnostic error handling to the .cc.
- static void HandleInvalidStatusCtorArg(absl::Nonnull<Status*>);
- ABSL_ATTRIBUTE_NORETURN static void Crash(const absl::Status& status);
- };
- // Construct an instance of T in `p` through placement new, passing Args... to
- // the constructor.
- // This abstraction is here mostly for the gcc performance fix.
- template <typename T, typename... Args>
- ABSL_ATTRIBUTE_NONNULL(1)
- void PlacementNew(absl::Nonnull<void*> p, Args&&... args) {
- new (p) T(std::forward<Args>(args)...);
- }
- // Helper base class to hold the data and all operations.
- // We move all this to a base class to allow mixing with the appropriate
- // TraitsBase specialization.
- template <typename T>
- class StatusOrData {
- template <typename U>
- friend class StatusOrData;
- public:
- StatusOrData() = delete;
- StatusOrData(const StatusOrData& other) {
- if (other.ok()) {
- MakeValue(other.data_);
- MakeStatus();
- } else {
- MakeStatus(other.status_);
- }
- }
- StatusOrData(StatusOrData&& other) noexcept {
- if (other.ok()) {
- MakeValue(std::move(other.data_));
- MakeStatus();
- } else {
- MakeStatus(std::move(other.status_));
- }
- }
- template <typename U>
- explicit StatusOrData(const StatusOrData<U>& other) {
- if (other.ok()) {
- MakeValue(other.data_);
- MakeStatus();
- } else {
- MakeStatus(other.status_);
- }
- }
- template <typename U>
- explicit StatusOrData(StatusOrData<U>&& other) {
- if (other.ok()) {
- MakeValue(std::move(other.data_));
- MakeStatus();
- } else {
- MakeStatus(std::move(other.status_));
- }
- }
- template <typename... Args>
- explicit StatusOrData(absl::in_place_t, Args&&... args)
- : data_(std::forward<Args>(args)...) {
- MakeStatus();
- }
- explicit StatusOrData(const T& value) : data_(value) {
- MakeStatus();
- }
- explicit StatusOrData(T&& value) : data_(std::move(value)) {
- MakeStatus();
- }
- template <typename U,
- absl::enable_if_t<std::is_constructible<absl::Status, U&&>::value,
- int> = 0>
- explicit StatusOrData(U&& v) : status_(std::forward<U>(v)) {
- EnsureNotOk();
- }
- StatusOrData& operator=(const StatusOrData& other) {
- if (this == &other) return *this;
- if (other.ok())
- Assign(other.data_);
- else
- AssignStatus(other.status_);
- return *this;
- }
- StatusOrData& operator=(StatusOrData&& other) {
- if (this == &other) return *this;
- if (other.ok())
- Assign(std::move(other.data_));
- else
- AssignStatus(std::move(other.status_));
- return *this;
- }
- ~StatusOrData() {
- if (ok()) {
- status_.~Status();
- data_.~T();
- } else {
- status_.~Status();
- }
- }
- template <typename U>
- void Assign(U&& value) {
- if (ok()) {
- data_ = std::forward<U>(value);
- } else {
- MakeValue(std::forward<U>(value));
- status_ = OkStatus();
- }
- }
- template <typename U>
- void AssignStatus(U&& v) {
- Clear();
- status_ = static_cast<absl::Status>(std::forward<U>(v));
- EnsureNotOk();
- }
- bool ok() const { return status_.ok(); }
- protected:
- // status_ will always be active after the constructor.
- // We make it a union to be able to initialize exactly how we need without
- // waste.
- // Eg. in the copy constructor we use the default constructor of Status in
- // the ok() path to avoid an extra Ref call.
- union {
- Status status_;
- };
- // data_ is active iff status_.ok()==true
- struct Dummy {};
- union {
- // When T is const, we need some non-const object we can cast to void* for
- // the placement new. dummy_ is that object.
- Dummy dummy_;
- T data_;
- };
- void Clear() {
- if (ok()) data_.~T();
- }
- void EnsureOk() const {
- if (ABSL_PREDICT_FALSE(!ok())) Helper::Crash(status_);
- }
- void EnsureNotOk() {
- if (ABSL_PREDICT_FALSE(ok())) Helper::HandleInvalidStatusCtorArg(&status_);
- }
- // Construct the value (ie. data_) through placement new with the passed
- // argument.
- template <typename... Arg>
- void MakeValue(Arg&&... arg) {
- internal_statusor::PlacementNew<T>(&dummy_, std::forward<Arg>(arg)...);
- }
- // Construct the status (ie. status_) through placement new with the passed
- // argument.
- template <typename... Args>
- void MakeStatus(Args&&... args) {
- internal_statusor::PlacementNew<Status>(&status_,
- std::forward<Args>(args)...);
- }
- };
- // Helper base classes to allow implicitly deleted constructors and assignment
- // operators in `StatusOr`. For example, `CopyCtorBase` will explicitly delete
- // the copy constructor when T is not copy constructible and `StatusOr` will
- // inherit that behavior implicitly.
- template <typename T, bool = std::is_copy_constructible<T>::value>
- struct CopyCtorBase {
- CopyCtorBase() = default;
- CopyCtorBase(const CopyCtorBase&) = default;
- CopyCtorBase(CopyCtorBase&&) = default;
- CopyCtorBase& operator=(const CopyCtorBase&) = default;
- CopyCtorBase& operator=(CopyCtorBase&&) = default;
- };
- template <typename T>
- struct CopyCtorBase<T, false> {
- CopyCtorBase() = default;
- CopyCtorBase(const CopyCtorBase&) = delete;
- CopyCtorBase(CopyCtorBase&&) = default;
- CopyCtorBase& operator=(const CopyCtorBase&) = default;
- CopyCtorBase& operator=(CopyCtorBase&&) = default;
- };
- template <typename T, bool = std::is_move_constructible<T>::value>
- struct MoveCtorBase {
- MoveCtorBase() = default;
- MoveCtorBase(const MoveCtorBase&) = default;
- MoveCtorBase(MoveCtorBase&&) = default;
- MoveCtorBase& operator=(const MoveCtorBase&) = default;
- MoveCtorBase& operator=(MoveCtorBase&&) = default;
- };
- template <typename T>
- struct MoveCtorBase<T, false> {
- MoveCtorBase() = default;
- MoveCtorBase(const MoveCtorBase&) = default;
- MoveCtorBase(MoveCtorBase&&) = delete;
- MoveCtorBase& operator=(const MoveCtorBase&) = default;
- MoveCtorBase& operator=(MoveCtorBase&&) = default;
- };
- template <typename T, bool = std::is_copy_constructible<T>::value&&
- std::is_copy_assignable<T>::value>
- struct CopyAssignBase {
- CopyAssignBase() = default;
- CopyAssignBase(const CopyAssignBase&) = default;
- CopyAssignBase(CopyAssignBase&&) = default;
- CopyAssignBase& operator=(const CopyAssignBase&) = default;
- CopyAssignBase& operator=(CopyAssignBase&&) = default;
- };
- template <typename T>
- struct CopyAssignBase<T, false> {
- CopyAssignBase() = default;
- CopyAssignBase(const CopyAssignBase&) = default;
- CopyAssignBase(CopyAssignBase&&) = default;
- CopyAssignBase& operator=(const CopyAssignBase&) = delete;
- CopyAssignBase& operator=(CopyAssignBase&&) = default;
- };
- template <typename T, bool = std::is_move_constructible<T>::value&&
- std::is_move_assignable<T>::value>
- struct MoveAssignBase {
- MoveAssignBase() = default;
- MoveAssignBase(const MoveAssignBase&) = default;
- MoveAssignBase(MoveAssignBase&&) = default;
- MoveAssignBase& operator=(const MoveAssignBase&) = default;
- MoveAssignBase& operator=(MoveAssignBase&&) = default;
- };
- template <typename T>
- struct MoveAssignBase<T, false> {
- MoveAssignBase() = default;
- MoveAssignBase(const MoveAssignBase&) = default;
- MoveAssignBase(MoveAssignBase&&) = default;
- MoveAssignBase& operator=(const MoveAssignBase&) = default;
- MoveAssignBase& operator=(MoveAssignBase&&) = delete;
- };
- ABSL_ATTRIBUTE_NORETURN void ThrowBadStatusOrAccess(absl::Status status);
- // Used to introduce jitter into the output of printing functions for
- // `StatusOr` (i.e. `AbslStringify` and `operator<<`).
- class StringifyRandom {
- enum BracesType {
- kBareParens = 0,
- kSpaceParens,
- kBareBrackets,
- kSpaceBrackets,
- };
- // Returns a random `BracesType` determined once per binary load.
- static BracesType RandomBraces() {
- static const BracesType kRandomBraces = static_cast<BracesType>(
- (reinterpret_cast<uintptr_t>(&kRandomBraces) >> 4) % 4);
- return kRandomBraces;
- }
- public:
- static inline absl::string_view OpenBrackets() {
- switch (RandomBraces()) {
- case kBareParens:
- return "(";
- case kSpaceParens:
- return "( ";
- case kBareBrackets:
- return "[";
- case kSpaceBrackets:
- return "[ ";
- }
- return "(";
- }
- static inline absl::string_view CloseBrackets() {
- switch (RandomBraces()) {
- case kBareParens:
- return ")";
- case kSpaceParens:
- return " )";
- case kBareBrackets:
- return "]";
- case kSpaceBrackets:
- return " ]";
- }
- return ")";
- }
- };
- } // namespace internal_statusor
- ABSL_NAMESPACE_END
- } // namespace absl
- #endif // ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
|