/* * 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 an Android implementation of the std::expected // proposal. // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0323r7.html // // 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) { printf("%f\n", q.value()); } // else { printf("%s\n", q.error().c_str()); } // } // // When the proposal becomes part of the standard and is implemented by // libcxx, this will be removed and android::base::expected will be // type alias 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 // Class expected template class _NODISCARD_ expected { public: using value_type = T; using error_type = E; using unexpected_type = unexpected; template using rebind = expected; // constructors constexpr expected() = default; constexpr expected(const expected& rhs) = default; constexpr expected(expected&& rhs) noexcept = default; template && std::is_constructible_v && !std::is_constructible_v&> && !std::is_constructible_v&&> && !std::is_constructible_v&> && !std::is_constructible_v&&> && !std::is_convertible_v&, T> && !std::is_convertible_v&&, T> && !std::is_convertible_v&, T> && !std::is_convertible_v&&, T> && !(!std::is_convertible_v || !std::is_convertible_v) /* non-explicit */ )> // NOLINTNEXTLINE(google-explicit-constructor) constexpr expected(const expected& rhs) { if (rhs.has_value()) var_ = rhs.value(); else var_ = unexpected(rhs.error()); } template && std::is_constructible_v && !std::is_constructible_v&> && !std::is_constructible_v&&> && !std::is_constructible_v&> && !std::is_constructible_v&&> && !std::is_convertible_v&, T> && !std::is_convertible_v&&, T> && !std::is_convertible_v&, T> && !std::is_convertible_v&&, T> && (!std::is_convertible_v || !std::is_convertible_v) /* explicit */ )> constexpr explicit expected(const expected& rhs) { if (rhs.has_value()) var_ = rhs.value(); else var_ = unexpected(rhs.error()); } template && std::is_constructible_v && !std::is_constructible_v&> && !std::is_constructible_v&&> && !std::is_constructible_v&> && !std::is_constructible_v&&> && !std::is_convertible_v&, T> && !std::is_convertible_v&&, T> && !std::is_convertible_v&, T> && !std::is_convertible_v&&, T> && !(!std::is_convertible_v || !std::is_convertible_v) /* non-explicit */ )> // NOLINTNEXTLINE(google-explicit-constructor) constexpr expected(expected&& rhs) { if (rhs.has_value()) var_ = std::move(rhs.value()); else var_ = unexpected(std::move(rhs.error())); } template && std::is_constructible_v && !std::is_constructible_v&> && !std::is_constructible_v&&> && !std::is_constructible_v&> && !std::is_constructible_v&&> && !std::is_convertible_v&, T> && !std::is_convertible_v&&, T> && !std::is_convertible_v&, T> && !std::is_convertible_v&&, T> && (!std::is_convertible_v || !std::is_convertible_v) /* explicit */ )> constexpr explicit expected(expected&& rhs) { if (rhs.has_value()) var_ = std::move(rhs.value()); else var_ = unexpected(std::move(rhs.error())); } template && !std::is_same_v>, std::in_place_t> && !std::is_same_v, std::remove_cv_t>> && !std::is_same_v, std::remove_cv_t>> && std::is_convertible_v /* non-explicit */ )> // NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload) constexpr expected(U&& v) : var_(std::in_place_index<0>, std::forward(v)) {} template && !std::is_same_v>, std::in_place_t> && !std::is_same_v, std::remove_cv_t>> && !std::is_same_v, std::remove_cv_t>> && !std::is_convertible_v /* explicit */ )> // NOLINTNEXTLINE(bugprone-forwarding-reference-overload) constexpr explicit expected(U&& v) : var_(std::in_place_index<0>, T(std::forward(v))) {} template && std::is_convertible_v /* non-explicit */ )> // NOLINTNEXTLINE(google-explicit-constructor) constexpr expected(const unexpected& e) : var_(std::in_place_index<1>, e.value()) {} template && !std::is_convertible_v /* explicit */ )> constexpr explicit expected(const unexpected& e) : var_(std::in_place_index<1>, E(e.value())) {} template && std::is_convertible_v /* non-explicit */ )> // NOLINTNEXTLINE(google-explicit-constructor) constexpr expected(unexpected&& e) : var_(std::in_place_index<1>, std::move(e.value())) {} template && !std::is_convertible_v /* explicit */ )> constexpr explicit expected(unexpected&& e) : var_(std::in_place_index<1>, E(std::move(e.value()))) {} template )> constexpr explicit expected(std::in_place_t, Args&&... args) : var_(std::in_place_index<0>, std::forward(args)...) {} template&, Args...> )> constexpr explicit expected(std::in_place_t, std::initializer_list il, Args&&... args) : var_(std::in_place_index<0>, il, std::forward(args)...) {} template )> constexpr explicit expected(unexpect_t, Args&&... args) : var_(unexpected_type(std::forward(args)...)) {} template&, Args...> )> constexpr explicit expected(unexpect_t, std::initializer_list il, Args&&... args) : var_(unexpected_type(il, std::forward(args)...)) {} // destructor ~expected() = default; // assignment // Note: SFNAIE doesn't work here because assignment operator should be // non-template. We could workaround this by defining a templated parent class // having the assignment operator. This incomplete implementation however // doesn't allow us to copy assign expected even when T is non-copy // assignable. The copy assignment will fail by the underlying std::variant // anyway though the error message won't be clear. expected& operator=(const expected& rhs) = default; // Note for SFNAIE above applies to here as well expected& operator=(expected&& rhs) noexcept( std::is_nothrow_move_assignable_v&& std::is_nothrow_move_assignable_v) = default; template && !std::is_same_v, std::remove_cv_t>> && !std::conjunction_v, std::is_same>> && std::is_constructible_v && std::is_assignable_v && std::is_nothrow_move_constructible_v)> expected& operator=(U&& rhs) { var_ = T(std::forward(rhs)); return *this; } template expected& operator=(const unexpected& rhs) { var_ = rhs; return *this; } template && std::is_move_assignable_v )> expected& operator=(unexpected&& rhs) { var_ = std::move(rhs); return *this; } // modifiers template )> T& emplace(Args&&... args) { expected(std::in_place, std::forward(args)...).swap(*this); return value(); } template&, Args...> )> T& emplace(std::initializer_list il, Args&&... args) { expected(std::in_place, il, std::forward(args)...).swap(*this); return value(); } // swap template && std::is_swappable_v && (std::is_move_constructible_v || std::is_move_constructible_v))>> 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 explicit operator bool() const noexcept { return has_value(); } constexpr bool has_value() const noexcept { return var_.index() == 0; } constexpr bool ok() 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_).value(); } constexpr E& error() & { return std::get(var_).value(); } constexpr const E&& error() const&& { return std::move(std::get(var_)).value(); } constexpr E&& error() && { return std::move(std::get(var_)).value(); } 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&); // Specialized algorithms template friend void swap(expected&, expected&) noexcept; private: std::variant 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.value()); } template constexpr bool operator==(const unexpected& x, const expected& y) { return !y.has_value() && (x.value() == y.error()); } template constexpr bool operator!=(const expected& x, const unexpected& y) { return x.has_value() || (x.error() != y.value()); } template constexpr bool operator!=(const unexpected& x, const expected& y) { return y.has_value() || (x.value() != y.error()); } template class _NODISCARD_ expected { public: using value_type = void; using error_type = E; using unexpected_type = unexpected; // constructors constexpr expected() = default; constexpr expected(const expected& rhs) = default; constexpr expected(expected&& rhs) noexcept = default; template && std::is_convertible_v /* non-explicit */ )> // NOLINTNEXTLINE(google-explicit-constructor) constexpr expected(const expected& rhs) { if (!rhs.has_value()) var_ = unexpected(rhs.error()); } template && !std::is_convertible_v /* explicit */ )> constexpr explicit expected(const expected& rhs) { if (!rhs.has_value()) var_ = unexpected(rhs.error()); } template && std::is_convertible_v /* non-explicit */ )> // NOLINTNEXTLINE(google-explicit-constructor) constexpr expected(expected&& rhs) { if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error())); } template && !std::is_convertible_v /* explicit */ )> constexpr explicit expected(expected&& rhs) { if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error())); } template && std::is_convertible_v /* non-explicit */ )> // NOLINTNEXTLINE(google-explicit-constructor) constexpr expected(const unexpected& e) : var_(std::in_place_index<1>, e.value()) {} template && !std::is_convertible_v /* explicit */ )> constexpr explicit expected(const unexpected& e) : var_(std::in_place_index<1>, E(e.value())) {} template && std::is_convertible_v /* non-explicit */ )> // NOLINTNEXTLINE(google-explicit-constructor) constexpr expected(unexpected&& e) : var_(std::in_place_index<1>, std::move(e.value())) {} template && !std::is_convertible_v /* explicit */ )> constexpr explicit expected(unexpected&& e) : var_(std::in_place_index<1>, E(std::move(e.value()))) {} template constexpr explicit expected(std::in_place_t, Args&&...) {} template )> constexpr explicit expected(unexpect_t, Args&&... args) : var_(unexpected_type(std::forward(args)...)) {} template&, Args...> )> constexpr explicit expected(unexpect_t, std::initializer_list il, Args&&... args) : var_(unexpected_type(il, std::forward(args)...)) {} // destructor ~expected() = default; // assignment // Note: SFNAIE doesn't work here because assignment operator should be // non-template. We could workaround this by defining a templated parent class // having the assignment operator. This incomplete implementation however // doesn't allow us to copy assign expected even when T is non-copy // assignable. The copy assignment will fail by the underlying std::variant // anyway though the error message won't be clear. expected& operator=(const expected& rhs) = default; // Note for SFNAIE above applies to here as well expected& operator=(expected&& rhs) noexcept(std::is_nothrow_move_assignable_v) = default; template expected& operator=(const unexpected& rhs) { var_ = rhs; return *this; } template && std::is_move_assignable_v )> expected& operator=(unexpected&& rhs) { var_ = std::move(rhs); return *this; } // modifiers void emplace() { var_ = std::monostate(); } // swap template> > void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v) { var_.swap(rhs.var_); } // observers constexpr explicit operator bool() const noexcept { return has_value(); } constexpr bool has_value() const noexcept { return var_.index() == 0; } constexpr bool ok() const noexcept { return has_value(); } constexpr void value() const& { if (!has_value()) std::get<0>(var_); } constexpr const E& error() const& { return std::get(var_).value(); } constexpr E& error() & { return std::get(var_).value(); } constexpr const E&& error() const&& { return std::move(std::get(var_)).value(); } constexpr E&& error() && { return std::move(std::get(var_)).value(); } // expected equality operators template friend constexpr bool operator==(const expected& x, const expected& y); // Specialized algorithms template friend void swap(expected&, expected&) noexcept; private: std::variant 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 class unexpected { public: // constructors constexpr unexpected(const unexpected&) = default; constexpr unexpected(unexpected&&) noexcept(std::is_nothrow_move_constructible_v) = default; template && !std::is_same_v>, std::in_place_t> && !std::is_same_v>, unexpected>)> // NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload) constexpr unexpected(Err&& e) : val_(std::forward(e)) {} template&, Args...> )> constexpr explicit unexpected(std::in_place_t, std::initializer_list il, Args&&... args) : val_(il, std::forward(args)...) {} template && !std::is_constructible_v&> && !std::is_constructible_v> && !std::is_constructible_v&> && !std::is_constructible_v> && !std::is_convertible_v&, E> && !std::is_convertible_v, E> && !std::is_convertible_v&, E> && !std::is_convertible_v, E> && std::is_convertible_v /* non-explicit */ )> // NOLINTNEXTLINE(google-explicit-constructor) constexpr unexpected(const unexpected& rhs) : val_(rhs.value()) {} template && !std::is_constructible_v&> && !std::is_constructible_v> && !std::is_constructible_v&> && !std::is_constructible_v> && !std::is_convertible_v&, E> && !std::is_convertible_v, E> && !std::is_convertible_v&, E> && !std::is_convertible_v, E> && !std::is_convertible_v /* explicit */ )> constexpr explicit unexpected(const unexpected& rhs) : val_(E(rhs.value())) {} template && !std::is_constructible_v&> && !std::is_constructible_v> && !std::is_constructible_v&> && !std::is_constructible_v> && !std::is_convertible_v&, E> && !std::is_convertible_v, E> && !std::is_convertible_v&, E> && !std::is_convertible_v, E> && std::is_convertible_v /* non-explicit */ )> // NOLINTNEXTLINE(google-explicit-constructor) constexpr unexpected(unexpected&& rhs) : val_(std::move(rhs.value())) {} template && !std::is_constructible_v&> && !std::is_constructible_v> && !std::is_constructible_v&> && !std::is_constructible_v> && !std::is_convertible_v&, E> && !std::is_convertible_v, E> && !std::is_convertible_v&, E> && !std::is_convertible_v, E> && !std::is_convertible_v /* explicit */ )> constexpr explicit unexpected(unexpected&& rhs) : val_(E(std::move(rhs.value()))) {} // assignment constexpr unexpected& operator=(const unexpected&) = default; constexpr unexpected& operator=(unexpected&&) noexcept(std::is_nothrow_move_assignable_v) = default; template constexpr unexpected& operator=(const unexpected& rhs) { val_ = rhs.value(); return *this; } template constexpr unexpected& operator=(unexpected&& rhs) { val_ = std::forward(rhs.value()); return *this; } // observer constexpr const E& value() const& noexcept { return val_; } constexpr E& value() & noexcept { return val_; } constexpr const E&& value() const&& noexcept { return std::move(val_); } constexpr E&& value() && noexcept { return std::move(val_); } void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v) { std::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); template friend void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))); private: E val_; }; template constexpr bool operator==(const unexpected& e1, const unexpected& e2) { return e1.value() == e2.value(); } template constexpr bool operator!=(const unexpected& e1, const unexpected& e2) { return e1.value() != e2.value(); } 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_ } // namespace base } // namespace android