You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
748 lines
26 KiB
748 lines
26 KiB
/*
|
|
* 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 <algorithm>
|
|
#include <initializer_list>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <variant>
|
|
|
|
// 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<double,std::string> 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 T, class E>
|
|
class expected;
|
|
|
|
template<class E>
|
|
class unexpected;
|
|
template<class E>
|
|
unexpected(E) -> unexpected<E>;
|
|
|
|
template<class E>
|
|
class bad_expected_access;
|
|
|
|
template<>
|
|
class bad_expected_access<void>;
|
|
|
|
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<T,E> 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 T, class E>
|
|
class _NODISCARD_ expected {
|
|
public:
|
|
using value_type = T;
|
|
using error_type = E;
|
|
using unexpected_type = unexpected<E>;
|
|
|
|
template<class U>
|
|
using rebind = expected<U, error_type>;
|
|
|
|
// constructors
|
|
constexpr expected() = default;
|
|
constexpr expected(const expected& rhs) = default;
|
|
constexpr expected(expected&& rhs) noexcept = default;
|
|
|
|
template<class U, class G _ENABLE_IF(
|
|
std::is_constructible_v<T, const U&> &&
|
|
std::is_constructible_v<E, const G&> &&
|
|
!std::is_constructible_v<T, expected<U, G>&> &&
|
|
!std::is_constructible_v<T, expected<U, G>&&> &&
|
|
!std::is_constructible_v<T, const expected<U, G>&> &&
|
|
!std::is_constructible_v<T, const expected<U, G>&&> &&
|
|
!std::is_convertible_v<expected<U, G>&, T> &&
|
|
!std::is_convertible_v<expected<U, G>&&, T> &&
|
|
!std::is_convertible_v<const expected<U, G>&, T> &&
|
|
!std::is_convertible_v<const expected<U, G>&&, T> &&
|
|
!(!std::is_convertible_v<const U&, T> ||
|
|
!std::is_convertible_v<const G&, E>) /* non-explicit */
|
|
)>
|
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
constexpr expected(const expected<U, G>& rhs) {
|
|
if (rhs.has_value()) var_ = rhs.value();
|
|
else var_ = unexpected(rhs.error());
|
|
}
|
|
|
|
template<class U, class G _ENABLE_IF(
|
|
std::is_constructible_v<T, const U&> &&
|
|
std::is_constructible_v<E, const G&> &&
|
|
!std::is_constructible_v<T, expected<U, G>&> &&
|
|
!std::is_constructible_v<T, expected<U, G>&&> &&
|
|
!std::is_constructible_v<T, const expected<U, G>&> &&
|
|
!std::is_constructible_v<T, const expected<U, G>&&> &&
|
|
!std::is_convertible_v<expected<U, G>&, T> &&
|
|
!std::is_convertible_v<expected<U, G>&&, T> &&
|
|
!std::is_convertible_v<const expected<U, G>&, T> &&
|
|
!std::is_convertible_v<const expected<U, G>&&, T> &&
|
|
(!std::is_convertible_v<const U&, T> ||
|
|
!std::is_convertible_v<const G&, E>) /* explicit */
|
|
)>
|
|
constexpr explicit expected(const expected<U, G>& rhs) {
|
|
if (rhs.has_value()) var_ = rhs.value();
|
|
else var_ = unexpected(rhs.error());
|
|
}
|
|
|
|
template<class U, class G _ENABLE_IF(
|
|
std::is_constructible_v<T, const U&> &&
|
|
std::is_constructible_v<E, const G&> &&
|
|
!std::is_constructible_v<T, expected<U, G>&> &&
|
|
!std::is_constructible_v<T, expected<U, G>&&> &&
|
|
!std::is_constructible_v<T, const expected<U, G>&> &&
|
|
!std::is_constructible_v<T, const expected<U, G>&&> &&
|
|
!std::is_convertible_v<expected<U, G>&, T> &&
|
|
!std::is_convertible_v<expected<U, G>&&, T> &&
|
|
!std::is_convertible_v<const expected<U, G>&, T> &&
|
|
!std::is_convertible_v<const expected<U, G>&&, T> &&
|
|
!(!std::is_convertible_v<const U&, T> ||
|
|
!std::is_convertible_v<const G&, E>) /* non-explicit */
|
|
)>
|
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
constexpr expected(expected<U, G>&& rhs) {
|
|
if (rhs.has_value()) var_ = std::move(rhs.value());
|
|
else var_ = unexpected(std::move(rhs.error()));
|
|
}
|
|
|
|
template<class U, class G _ENABLE_IF(
|
|
std::is_constructible_v<T, const U&> &&
|
|
std::is_constructible_v<E, const G&> &&
|
|
!std::is_constructible_v<T, expected<U, G>&> &&
|
|
!std::is_constructible_v<T, expected<U, G>&&> &&
|
|
!std::is_constructible_v<T, const expected<U, G>&> &&
|
|
!std::is_constructible_v<T, const expected<U, G>&&> &&
|
|
!std::is_convertible_v<expected<U, G>&, T> &&
|
|
!std::is_convertible_v<expected<U, G>&&, T> &&
|
|
!std::is_convertible_v<const expected<U, G>&, T> &&
|
|
!std::is_convertible_v<const expected<U, G>&&, T> &&
|
|
(!std::is_convertible_v<const U&, T> ||
|
|
!std::is_convertible_v<const G&, E>) /* explicit */
|
|
)>
|
|
constexpr explicit expected(expected<U, G>&& rhs) {
|
|
if (rhs.has_value()) var_ = std::move(rhs.value());
|
|
else var_ = unexpected(std::move(rhs.error()));
|
|
}
|
|
|
|
template <class U = T _ENABLE_IF(
|
|
std::is_constructible_v<T, U&&> &&
|
|
!std::is_same_v<std::remove_cv_t<std::remove_reference_t<U>>, std::in_place_t> &&
|
|
!std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
|
|
!std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
|
|
std::is_convertible_v<U&&, T> /* non-explicit */
|
|
)>
|
|
// NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload)
|
|
constexpr expected(U&& v) : var_(std::in_place_index<0>, std::forward<U>(v)) {}
|
|
|
|
template <class U = T _ENABLE_IF(
|
|
std::is_constructible_v<T, U&&> &&
|
|
!std::is_same_v<std::remove_cv_t<std::remove_reference_t<U>>, std::in_place_t> &&
|
|
!std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
|
|
!std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
|
|
!std::is_convertible_v<U&&, T> /* explicit */
|
|
)>
|
|
// NOLINTNEXTLINE(bugprone-forwarding-reference-overload)
|
|
constexpr explicit expected(U&& v) : var_(std::in_place_index<0>, T(std::forward<U>(v))) {}
|
|
|
|
template<class G = E _ENABLE_IF(
|
|
std::is_constructible_v<E, const G&> &&
|
|
std::is_convertible_v<const G&, E> /* non-explicit */
|
|
)>
|
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
constexpr expected(const unexpected<G>& e)
|
|
: var_(std::in_place_index<1>, e.value()) {}
|
|
|
|
template<class G = E _ENABLE_IF(
|
|
std::is_constructible_v<E, const G&> &&
|
|
!std::is_convertible_v<const G&, E> /* explicit */
|
|
)>
|
|
constexpr explicit expected(const unexpected<G>& e)
|
|
: var_(std::in_place_index<1>, E(e.value())) {}
|
|
|
|
template<class G = E _ENABLE_IF(
|
|
std::is_constructible_v<E, G&&> &&
|
|
std::is_convertible_v<G&&, E> /* non-explicit */
|
|
)>
|
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
constexpr expected(unexpected<G>&& e)
|
|
: var_(std::in_place_index<1>, std::move(e.value())) {}
|
|
|
|
template<class G = E _ENABLE_IF(
|
|
std::is_constructible_v<E, G&&> &&
|
|
!std::is_convertible_v<G&&, E> /* explicit */
|
|
)>
|
|
constexpr explicit expected(unexpected<G>&& e)
|
|
: var_(std::in_place_index<1>, E(std::move(e.value()))) {}
|
|
|
|
template<class... Args _ENABLE_IF(
|
|
std::is_constructible_v<T, Args&&...>
|
|
)>
|
|
constexpr explicit expected(std::in_place_t, Args&&... args)
|
|
: var_(std::in_place_index<0>, std::forward<Args>(args)...) {}
|
|
|
|
template<class U, class... Args _ENABLE_IF(
|
|
std::is_constructible_v<T, std::initializer_list<U>&, Args...>
|
|
)>
|
|
constexpr explicit expected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
|
|
: var_(std::in_place_index<0>, il, std::forward<Args>(args)...) {}
|
|
|
|
template<class... Args _ENABLE_IF(
|
|
std::is_constructible_v<E, Args...>
|
|
)>
|
|
constexpr explicit expected(unexpect_t, Args&&... args)
|
|
: var_(unexpected_type(std::forward<Args>(args)...)) {}
|
|
|
|
template<class U, class... Args _ENABLE_IF(
|
|
std::is_constructible_v<E, std::initializer_list<U>&, Args...>
|
|
)>
|
|
constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
|
|
: var_(unexpected_type(il, std::forward<Args>(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<T,E> 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<T>&& std::is_nothrow_move_assignable_v<E>) = default;
|
|
|
|
template <class U = T _ENABLE_IF(
|
|
!std::is_void_v<T> &&
|
|
!std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
|
|
!std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::decay_t<U>>> &&
|
|
std::is_constructible_v<T, U> && std::is_assignable_v<T&, U> &&
|
|
std::is_nothrow_move_constructible_v<E>)>
|
|
expected& operator=(U&& rhs) {
|
|
var_ = T(std::forward<U>(rhs));
|
|
return *this;
|
|
}
|
|
|
|
template<class G = E>
|
|
expected& operator=(const unexpected<G>& rhs) {
|
|
var_ = rhs;
|
|
return *this;
|
|
}
|
|
|
|
template<class G = E _ENABLE_IF(
|
|
std::is_nothrow_move_constructible_v<G> &&
|
|
std::is_move_assignable_v<G>
|
|
)>
|
|
expected& operator=(unexpected<G>&& rhs) {
|
|
var_ = std::move(rhs);
|
|
return *this;
|
|
}
|
|
|
|
// modifiers
|
|
template<class... Args _ENABLE_IF(
|
|
std::is_nothrow_constructible_v<T, Args...>
|
|
)>
|
|
T& emplace(Args&&... args) {
|
|
expected(std::in_place, std::forward<Args>(args)...).swap(*this);
|
|
return value();
|
|
}
|
|
|
|
template<class U, class... Args _ENABLE_IF(
|
|
std::is_nothrow_constructible_v<T, std::initializer_list<U>&, Args...>
|
|
)>
|
|
T& emplace(std::initializer_list<U> il, Args&&... args) {
|
|
expected(std::in_place, il, std::forward<Args>(args)...).swap(*this);
|
|
return value();
|
|
}
|
|
|
|
// swap
|
|
template<typename U = T, typename = std::enable_if_t<(
|
|
std::is_swappable_v<U> &&
|
|
std::is_swappable_v<E> &&
|
|
(std::is_move_constructible_v<U> ||
|
|
std::is_move_constructible_v<E>))>>
|
|
void swap(expected& rhs) noexcept(
|
|
std::is_nothrow_move_constructible_v<T> &&
|
|
std::is_nothrow_swappable_v<T> &&
|
|
std::is_nothrow_move_constructible_v<E> &&
|
|
std::is_nothrow_swappable_v<E>) {
|
|
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<T>(var_)); }
|
|
constexpr T&& operator*() && { return std::move(std::get<T>(var_)); }
|
|
|
|
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<T>(var_); }
|
|
constexpr T& value() & { return std::get<T>(var_); }
|
|
constexpr const T&& value() const&& { return std::move(std::get<T>(var_)); }
|
|
constexpr T&& value() && { return std::move(std::get<T>(var_)); }
|
|
|
|
constexpr const E& error() const& { return std::get<unexpected_type>(var_).value(); }
|
|
constexpr E& error() & { return std::get<unexpected_type>(var_).value(); }
|
|
constexpr const E&& error() const&& { return std::move(std::get<unexpected_type>(var_)).value(); }
|
|
constexpr E&& error() && { return std::move(std::get<unexpected_type>(var_)).value(); }
|
|
|
|
template<class U _ENABLE_IF(
|
|
std::is_copy_constructible_v<T> &&
|
|
std::is_convertible_v<U, T>
|
|
)>
|
|
constexpr T value_or(U&& v) const& {
|
|
if (has_value()) return value();
|
|
else return static_cast<T>(std::forward<U>(v));
|
|
}
|
|
|
|
template<class U _ENABLE_IF(
|
|
std::is_move_constructible_v<T> &&
|
|
std::is_convertible_v<U, T>
|
|
)>
|
|
constexpr T value_or(U&& v) && {
|
|
if (has_value()) return std::move(value());
|
|
else return static_cast<T>(std::forward<U>(v));
|
|
}
|
|
|
|
// expected equality operators
|
|
template<class T1, class E1, class T2, class E2>
|
|
friend constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y);
|
|
template<class T1, class E1, class T2, class E2>
|
|
friend constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y);
|
|
|
|
// Comparison with unexpected<E>
|
|
template<class T1, class E1, class E2>
|
|
friend constexpr bool operator==(const expected<T1, E1>&, const unexpected<E2>&);
|
|
template<class T1, class E1, class E2>
|
|
friend constexpr bool operator==(const unexpected<E2>&, const expected<T1, E1>&);
|
|
template<class T1, class E1, class E2>
|
|
friend constexpr bool operator!=(const expected<T1, E1>&, const unexpected<E2>&);
|
|
template<class T1, class E1, class E2>
|
|
friend constexpr bool operator!=(const unexpected<E2>&, const expected<T1, E1>&);
|
|
|
|
// Specialized algorithms
|
|
template<class T1, class E1>
|
|
friend void swap(expected<T1, E1>& x, expected<T1, E1>& y) noexcept(noexcept(x.swap(y)));
|
|
|
|
private:
|
|
std::variant<value_type, unexpected_type> var_;
|
|
};
|
|
|
|
template<class T1, class E1, class T2, class E2>
|
|
constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y) {
|
|
if (x.has_value() != y.has_value()) return false;
|
|
if (!x.has_value()) return x.error() == y.error();
|
|
return *x == *y;
|
|
}
|
|
|
|
template<class T1, class E1, class T2, class E2>
|
|
constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y) {
|
|
return !(x == y);
|
|
}
|
|
|
|
// Comparison with unexpected<E>
|
|
template<class T1, class E1, class E2>
|
|
constexpr bool operator==(const expected<T1, E1>& x, const unexpected<E2>& y) {
|
|
return !x.has_value() && (x.error() == y.value());
|
|
}
|
|
template<class T1, class E1, class E2>
|
|
constexpr bool operator==(const unexpected<E2>& x, const expected<T1, E1>& y) {
|
|
return !y.has_value() && (x.value() == y.error());
|
|
}
|
|
template<class T1, class E1, class E2>
|
|
constexpr bool operator!=(const expected<T1, E1>& x, const unexpected<E2>& y) {
|
|
return x.has_value() || (x.error() != y.value());
|
|
}
|
|
template<class T1, class E1, class E2>
|
|
constexpr bool operator!=(const unexpected<E2>& x, const expected<T1, E1>& y) {
|
|
return y.has_value() || (x.value() != y.error());
|
|
}
|
|
|
|
template<class T1, class E1>
|
|
void swap(expected<T1, E1>& x, expected<T1, E1>& y) noexcept(noexcept(x.swap(y))) {
|
|
x.swap(y);
|
|
}
|
|
|
|
template<class E>
|
|
class _NODISCARD_ expected<void, E> {
|
|
public:
|
|
using value_type = void;
|
|
using error_type = E;
|
|
using unexpected_type = unexpected<E>;
|
|
|
|
// constructors
|
|
constexpr expected() = default;
|
|
constexpr expected(const expected& rhs) = default;
|
|
constexpr expected(expected&& rhs) noexcept = default;
|
|
|
|
template<class U, class G _ENABLE_IF(
|
|
std::is_void_v<U> &&
|
|
std::is_convertible_v<const G&, E> /* non-explicit */
|
|
)>
|
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
constexpr expected(const expected<U, G>& rhs) {
|
|
if (!rhs.has_value()) var_ = unexpected(rhs.error());
|
|
}
|
|
|
|
template<class U, class G _ENABLE_IF(
|
|
std::is_void_v<U> &&
|
|
!std::is_convertible_v<const G&, E> /* explicit */
|
|
)>
|
|
constexpr explicit expected(const expected<U, G>& rhs) {
|
|
if (!rhs.has_value()) var_ = unexpected(rhs.error());
|
|
}
|
|
|
|
template<class U, class G _ENABLE_IF(
|
|
std::is_void_v<U> &&
|
|
std::is_convertible_v<const G&&, E> /* non-explicit */
|
|
)>
|
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
constexpr expected(expected<U, G>&& rhs) {
|
|
if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error()));
|
|
}
|
|
|
|
template<class U, class G _ENABLE_IF(
|
|
std::is_void_v<U> &&
|
|
!std::is_convertible_v<const G&&, E> /* explicit */
|
|
)>
|
|
constexpr explicit expected(expected<U, G>&& rhs) {
|
|
if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error()));
|
|
}
|
|
|
|
template<class G = E _ENABLE_IF(
|
|
std::is_constructible_v<E, const G&> &&
|
|
std::is_convertible_v<const G&, E> /* non-explicit */
|
|
)>
|
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
constexpr expected(const unexpected<G>& e)
|
|
: var_(std::in_place_index<1>, e.value()) {}
|
|
|
|
template<class G = E _ENABLE_IF(
|
|
std::is_constructible_v<E, const G&> &&
|
|
!std::is_convertible_v<const G&, E> /* explicit */
|
|
)>
|
|
constexpr explicit expected(const unexpected<G>& e)
|
|
: var_(std::in_place_index<1>, E(e.value())) {}
|
|
|
|
template<class G = E _ENABLE_IF(
|
|
std::is_constructible_v<E, G&&> &&
|
|
std::is_convertible_v<G&&, E> /* non-explicit */
|
|
)>
|
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
constexpr expected(unexpected<G>&& e)
|
|
: var_(std::in_place_index<1>, std::move(e.value())) {}
|
|
|
|
template<class G = E _ENABLE_IF(
|
|
std::is_constructible_v<E, G&&> &&
|
|
!std::is_convertible_v<G&&, E> /* explicit */
|
|
)>
|
|
constexpr explicit expected(unexpected<G>&& e)
|
|
: var_(std::in_place_index<1>, E(std::move(e.value()))) {}
|
|
|
|
template<class... Args _ENABLE_IF(
|
|
sizeof...(Args) == 0
|
|
)>
|
|
constexpr explicit expected(std::in_place_t, Args&&...) {}
|
|
|
|
template<class... Args _ENABLE_IF(
|
|
std::is_constructible_v<E, Args...>
|
|
)>
|
|
constexpr explicit expected(unexpect_t, Args&&... args)
|
|
: var_(unexpected_type(std::forward<Args>(args)...)) {}
|
|
|
|
template<class U, class... Args _ENABLE_IF(
|
|
std::is_constructible_v<E, std::initializer_list<U>&, Args...>
|
|
)>
|
|
constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
|
|
: var_(unexpected_type(il, std::forward<Args>(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<T,E> 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<E>) = default;
|
|
|
|
template<class G = E>
|
|
expected& operator=(const unexpected<G>& rhs) {
|
|
var_ = rhs;
|
|
return *this;
|
|
}
|
|
|
|
template<class G = E _ENABLE_IF(
|
|
std::is_nothrow_move_constructible_v<G> &&
|
|
std::is_move_assignable_v<G>
|
|
)>
|
|
expected& operator=(unexpected<G>&& rhs) {
|
|
var_ = std::move(rhs);
|
|
return *this;
|
|
}
|
|
|
|
// modifiers
|
|
void emplace() {
|
|
var_ = std::monostate();
|
|
}
|
|
|
|
// swap
|
|
template<typename = std::enable_if_t<
|
|
std::is_swappable_v<E>>
|
|
>
|
|
void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v<E>) {
|
|
var_.swap(rhs.var_);
|
|
}
|
|
|
|
// observers
|
|
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<unexpected_type>(var_).value(); }
|
|
constexpr E& error() & { return std::get<unexpected_type>(var_).value(); }
|
|
constexpr const E&& error() const&& { return std::move(std::get<unexpected_type>(var_)).value(); }
|
|
constexpr E&& error() && { return std::move(std::get<unexpected_type>(var_)).value(); }
|
|
|
|
// expected equality operators
|
|
template<class E1, class E2>
|
|
friend constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y);
|
|
|
|
// Specialized algorithms
|
|
template<class T1, class E1>
|
|
friend void swap(expected<T1, E1>& x, expected<T1, E1>& y) noexcept(noexcept(x.swap(y)));
|
|
|
|
private:
|
|
std::variant<std::monostate, unexpected_type> var_;
|
|
};
|
|
|
|
template<class E1, class E2>
|
|
constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y) {
|
|
if (x.has_value() != y.has_value()) return false;
|
|
if (!x.has_value()) return x.error() == y.error();
|
|
return true;
|
|
}
|
|
|
|
template<class T1, class E1, class E2>
|
|
constexpr bool operator==(const expected<T1, E1>& x, const expected<void, E2>& y) {
|
|
if (x.has_value() != y.has_value()) return false;
|
|
if (!x.has_value()) return x.error() == y.error();
|
|
return false;
|
|
}
|
|
|
|
template<class E1, class T2, class E2>
|
|
constexpr bool operator==(const expected<void, E1>& x, const expected<T2, E2>& y) {
|
|
if (x.has_value() != y.has_value()) return false;
|
|
if (!x.has_value()) return x.error() == y.error();
|
|
return false;
|
|
}
|
|
|
|
template<class E>
|
|
class unexpected {
|
|
public:
|
|
// constructors
|
|
constexpr unexpected(const unexpected&) = default;
|
|
constexpr unexpected(unexpected&&) noexcept(std::is_nothrow_move_constructible_v<E>) = default;
|
|
|
|
template <class Err = E _ENABLE_IF(
|
|
std::is_constructible_v<E, Err> &&
|
|
!std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, std::in_place_t> &&
|
|
!std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, unexpected>)>
|
|
// NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload)
|
|
constexpr unexpected(Err&& e) : val_(std::forward<Err>(e)) {}
|
|
|
|
template<class U, class... Args _ENABLE_IF(
|
|
std::is_constructible_v<E, std::initializer_list<U>&, Args...>
|
|
)>
|
|
constexpr explicit unexpected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
|
|
: val_(il, std::forward<Args>(args)...) {}
|
|
|
|
template<class Err _ENABLE_IF(
|
|
std::is_constructible_v<E, Err> &&
|
|
!std::is_constructible_v<E, unexpected<Err>&> &&
|
|
!std::is_constructible_v<E, unexpected<Err>> &&
|
|
!std::is_constructible_v<E, const unexpected<Err>&> &&
|
|
!std::is_constructible_v<E, const unexpected<Err>> &&
|
|
!std::is_convertible_v<unexpected<Err>&, E> &&
|
|
!std::is_convertible_v<unexpected<Err>, E> &&
|
|
!std::is_convertible_v<const unexpected<Err>&, E> &&
|
|
!std::is_convertible_v<const unexpected<Err>, E> &&
|
|
std::is_convertible_v<Err, E> /* non-explicit */
|
|
)>
|
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
constexpr unexpected(const unexpected<Err>& rhs)
|
|
: val_(rhs.value()) {}
|
|
|
|
template<class Err _ENABLE_IF(
|
|
std::is_constructible_v<E, Err> &&
|
|
!std::is_constructible_v<E, unexpected<Err>&> &&
|
|
!std::is_constructible_v<E, unexpected<Err>> &&
|
|
!std::is_constructible_v<E, const unexpected<Err>&> &&
|
|
!std::is_constructible_v<E, const unexpected<Err>> &&
|
|
!std::is_convertible_v<unexpected<Err>&, E> &&
|
|
!std::is_convertible_v<unexpected<Err>, E> &&
|
|
!std::is_convertible_v<const unexpected<Err>&, E> &&
|
|
!std::is_convertible_v<const unexpected<Err>, E> &&
|
|
!std::is_convertible_v<Err, E> /* explicit */
|
|
)>
|
|
constexpr explicit unexpected(const unexpected<Err>& rhs)
|
|
: val_(E(rhs.value())) {}
|
|
|
|
template<class Err _ENABLE_IF(
|
|
std::is_constructible_v<E, Err> &&
|
|
!std::is_constructible_v<E, unexpected<Err>&> &&
|
|
!std::is_constructible_v<E, unexpected<Err>> &&
|
|
!std::is_constructible_v<E, const unexpected<Err>&> &&
|
|
!std::is_constructible_v<E, const unexpected<Err>> &&
|
|
!std::is_convertible_v<unexpected<Err>&, E> &&
|
|
!std::is_convertible_v<unexpected<Err>, E> &&
|
|
!std::is_convertible_v<const unexpected<Err>&, E> &&
|
|
!std::is_convertible_v<const unexpected<Err>, E> &&
|
|
std::is_convertible_v<Err, E> /* non-explicit */
|
|
)>
|
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
constexpr unexpected(unexpected<Err>&& rhs)
|
|
: val_(std::move(rhs.value())) {}
|
|
|
|
template<class Err _ENABLE_IF(
|
|
std::is_constructible_v<E, Err> &&
|
|
!std::is_constructible_v<E, unexpected<Err>&> &&
|
|
!std::is_constructible_v<E, unexpected<Err>> &&
|
|
!std::is_constructible_v<E, const unexpected<Err>&> &&
|
|
!std::is_constructible_v<E, const unexpected<Err>> &&
|
|
!std::is_convertible_v<unexpected<Err>&, E> &&
|
|
!std::is_convertible_v<unexpected<Err>, E> &&
|
|
!std::is_convertible_v<const unexpected<Err>&, E> &&
|
|
!std::is_convertible_v<const unexpected<Err>, E> &&
|
|
!std::is_convertible_v<Err, E> /* explicit */
|
|
)>
|
|
constexpr explicit unexpected(unexpected<Err>&& 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<E>) =
|
|
default;
|
|
template<class Err = E>
|
|
constexpr unexpected& operator=(const unexpected<Err>& rhs) {
|
|
val_ = rhs.value();
|
|
return *this;
|
|
}
|
|
template<class Err = E>
|
|
constexpr unexpected& operator=(unexpected<Err>&& rhs) {
|
|
val_ = std::forward<E>(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<E>) {
|
|
std::swap(val_, other.val_);
|
|
}
|
|
|
|
template<class E1, class E2>
|
|
friend constexpr bool
|
|
operator==(const unexpected<E1>& e1, const unexpected<E2>& e2);
|
|
template<class E1, class E2>
|
|
friend constexpr bool
|
|
operator!=(const unexpected<E1>& e1, const unexpected<E2>& e2);
|
|
|
|
template<class E1>
|
|
friend void swap(unexpected<E1>& x, unexpected<E1>& y) noexcept(noexcept(x.swap(y)));
|
|
|
|
private:
|
|
E val_;
|
|
};
|
|
|
|
template<class E1, class E2>
|
|
constexpr bool
|
|
operator==(const unexpected<E1>& e1, const unexpected<E2>& e2) {
|
|
return e1.value() == e2.value();
|
|
}
|
|
|
|
template<class E1, class E2>
|
|
constexpr bool
|
|
operator!=(const unexpected<E1>& e1, const unexpected<E2>& e2) {
|
|
return e1.value() != e2.value();
|
|
}
|
|
|
|
template<class E1>
|
|
void swap(unexpected<E1>& x, unexpected<E1>& y) noexcept(noexcept(x.swap(y))) {
|
|
x.swap(y);
|
|
}
|
|
|
|
// TODO: bad_expected_access class
|
|
|
|
#undef _ENABLE_IF
|
|
#undef _NODISCARD_
|
|
|
|
} // namespace base
|
|
} // namespace android
|