// Copyright 2022 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_TYPES_EXPECTED_INTERNAL_H_ #define BASE_TYPES_EXPECTED_INTERNAL_H_ // IWYU pragma: private, include "base/types/expected.h" #include #include #include "base/check.h" #include "base/template_util.h" #include "third_party/abseil-cpp/absl/types/variant.h" #include "third_party/abseil-cpp/absl/utility/utility.h" // This header defines type traits and aliases used for the implementation of // base::expected. namespace base { template class unexpected; template > class expected; namespace internal { template struct IsUnexpected : std::false_type {}; template struct IsUnexpected> : std::true_type {}; template struct IsConstructibleOrConvertible : std::disjunction, std::is_convertible> { }; template struct IsAnyConstructibleOrConvertible : std::disjunction, IsConstructibleOrConvertible, IsConstructibleOrConvertible, IsConstructibleOrConvertible> {}; // Checks whether a given expected can be converted into another // expected. Used inside expected's conversion constructors. UF and GF are // the forwarded versions of U and G, e.g. UF is const U& for the converting // copy constructor and U for the converting move constructor. Similarly for GF. // ExUG is used for convenience, and not expected to be passed explicitly. // See https://eel.is/c++draft/expected#lib:expected,constructor___ template , remove_cvref_t>> struct IsValidConversion : std::conjunction< std::is_constructible, std::is_constructible, std::negation>, std::negation, ExUG>>> { }; // Checks whether a given expected can be converted into another // expected when T is a void type. Used inside expected's conversion // constructors. GF is the forwarded versions of G, e.g. GF is const G& for the // converting copy constructor and G for the converting move constructor. ExUG // is used for convenience, and not expected to be passed explicitly. See // https://eel.is/c++draft/expected#lib:expected%3cvoid%3e,constructor___ template >> struct IsValidVoidConversion : std::conjunction< std::is_void, std::is_constructible, std::negation, ExUG>>> { }; template struct IsValidValueConstruction : std::conjunction< std::is_constructible, std::negation, absl::in_place_t>>, std::negation, expected>>, std::negation>>> {}; template struct AreValueAndErrorConvertible : std::conjunction, std::is_convertible> { }; template using EnableIfDefaultConstruction = std::enable_if_t, int>; template using EnableIfExplicitConversion = std::enable_if_t< std::conjunction_v< IsValidConversion, std::negation>>, int>; template using EnableIfImplicitConversion = std::enable_if_t< std::conjunction_v, AreValueAndErrorConvertible>, int>; template using EnableIfExplicitVoidConversion = std::enable_if_t< std::conjunction_v, std::negation>>, int>; template using EnableIfImplicitVoidConversion = std::enable_if_t, std::is_convertible>, int>; template using EnableIfUnexpectedValueConstruction = std::enable_if_t< std::conjunction_v< std::negation, unexpected>>, std::negation, absl::in_place_t>>, std::is_constructible>, int>; template using EnableIfExplicitValueConstruction = std::enable_if_t< std::conjunction_v, std::negation>>, int>; template using EnableIfImplicitValueConstruction = std::enable_if_t, std::is_convertible>, int>; template using EnableIfExplicitUnexpectedConstruction = std::enable_if_t< std::conjunction_v, std::negation>>, int>; template using EnableIfImplicitUnexpectedConstruction = std::enable_if_t< std::conjunction_v, std::is_convertible>, int>; template using EnableIfValueAssignment = std::enable_if_t< std::conjunction_v< std::negation, remove_cvref_t>>, std::negation>>, std::is_constructible, std::is_assignable>, int>; template using EnableIfNotVoid = std::enable_if_t>, int>; template class ExpectedImpl { public: static constexpr size_t kValIdx = 1; static constexpr size_t kErrIdx = 2; static constexpr absl::in_place_index_t<1> kValTag{}; static constexpr absl::in_place_index_t<2> kErrTag{}; template friend class ExpectedImpl; template = 0> constexpr ExpectedImpl() noexcept : data_(kValTag) {} constexpr ExpectedImpl(const ExpectedImpl& rhs) noexcept : data_(rhs.data_) { CHECK(!rhs.is_moved_from()); } constexpr ExpectedImpl(ExpectedImpl&& rhs) noexcept : data_(std::move(rhs.data_)) { CHECK(!rhs.is_moved_from()); rhs.set_is_moved_from(); } template constexpr explicit ExpectedImpl(const ExpectedImpl& rhs) noexcept { if (rhs.has_value()) { emplace_value(rhs.value()); } else { emplace_error(rhs.error()); } } template constexpr explicit ExpectedImpl(ExpectedImpl&& rhs) noexcept { if (rhs.has_value()) { emplace_value(std::move(rhs.value())); } else { emplace_error(std::move(rhs.error())); } rhs.set_is_moved_from(); } template constexpr explicit ExpectedImpl(decltype(kValTag), Args&&... args) noexcept : data_(kValTag, std::forward(args)...) {} template constexpr explicit ExpectedImpl(decltype(kValTag), std::initializer_list il, Args&&... args) noexcept : data_(kValTag, il, std::forward(args)...) {} template constexpr explicit ExpectedImpl(decltype(kErrTag), Args&&... args) noexcept : data_(kErrTag, std::forward(args)...) {} template constexpr explicit ExpectedImpl(decltype(kErrTag), std::initializer_list il, Args&&... args) noexcept : data_(kErrTag, il, std::forward(args)...) {} constexpr ExpectedImpl& operator=(const ExpectedImpl& rhs) noexcept { CHECK(!rhs.is_moved_from()); data_ = rhs.data_; return *this; } constexpr ExpectedImpl& operator=(ExpectedImpl&& rhs) noexcept { CHECK(!rhs.is_moved_from()); data_ = std::move(rhs.data_); rhs.set_is_moved_from(); return *this; } template constexpr T& emplace_value(Args&&... args) noexcept { return data_.template emplace(std::forward(args)...); } template constexpr T& emplace_value(std::initializer_list il, Args&&... args) noexcept { return data_.template emplace(il, std::forward(args)...); } template constexpr E& emplace_error(Args&&... args) noexcept { return data_.template emplace(std::forward(args)...); } template constexpr E& emplace_error(std::initializer_list il, Args&&... args) noexcept { return data_.template emplace(il, std::forward(args)...); } void swap(ExpectedImpl& rhs) noexcept { CHECK(!is_moved_from()); CHECK(!rhs.is_moved_from()); data_.swap(rhs.data_); } constexpr bool has_value() const noexcept { CHECK(!is_moved_from()); return data_.index() == kValIdx; } // Note: No `CHECK()` here and below, since absl::get already checks that // the passed in index is active. constexpr T& value() noexcept { return absl::get(data_); } constexpr const T& value() const noexcept { return absl::get(data_); } constexpr E& error() noexcept { return absl::get(data_); } constexpr const E& error() const noexcept { return absl::get(data_); } private: static constexpr size_t kNulIdx = 0; static_assert(kNulIdx != kValIdx); static_assert(kNulIdx != kErrIdx); constexpr bool is_moved_from() const noexcept { return data_.index() == kNulIdx; } constexpr void set_is_moved_from() noexcept { data_.template emplace(); } absl::variant data_; }; } // namespace internal } // namespace base #endif // BASE_TYPES_EXPECTED_INTERNAL_H_