/* * Copyright (C) 2019 The Android Open Source Project * * 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 * * http://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. */ #pragma once #include #include #include #include #include // android::base::expected is a partial implementation of C++23's std::expected // for Android. // // Usage: // using android::base::expected; // using android::base::unexpected; // // expected safe_divide(double i, double j) { // if (j == 0) return unexpected("divide by zero"); // else return i / j; // } // // void test() { // auto q = safe_divide(10, 0); // if (q.ok()) { printf("%f\n", q.value()); } // else { printf("%s\n", q.error().c_str()); } // } // // Once the Android platform has moved to C++23, this will be removed and // android::base::expected will be type aliased to std::expected. // namespace android { namespace base { // Synopsis template class expected; template class unexpected; template unexpected(E) -> unexpected; template class bad_expected_access; template <> class bad_expected_access; struct unexpect_t { explicit unexpect_t() = default; }; inline constexpr unexpect_t unexpect{}; // macros for SFINAE #define _ENABLE_IF(...) \ , std::enable_if_t<(__VA_ARGS__)>* = nullptr // Define NODISCARD_EXPECTED to prevent expected from being // ignored when used as a return value. This is off by default. #ifdef NODISCARD_EXPECTED #define _NODISCARD_ [[nodiscard]] #else #define _NODISCARD_ #endif #define _EXPLICIT(cond) \ _Pragma("clang diagnostic push") \ _Pragma("clang diagnostic ignored \"-Wc++20-extensions\"") explicit(cond) \ _Pragma("clang diagnostic pop") #define _COMMA , namespace expected_internal { template struct remove_cvref { using type = std::remove_cv_t>; }; template using remove_cvref_t = typename remove_cvref::type; // Can T be constructed from W (or W converted to T)? W can be lvalue or rvalue, // const or not. template inline constexpr bool converts_from_any_cvref = std::disjunction_v, std::is_convertible, std::is_constructible, std::is_convertible, std::is_constructible, std::is_convertible, std::is_constructible, std::is_convertible>; template struct is_expected : std::false_type {}; template struct is_expected> : std::true_type {}; template inline constexpr bool is_expected_v = is_expected::value; template struct is_unexpected : std::false_type {}; template struct is_unexpected> : std::true_type {}; template inline constexpr bool is_unexpected_v = is_unexpected::value; // Constraints on constructing an expected from an expected // related to T and U. UF is either "const U&" or "U". template inline constexpr bool convert_value_constraints = std::is_constructible_v && (std::is_same_v, bool> || !converts_from_any_cvref>); // Constraints on constructing an expected<..., E> from an expected // related to E, G, and expected. GF is either "const G&" or "G". template inline constexpr bool convert_error_constraints = std::is_constructible_v && !std::is_constructible_v, expected&> && !std::is_constructible_v, expected> && !std::is_constructible_v, const expected&> && !std::is_constructible_v, const expected>; // If an exception is thrown in expected::operator=, while changing the expected // object between a value and an error, the expected object is supposed to // retain its original value, which is only possible if certain constructors // are noexcept. This implementation doesn't try to be exception-safe, but // enforce these constraints anyway because std::expected also will enforce // them, and we intend to switch to it eventually. template inline constexpr bool eh_assign_constraints = std::is_nothrow_constructible_v || std::is_nothrow_move_constructible_v || std::is_nothrow_move_constructible_v; // Implement expected<..., E>::expected([const] unexpected [&/&&]). #define _CONSTRUCT_EXPECTED_FROM_UNEXPECTED(GF, ParamType, forward_func) \ template )> \ constexpr _EXPLICIT((!std::is_convertible_v)) \ expected(ParamType e) noexcept(std::is_nothrow_constructible_v) \ : var_(std::in_place_index<1>, forward_func(e.error())) {} // Implement expected<..., E>::operator=([const] unexpected [&/&&]). #define _ASSIGN_UNEXPECTED_TO_EXPECTED(GF, ParamType, forward_func, extra_constraints) \ template && \ std::is_assignable_v) && \ extra_constraints> \ constexpr expected& operator=(ParamType e) noexcept(std::is_nothrow_constructible_v && \ std::is_nothrow_assignable_v) { \ if (has_value()) { \ var_.template emplace<1>(forward_func(e.error())); \ } else { \ error() = forward_func(e.error()); \ } \ return *this; \ } } // namespace expected_internal // Class expected template class _NODISCARD_ expected { static_assert(std::is_object_v && !std::is_array_v && !std::is_same_v, std::in_place_t> && !std::is_same_v, unexpect_t> && !expected_internal::is_unexpected_v>, "expected value type cannot be a reference, a function, an array, in_place_t, " "unexpect_t, or unexpected"); public: using value_type = T; using error_type = E; using unexpected_type = unexpected; template using rebind = expected; // Delegate simple operations to the underlying std::variant. std::variant // doesn't set noexcept well, at least for copy ctor/assign, so set it // explicitly. Technically the copy/move assignment operators should also be // deleted if neither T nor E satisfies is_nothrow_move_constructible_v, but // that would require making these operator= methods into template functions. constexpr expected() = default; constexpr expected(const expected& rhs) noexcept( std::is_nothrow_copy_constructible_v && std::is_nothrow_copy_constructible_v) = default; constexpr expected(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) = default; constexpr expected& operator=(const expected& rhs) noexcept( std::is_nothrow_copy_constructible_v && std::is_nothrow_copy_assignable_v && std::is_nothrow_copy_constructible_v && std::is_nothrow_copy_assignable_v) = default; constexpr expected& operator=(expected&& rhs) noexcept( std::is_nothrow_move_constructible_v && std::is_nothrow_move_assignable_v && std::is_nothrow_move_constructible_v && std::is_nothrow_move_assignable_v) = default; // Construct this expected from a different expected type. #define _CONVERTING_CTOR(UF, GF, ParamType, forward_func) \ template && \ expected_internal::convert_error_constraints)> \ constexpr _EXPLICIT((!std::is_convertible_v || !std::is_convertible_v)) \ expected(ParamType rhs) noexcept(std::is_nothrow_constructible_v && \ std::is_nothrow_constructible_v) \ : var_(rhs.has_value() ? variant_type(std::in_place_index<0>, forward_func(rhs.value())) \ : variant_type(std::in_place_index<1>, forward_func(rhs.error()))) {} // NOLINTNEXTLINE(google-explicit-constructor) _CONVERTING_CTOR(const U&, const G&, const expected&, ) // NOLINTNEXTLINE(google-explicit-constructor) _CONVERTING_CTOR(U, G, expected&&, std::move) #undef _CONVERTING_CTOR // Construct from (converted) success value, using a forwarding reference. template , std::in_place_t> && !std::is_same_v, expected> && !expected_internal::is_unexpected_v> && std::is_constructible_v && (!std::is_same_v, bool> || !expected_internal::is_expected_v>))> constexpr _EXPLICIT((!std::is_convertible_v)) // NOLINTNEXTLINE(google-explicit-constructor) expected(U&& v) noexcept(std::is_nothrow_constructible_v) : var_(std::in_place_index<0>, std::forward(v)) {} // NOLINTNEXTLINE(google-explicit-constructor) _CONSTRUCT_EXPECTED_FROM_UNEXPECTED(const G&, const unexpected&, ) // NOLINTNEXTLINE(google-explicit-constructor) _CONSTRUCT_EXPECTED_FROM_UNEXPECTED(G, unexpected&&, std::move) // in_place_t construction template )> constexpr explicit expected(std::in_place_t, Args&&... args) noexcept(std::is_nothrow_constructible_v) : var_(std::in_place_index<0>, std::forward(args)...) {} // in_place_t with initializer_list construction template &, Args...>)> constexpr explicit expected(std::in_place_t, std::initializer_list il, Args&&... args) noexcept(std::is_nothrow_constructible_v&, Args...>) : var_(std::in_place_index<0>, il, std::forward(args)...) {} // unexpect_t construction template )> constexpr explicit expected(unexpect_t, Args&&... args) noexcept(std::is_nothrow_constructible_v) : var_(std::in_place_index<1>, unexpected_type(std::forward(args)...)) {} // unexpect_t with initializer_list construction template &, Args...>)> constexpr explicit expected(unexpect_t, std::initializer_list il, Args&&... args) noexcept(std::is_nothrow_constructible_v&, Args...>) : var_(std::in_place_index<1>, unexpected_type(il, std::forward(args)...)) {} // Assignment from (converted) success value, using a forwarding reference. template > && !expected_internal::is_unexpected_v> && std::is_constructible_v && std::is_assignable_v && expected_internal::eh_assign_constraints)> constexpr expected& operator=(U&& v) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_assignable_v) { if (has_value()) { value() = std::forward(v); } else { var_.template emplace<0>(std::forward(v)); } return *this; } _ASSIGN_UNEXPECTED_TO_EXPECTED(const G&, const unexpected&, , (expected_internal::eh_assign_constraints)) _ASSIGN_UNEXPECTED_TO_EXPECTED(G, unexpected&&, std::move, (expected_internal::eh_assign_constraints)) // modifiers template )> constexpr T& emplace(Args&&... args) noexcept { var_.template emplace<0>(std::forward(args)...); return value(); } template &, Args...>)> constexpr T& emplace(std::initializer_list il, Args&&... args) noexcept { var_.template emplace<0>(il, std::forward(args)...); return value(); } // Swap. This function takes a template argument so that _ENABLE_IF works. template && std::is_swappable_v && std::is_swappable_v && std::is_move_constructible_v && std::is_move_constructible_v && (std::is_nothrow_move_constructible_v || std::is_nothrow_move_constructible_v))> constexpr void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_swappable_v && std::is_nothrow_move_constructible_v && std::is_nothrow_swappable_v) { var_.swap(rhs.var_); } // observers constexpr const T* operator->() const { return std::addressof(value()); } constexpr T* operator->() { return std::addressof(value()); } constexpr const T& operator*() const& { return value(); } constexpr T& operator*() & { return value(); } constexpr const T&& operator*() const&& { return std::move(std::get(var_)); } constexpr T&& operator*() && { return std::move(std::get(var_)); } constexpr bool has_value() const noexcept { return var_.index() == 0; } constexpr bool ok() const noexcept { return has_value(); } constexpr explicit operator bool() const noexcept { return has_value(); } constexpr const T& value() const& { return std::get(var_); } constexpr T& value() & { return std::get(var_); } constexpr const T&& value() const&& { return std::move(std::get(var_)); } constexpr T&& value() && { return std::move(std::get(var_)); } constexpr const E& error() const& { return std::get(var_).error(); } constexpr E& error() & { return std::get(var_).error(); } constexpr const E&& error() const&& { return std::move(std::get(var_)).error(); } constexpr E&& error() && { return std::move(std::get(var_)).error(); } template && std::is_convertible_v )> constexpr T value_or(U&& v) const& { if (has_value()) return value(); else return static_cast(std::forward(v)); } template && std::is_convertible_v )> constexpr T value_or(U&& v) && { if (has_value()) return std::move(value()); else return static_cast(std::forward(v)); } // expected equality operators template friend constexpr bool operator==(const expected& x, const expected& y); template friend constexpr bool operator!=(const expected& x, const expected& y); // Comparison with unexpected template friend constexpr bool operator==(const expected&, const unexpected&); template friend constexpr bool operator==(const unexpected&, const expected&); template friend constexpr bool operator!=(const expected&, const unexpected&); template friend constexpr bool operator!=(const unexpected&, const expected&); private: using variant_type = std::variant; variant_type var_; }; template constexpr bool operator==(const expected& x, const expected& y) { if (x.has_value() != y.has_value()) return false; if (!x.has_value()) return x.error() == y.error(); return *x == *y; } template constexpr bool operator!=(const expected& x, const expected& y) { return !(x == y); } // Comparison with unexpected template constexpr bool operator==(const expected& x, const unexpected& y) { return !x.has_value() && (x.error() == y.error()); } template constexpr bool operator==(const unexpected& x, const expected& y) { return !y.has_value() && (x.error() == y.error()); } template constexpr bool operator!=(const expected& x, const unexpected& y) { return x.has_value() || (x.error() != y.error()); } template constexpr bool operator!=(const unexpected& x, const expected& y) { return y.has_value() || (x.error() != y.error()); } template class _NODISCARD_ expected { public: using value_type = void; using error_type = E; using unexpected_type = unexpected; template using rebind = expected; // Delegate simple operations to the underlying std::variant. constexpr expected() = default; constexpr expected(const expected& rhs) noexcept(std::is_nothrow_copy_constructible_v) = default; constexpr expected(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v) = default; constexpr expected& operator=(const expected& rhs) noexcept( std::is_nothrow_copy_constructible_v && std::is_nothrow_copy_assignable_v) = default; constexpr expected& operator=(expected&& rhs) noexcept( std::is_nothrow_move_constructible_v && std::is_nothrow_move_assignable_v) = default; // Construct this expected from a different expected type. #define _CONVERTING_CTOR(GF, ParamType, forward_func) \ template && \ expected_internal::convert_error_constraints)> \ constexpr _EXPLICIT((!std::is_convertible_v)) \ expected(ParamType rhs) noexcept(std::is_nothrow_constructible_v) \ : var_(rhs.has_value() ? variant_type(std::in_place_index<0>, std::monostate()) \ : variant_type(std::in_place_index<1>, forward_func(rhs.error()))) {} // NOLINTNEXTLINE(google-explicit-constructor) _CONVERTING_CTOR(const G&, const expected&, ) // NOLINTNEXTLINE(google-explicit-constructor) _CONVERTING_CTOR(G, expected&&, std::move) #undef _CONVERTING_CTOR // NOLINTNEXTLINE(google-explicit-constructor) _CONSTRUCT_EXPECTED_FROM_UNEXPECTED(const G&, const unexpected&, ) // NOLINTNEXTLINE(google-explicit-constructor) _CONSTRUCT_EXPECTED_FROM_UNEXPECTED(G, unexpected&&, std::move) // in_place_t construction constexpr explicit expected(std::in_place_t) noexcept {} // unexpect_t construction template )> constexpr explicit expected(unexpect_t, Args&&... args) noexcept(std::is_nothrow_constructible_v) : var_(std::in_place_index<1>, unexpected_type(std::forward(args)...)) {} // unexpect_t with initializer_list construction template &, Args...>)> constexpr explicit expected(unexpect_t, std::initializer_list il, Args&&... args) noexcept(std::is_nothrow_constructible_v&, Args...>) : var_(std::in_place_index<1>, unexpected_type(il, std::forward(args)...)) {} _ASSIGN_UNEXPECTED_TO_EXPECTED(const G&, const unexpected&, , true) _ASSIGN_UNEXPECTED_TO_EXPECTED(G, unexpected&&, std::move, true) // modifiers constexpr void emplace() noexcept { var_.template emplace<0>(std::monostate()); } // Swap. This function takes a template argument so that _ENABLE_IF works. template && std::is_swappable_v && std::is_move_constructible_v)> constexpr void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_swappable_v) { var_.swap(rhs.var_); } // observers constexpr bool has_value() const noexcept { return var_.index() == 0; } constexpr bool ok() const noexcept { return has_value(); } constexpr explicit operator bool() const noexcept { return has_value(); } constexpr void value() const& { if (!has_value()) std::get<0>(var_); } constexpr const E& error() const& { return std::get<1>(var_).error(); } constexpr E& error() & { return std::get<1>(var_).error(); } constexpr const E&& error() const&& { return std::move(std::get<1>(var_)).error(); } constexpr E&& error() && { return std::move(std::get<1>(var_)).error(); } // expected equality operators template friend constexpr bool operator==(const expected& x, const expected& y); private: using variant_type = std::variant; variant_type var_; }; template constexpr bool operator==(const expected& x, const expected& y) { if (x.has_value() != y.has_value()) return false; if (!x.has_value()) return x.error() == y.error(); return true; } template constexpr bool operator==(const expected& x, const expected& y) { if (x.has_value() != y.has_value()) return false; if (!x.has_value()) return x.error() == y.error(); return false; } template constexpr bool operator==(const expected& x, const expected& y) { if (x.has_value() != y.has_value()) return false; if (!x.has_value()) return x.error() == y.error(); return false; } template &>().swap(std::declval&>()))> constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))) { x.swap(y); } template class unexpected { static_assert(std::is_object_v && !std::is_array_v && !std::is_const_v && !std::is_volatile_v && !expected_internal::is_unexpected_v, "unexpected error type cannot be a reference, a function, an array, cv-qualified, " "or unexpected"); public: // constructors constexpr unexpected(const unexpected&) = default; constexpr unexpected(unexpected&&) = default; template , unexpected> && !std::is_same_v, std::in_place_t> && std::is_constructible_v)> constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v) : val_(std::forward(e)) {} template )> constexpr explicit unexpected(std::in_place_t, Args&&... args) noexcept(std::is_nothrow_constructible_v) : val_(std::forward(args)...) {} template &, Args...>)> constexpr explicit unexpected(std::in_place_t, std::initializer_list il, Args&&... args) noexcept(std::is_nothrow_constructible_v&, Args...>) : val_(il, std::forward(args)...) {} constexpr unexpected& operator=(const unexpected&) = default; constexpr unexpected& operator=(unexpected&&) = default; // observer constexpr const E& error() const& noexcept { return val_; } constexpr E& error() & noexcept { return val_; } constexpr const E&& error() const&& noexcept { return std::move(val_); } constexpr E&& error() && noexcept { return std::move(val_); } // Swap. This function takes a template argument so that _ENABLE_IF works. template && std::is_swappable_v)> void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v) { // Make std::swap visible to provide swap for STL and builtin types, but use // an unqualified swap to invoke argument-dependent lookup to find the swap // functions for user-declared types. using std::swap; swap(val_, other.val_); } template friend constexpr bool operator==(const unexpected& e1, const unexpected& e2); template friend constexpr bool operator!=(const unexpected& e1, const unexpected& e2); private: E val_; }; template constexpr bool operator==(const unexpected& e1, const unexpected& e2) { return e1.error() == e2.error(); } template constexpr bool operator!=(const unexpected& e1, const unexpected& e2) { return e1.error() != e2.error(); } template )> void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) { x.swap(y); } // TODO: bad_expected_access class #undef _ENABLE_IF #undef _NODISCARD_ #undef _EXPLICIT #undef _COMMA #undef _CONSTRUCT_EXPECTED_FROM_UNEXPECTED #undef _ASSIGN_UNEXPECTED_TO_EXPECTED } // namespace base } // namespace android