blob: fe97010dae065adcb7c32fff15a2630c8b52e079 [file] [log] [blame]
// Copyright 2023 The Pigweed 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.
#pragma once
#include <functional>
#include <optional>
#include <type_traits>
#include <utility>
#include <variant>
namespace pw::internal {
// Helper type trait for removing reference and cv-qualification of a type.
template <class T>
struct remove_cvref {
typedef std::remove_cv_t<std::remove_reference_t<T>> type;
};
// Helper type trait ::type of above struct.
template <class T>
using remove_cvref_t = typename remove_cvref<T>::type;
// Helper type trait for enabling/disabling std::expected constructors.
template <class T, class W>
constexpr bool converts_from_any_cvref =
std::disjunction_v<std::is_constructible<T, W&>,
std::is_convertible<W&, T>,
std::is_constructible<T, W>,
std::is_convertible<W, T>,
std::is_constructible<T, const W&>,
std::is_convertible<const W&, T>,
std::is_constructible<T, const W>,
std::is_convertible<const W, T>>;
// Helper type trait for determining if a type is any specialization of a
// template class.
template <typename T, template <typename...> class Template>
constexpr bool is_specialization = false;
template <template <typename...> class Template, typename... Ts>
constexpr bool is_specialization<Template<Ts...>, Template> = true;
// Polyfill implementaion of std::unexpected.
template <class E>
class unexpected {
public:
constexpr unexpected(const unexpected&) = default;
constexpr unexpected(unexpected&&) = default;
template <class Err = E,
std::enable_if_t<
!std::is_same_v<remove_cvref_t<Err>, unexpected> &&
!std::is_same_v<remove_cvref_t<Err>, std::in_place_t> &&
std::is_constructible_v<E, Err>,
bool> = true>
constexpr explicit unexpected(Err&& e) : unex_(std::forward<Err>(e)) {}
template <class... Args,
std::enable_if_t<std::is_constructible_v<E, Args...>, bool> = true>
constexpr explicit unexpected(std::in_place_t, Args&&... args)
: unex_(std::forward<Args>(args)...) {}
template <
class U,
class... Args,
std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>>,
bool> = true>
constexpr explicit unexpected(std::in_place_t,
std::initializer_list<U> il,
Args&&... args)
: unex_(il, std::forward<Args>(args)...) {}
constexpr unexpected& operator=(const unexpected&) = default;
constexpr unexpected& operator=(unexpected&&) = default;
constexpr const E& error() const& noexcept { return unex_; }
constexpr E& error() & noexcept { return unex_; }
constexpr E&& error() && noexcept { return std::move(unex_); }
constexpr const E&& error() const&& noexcept { return std::move(unex_); }
constexpr void swap(unexpected& other) noexcept(
std::is_nothrow_swappable<E>::value) {
std::swap(unex_, other.unex_);
}
template <class E2>
friend constexpr bool operator==(const unexpected& x,
const unexpected<E2>& y) {
return x.error() == y.error();
}
private:
E unex_;
};
// Polyfill implementation of std::unexpect_t and std::unexpect.
struct unexpect_t {
explicit unexpect_t() = default;
};
inline constexpr unexpect_t unexpect;
template <class T, class E, typename Enable = void>
class expected;
// Polyfill implementation of std::expected.
template <class T, class E>
class expected<T, E, std::enable_if_t<!std::is_void_v<T>>> {
public:
using value_type = T;
using error_type = E;
using unexpected_type = unexpected<E>;
template <class U>
using rebind = expected<U, error_type>;
template <
class Enable = T,
std::enable_if_t<std::is_default_constructible_v<Enable>, bool> = true>
constexpr expected() : contents_(kInPlaceValue) {}
constexpr expected(const expected& rhs) = default;
constexpr expected(expected&& rhs) = default;
template <
class U,
class G,
// Constraints
std::enable_if_t<
std::is_constructible_v<T, const U&> &&
std::is_constructible_v<E, const G&> &&
!converts_from_any_cvref<T, expected<U, G>> &&
!std::is_constructible_v<unexpected<E>, expected<U, G>&> &&
!std::is_constructible_v<unexpected<E>, expected<U, G>> &&
!std::is_constructible_v<unexpected<E>, const expected<U, G>&> &&
!std::is_constructible_v<unexpected<E>, const expected<U, G>>,
bool> = true,
// Explicit
std::enable_if_t<!std::is_convertible_v<const U&, T> ||
!std::is_convertible_v<const G&, E>,
bool> = true>
constexpr explicit expected(const expected<U, G>& rhs)
: contents_(convert_variant(
std::forward<const std::variant<U, G>&>(rhs.contents_))) {}
template <
class U,
class G,
// Constraints
std::enable_if_t<
std::is_constructible_v<T, const U&> &&
std::is_constructible_v<E, const G&> &&
!converts_from_any_cvref<T, expected<U, G>> &&
!std::is_constructible_v<unexpected<E>, expected<U, G>&> &&
!std::is_constructible_v<unexpected<E>, expected<U, G>> &&
!std::is_constructible_v<unexpected<E>, const expected<U, G>&> &&
!std::is_constructible_v<unexpected<E>, const expected<U, G>>,
bool> = true,
// Explicit
std::enable_if_t<std::is_convertible_v<const U&, T> &&
std::is_convertible_v<const G&, E>,
bool> = true>
constexpr /* implicit */ expected(const expected<U, G>& rhs)
: contents_(convert_variant(
std::forward<const std::variant<U, G>&>(rhs.contents_))) {}
template <
class U,
class G,
// Constraints
std::enable_if_t<
std::is_constructible_v<T, U&&> && std::is_constructible_v<E, G&&> &&
!converts_from_any_cvref<T, expected<U, G>> &&
!std::is_constructible_v<unexpected<E>, expected<U, G>&> &&
!std::is_constructible_v<unexpected<E>, expected<U, G>> &&
!std::is_constructible_v<unexpected<E>, const expected<U, G>&> &&
!std::is_constructible_v<unexpected<E>, const expected<U, G>>,
bool> = true,
// Explicit
std::enable_if_t<!std::is_convertible_v<U&&, T> ||
!std::is_convertible_v<G&&, E>,
bool> = true>
constexpr explicit expected(expected<U, G>&& rhs)
: contents_(
std::forward<std::variant<U, G>>(convert_variant(rhs.contents_))) {}
template <
class U,
class G,
// Constraints
std::enable_if_t<
std::is_constructible_v<T, U&&> && std::is_constructible_v<E, G&&> &&
!converts_from_any_cvref<T, expected<U, G>> &&
!std::is_constructible_v<unexpected<E>, expected<U, G>&> &&
!std::is_constructible_v<unexpected<E>, expected<U, G>> &&
!std::is_constructible_v<unexpected<E>, const expected<U, G>&> &&
!std::is_constructible_v<unexpected<E>, const expected<U, G>>,
bool> = true,
// Explicit
std::enable_if_t<std::is_convertible_v<U&&, T> &&
std::is_convertible_v<G&&, E>,
bool> = true>
constexpr /* implicit */ expected(expected<U, G>&& rhs)
: contents_(
convert_variant(std::forward<std::variant<U, G>>(rhs.contents_))) {}
template <
class U = T,
// Constraints
std::enable_if_t<!std::is_same_v<remove_cvref_t<U>, std::in_place_t> &&
!std::is_same_v<remove_cvref_t<U>, expected> &&
!is_specialization<U, unexpected> &&
std::is_constructible_v<T, U>,
bool> = true,
// Explicit
std::enable_if_t<!std::is_convertible_v<U, T>, bool> = true>
constexpr explicit expected(U&& u)
: contents_(kInPlaceValue, std::forward<U>(u)) {}
template <
class U = T,
// Constraints
std::enable_if_t<!std::is_same_v<remove_cvref_t<U>, std::in_place_t> &&
!std::is_same_v<remove_cvref_t<U>, expected> &&
!is_specialization<U, unexpected> &&
std::is_constructible_v<T, U>,
bool> = true,
// Explicit
std::enable_if_t<std::is_convertible_v<U, T>, bool> = true>
constexpr /* implicit */ expected(U&& u)
: contents_(kInPlaceValue, std::forward<U>(u)) {}
template <class G,
// Constraints
std::enable_if_t<std::is_constructible_v<E, const G&>, bool> = true,
// Explicit
std::enable_if_t<!std::is_convertible_v<const G&, E>, bool> = true>
constexpr explicit expected(const unexpected<G>& e)
: contents_(kInPlaceError, std::forward<const G&>(e.error())) {}
template <class G,
// Constraints
std::enable_if_t<std::is_constructible_v<E, const G&>, bool> = true,
// Explicit
std::enable_if_t<std::is_convertible_v<const G&, E>, bool> = true>
constexpr /* implicit */ expected(const unexpected<G>& e)
: contents_(kInPlaceError, std::forward<const G&>(e.error())) {}
template <class G,
// Constraints
std::enable_if_t<std::is_constructible_v<E, G>, bool> = true,
// Explicit
std::enable_if_t<!std::is_convertible_v<G, E>, bool> = true>
constexpr explicit expected(unexpected<G>&& e)
: contents_(kInPlaceError, std::forward<G>(e.error())) {}
template <class G,
// Constraints
std::enable_if_t<std::is_constructible_v<E, G>, bool> = true,
// Explicit
std::enable_if_t<std::is_convertible_v<G, E>, bool> = true>
constexpr /* implicit */ expected(unexpected<G>&& e)
: contents_(kInPlaceError, std::forward<G>(e.error())) {}
template <class... Args,
std::enable_if_t<std::is_constructible_v<T, Args...>, bool> = true>
constexpr explicit expected(std::in_place_t, Args&&... args)
: contents_(kInPlaceValue, std::forward<Args>(args)...) {}
template <class U,
class... Args,
std::enable_if_t<
std::is_constructible_v<T, std::initializer_list<U>&, Args...>,
bool> = true>
constexpr explicit expected(std::in_place_t,
std::initializer_list<U> il,
Args&&... args)
: contents_(kInPlaceValue, il, std::forward<Args>(args)...) {}
template <class... Args,
std::enable_if_t<std::is_constructible_v<E, Args...>, bool> = true>
constexpr explicit expected(unexpect_t, Args&&... args)
: contents_(kInPlaceError, std::forward<Args>(args)...) {}
template <class U,
class... Args,
std::enable_if_t<
std::is_constructible_v<E, std::initializer_list<U>&, Args...>,
bool> = true>
constexpr explicit expected(unexpect_t,
std::initializer_list<U> il,
Args&&... args)
: contents_(kInPlaceError, il, std::forward<Args>(args)...) {}
constexpr expected& operator=(const expected& rhs) = default;
constexpr expected& operator=(expected&& rhs) = default;
template <
class U = T,
std::enable_if_t<!std::is_same_v<expected, remove_cvref_t<U>> &&
!is_specialization<remove_cvref_t<U>, unexpected>,
bool> = true>
constexpr expected& operator=(U&& u) {
value() = std::forward<U>(u);
}
template <class G>
constexpr expected& operator=(const unexpected<G>& e) {
error() = std::forward<const G&>(e.error());
}
template <class G>
constexpr expected& operator=(unexpected<G>&& e) {
error() = std::forward<G>(e.error());
}
template <class... Args,
std::enable_if_t<std::is_nothrow_constructible_v<T, Args...>,
bool> = true>
constexpr T& emplace(Args&&... args) noexcept {
return contents_.template emplace<kValue>(std::forward<Args>(args)...);
}
template <
class U,
class... Args,
std::enable_if_t<
std::is_nothrow_constructible_v<T, std::initializer_list<U>, Args...>,
bool> = true>
constexpr T& emplace(std::initializer_list<U> il, Args&&... args) noexcept {
return contents_.template emplace<kValue>(il, std::forward<Args>(args)...);
}
constexpr void swap(expected& rhs) { std::swap(contents_, rhs.contents_); }
constexpr T* operator->() noexcept { return std::addressof(value()); }
constexpr const T* operator->() const noexcept {
return std::addressof(value());
}
constexpr T& operator*() & noexcept { return value(); }
constexpr T&& operator*() && noexcept { return std::move(value()); }
constexpr const T& operator*() const& noexcept { return value(); }
constexpr const T&& operator*() const&& noexcept {
return std::move(value());
}
constexpr explicit operator bool() const noexcept { return has_value(); }
constexpr bool has_value() const noexcept {
return contents_.index() == kValue;
}
constexpr T& value() & { return std::get<kValue>(contents_); }
constexpr T&& value() && { return std::move(std::get<kValue>(contents_)); }
constexpr const T& value() const& { return std::get<kValue>(contents_); }
constexpr const T&& value() const&& {
return std::move(std::get<kValue>(contents_));
}
constexpr E& error() & { return std::get<kError>(contents_); }
constexpr E&& error() && { return std::move(std::get<kError>(contents_)); }
constexpr const E& error() const& { return std::get<kError>(contents_); }
constexpr const E&& error() const&& {
return std::move(std::get<kError>(contents_));
}
template <class U>
constexpr T value_or(U&& u) && {
return has_value() ? std::move(value())
: static_cast<T>(std::forward<U>(u));
}
template <class U>
constexpr T value_or(U&& u) const& {
return has_value() ? value() : static_cast<T>(std::forward<U>(u));
}
template <class G>
constexpr E error_or(G&& g) && {
return has_value() ? std::forward<G>(g) : std::move(error());
}
template <class G>
constexpr E error_or(G&& g) const& {
return has_value() ? std::forward<G>(g) : error();
}
template <class F>
constexpr auto and_then(F&& f) & {
using U = remove_cvref_t<std::invoke_result_t<F, decltype(value())>>;
if (has_value()) {
return std::invoke(std::forward<F>(f), value());
} else {
return U(unexpect, error());
}
}
template <class F>
constexpr auto and_then(F&& f) && {
using U = remove_cvref_t<std::invoke_result_t<F, decltype(value())>>;
if (has_value()) {
return std::invoke(std::forward<F>(f), std::move(value()));
} else {
return U(unexpect, std::move(error()));
}
}
template <class F>
constexpr auto and_then(F&& f) const& {
using U = remove_cvref_t<std::invoke_result_t<F, decltype(value())>>;
if (has_value()) {
return std::invoke(std::forward<F>(f), value());
} else {
return U(unexpect, error());
}
}
template <class F>
constexpr auto and_then(F&& f) const&& {
using U = remove_cvref_t<std::invoke_result_t<F, decltype(value())>>;
if (has_value()) {
return std::invoke(std::forward<F>(f), std::move(value()));
} else {
return U(unexpect, std::move(error()));
}
}
template <class F>
constexpr auto or_else(F&& f) & {
using G = remove_cvref_t<std::invoke_result_t<F, decltype(error())>>;
if (has_value()) {
return G(std::in_place, value());
} else {
return std::invoke(std::forward<F>(f), error());
}
}
template <class F>
constexpr auto or_else(F&& f) && {
using G = remove_cvref_t<std::invoke_result_t<F, decltype(error())>>;
if (has_value()) {
return G(std::in_place, value());
} else {
return std::invoke(std::forward<F>(f), std::move(error()));
}
}
template <class F>
constexpr auto or_else(F&& f) const& {
using G = remove_cvref_t<std::invoke_result_t<F, decltype(error())>>;
if (has_value()) {
return G(std::in_place, value());
} else {
return std::invoke(std::forward<F>(f), error());
}
}
template <class F>
constexpr auto or_else(F&& f) const&& {
using G = remove_cvref_t<std::invoke_result_t<F, decltype(error())>>;
if (has_value()) {
return G(std::in_place, value());
} else {
return std::invoke(std::forward<F>(f), std::move(error()));
}
}
template <class F>
constexpr auto transform(F&& f) & {
using U = std::remove_cv_t<std::invoke_result_t<F, decltype(value())>>;
if (has_value()) {
return transform_helper<U>(std::forward<F>(f));
} else {
return expected<U, E>(unexpect, error());
}
}
template <class F>
constexpr auto transform(F&& f) && {
using U = std::remove_cv_t<std::invoke_result_t<F, decltype(value())>>;
if (has_value()) {
return transform_helper<U>(std::forward<F>(f));
} else {
return expected<U, E>(unexpect, std::move(error()));
}
}
template <class F>
constexpr auto transform(F&& f) const& {
using U = std::remove_cv_t<std::invoke_result_t<F, decltype(value())>>;
if (has_value()) {
return transform_helper<U>(std::forward<F>(f));
} else {
return expected<U, E>(unexpect, error());
}
}
template <class F>
constexpr auto transform(F&& f) const&& {
using U = std::remove_cv_t<std::invoke_result_t<F, decltype(value())>>;
if (has_value()) {
return transform_helper<U>(std::forward<F>(f));
} else {
return expected<U, E>(unexpect, std::move(error()));
}
}
template <class F>
constexpr auto transform_error(F&& f) & {
using G = std::remove_cv_t<std::invoke_result_t<F, decltype(error())>>;
if (has_value()) {
return expected<T, G>(std::in_place, value());
} else {
return expected<T, G>(unexpect, std::invoke(std::forward<F>(f), error()));
}
}
template <class F>
constexpr auto transform_error(F&& f) && {
using G = std::remove_cv_t<std::invoke_result_t<F, decltype(error())>>;
if (has_value()) {
return expected<T, G>(std::in_place, std::move(value()));
} else {
return expected<T, G>(
unexpect, std::invoke(std::forward<F>(f), std::move(error())));
}
}
template <class F>
constexpr auto transform_error(F&& f) const& {
using G = std::remove_cv_t<std::invoke_result_t<F, decltype(error())>>;
if (has_value()) {
return expected<T, G>(std::in_place, value());
} else {
return expected<T, G>(unexpect, std::invoke(std::forward<F>(f), error()));
}
}
template <class F>
constexpr auto transform_error(F&& f) const&& {
using G = std::remove_cv_t<std::invoke_result_t<F, decltype(error())>>;
if (has_value()) {
return expected<T, G>(std::in_place, std::move(value()));
} else {
return expected<T, G>(
unexpect, std::invoke(std::forward<F>(f), std::move(error())));
}
}
private:
// Make all specializations of `expected` friends.
template <class U, class G, class>
friend class expected;
static constexpr size_t kValue = 0;
static constexpr size_t kError = 1;
static constexpr auto kInPlaceValue = std::in_place_index<kValue>;
static constexpr auto kInPlaceError = std::in_place_index<kError>;
// Helper to convert variant<U, G> -> variant<T, E>
template <class U, class G>
std::variant<T, E> convert_variant(const std::variant<U, G>& v) {
switch (v.index()) {
case kValue:
return std::variant<T, E>(kInPlaceValue, std::get<kValue>(v));
case kError:
return std::variant<T, E>(kInPlaceError, std::get<kError>(v));
default:
// Could only happen if valueless_by_exception, which can't be handled
// gracefully anyways.
std::abort();
}
}
// Helper to convert variant<U, G> -> variant<T, E>
template <class U, class G>
std::variant<T, E> convert_variant(std::variant<U, G>&& v) {
switch (v.index()) {
case kValue:
return std::variant<T, E>(kInPlaceValue,
std::forward<U>(std::get<kValue>(v)));
case kError:
return std::variant<T, E>(kInPlaceError,
std::forward<G>(std::get<kError>(v)));
default:
// Could only happen if valueless_by_exception, which can't be handled
// gracefully anyways.
std::abort();
}
}
// Helper to handle transform correctly for void(Args...) functions, non-void
// case.
template <class U, class F, std::enable_if_t<!std::is_void_v<U>, bool> = true>
expected<U, E> transform_helper(F&& f) & {
return expected<U, E>(std::in_place,
std::invoke(std::forward<F>(f), value()));
}
template <class U, class F, std::enable_if_t<!std::is_void_v<U>, bool> = true>
expected<U, E> transform_helper(F&& f) && {
return expected<U, E>(std::in_place,
std::invoke(std::forward<F>(f), std::move(value())));
}
template <class U, class F, std::enable_if_t<!std::is_void_v<U>, bool> = true>
expected<U, E> transform_helper(F&& f) const& {
return expected<U, E>(std::in_place,
std::invoke(std::forward<F>(f), value()));
}
template <class U, class F, std::enable_if_t<!std::is_void_v<U>, bool> = true>
expected<U, E> transform_helper(F&& f) const&& {
return expected<U, E>(std::in_place,
std::invoke(std::forward<F>(f), std::move(value())));
}
// Helper to handle transform correctly for void(Args...) functions, void
// case.
template <class U, class F, std::enable_if_t<std::is_void_v<U>, bool> = true>
expected<U, E> transform_helper(F&& f) & {
std::invoke(std::forward<F>(f), value());
return expected<U, E>();
}
template <class U, class F, std::enable_if_t<std::is_void_v<U>, bool> = true>
expected<U, E> transform_helper(F&& f) && {
std::invoke(std::forward<F>(f), std::move(value()));
return expected<U, E>();
}
template <class U, class F, std::enable_if_t<std::is_void_v<U>, bool> = true>
expected<U, E> transform_helper(F&& f) const& {
std::invoke(std::forward<F>(f), value());
return expected<U, E>();
}
template <class U, class F, std::enable_if_t<std::is_void_v<U>, bool> = true>
expected<U, E> transform_helper(F&& f) const&& {
std::invoke(std::forward<F>(f), std::move(value()));
return expected<U, E>();
}
std::variant<T, E> contents_;
};
template <class T,
class E,
class U,
class G,
std::enable_if_t<!std::is_void_v<U>, bool> = true>
constexpr bool operator==(const expected<T, E>& lhs,
const expected<U, G>& rhs) {
if (lhs.has_value() != rhs.has_value()) {
return false;
}
if (lhs.has_value()) {
return lhs.value() == rhs.value();
} else {
return lhs.error() == rhs.error();
}
}
template <class T, class E, class U>
constexpr bool operator==(const expected<T, E>& x, const U& u) {
return x.has_value() && static_cast<bool>(*x == u);
}
template <class T, class E, class G>
constexpr bool operator==(const expected<T, E>& x, const unexpected<G> e) {
return !x.has_value() && static_cast<bool>(x.error() == e.error());
}
// Polyfill implementation of std::expected<void, ...>
template <class T, class E>
class expected<T, E, std::enable_if_t<std::is_void_v<T>>> {
public:
using value_type = T;
using error_type = E;
using unexpected_type = unexpected<E>;
template <class U>
using rebind = expected<U, error_type>;
constexpr expected() noexcept : error_contents_(std::nullopt) {}
constexpr expected(const expected& rhs) = default;
constexpr expected(expected&& rhs) = default;
template <
class U,
class G,
// Constraints
std::enable_if_t<
std::is_void_v<U> && std::is_constructible_v<E, const G&> &&
!std::is_constructible_v<unexpected<E>, expected<U, G>&> &&
!std::is_constructible_v<unexpected<E>, expected<U, G>> &&
!std::is_constructible_v<unexpected<E>, const expected<U, G>&> &&
!std::is_constructible_v<unexpected<E>, const expected<U, G>>,
bool> = true,
// Explicit
std::enable_if_t<std::is_convertible_v<const G&, E>, bool> = true>
constexpr /* implicit */ expected(const expected<U, G>& rhs)
: error_contents_(rhs.error_contents_.has_value() ? rhs.error_contents_
: std::nullopt) {}
template <
class U,
class G,
// Constraints
std::enable_if_t<
std::is_void_v<U> && std::is_constructible_v<E, const G&> &&
!std::is_constructible_v<unexpected<E>, expected<U, G>&> &&
!std::is_constructible_v<unexpected<E>, expected<U, G>> &&
!std::is_constructible_v<unexpected<E>, const expected<U, G>&> &&
!std::is_constructible_v<unexpected<E>, const expected<U, G>>,
bool> = true,
// Explicit
std::enable_if_t<!std::is_convertible_v<const G&, E>, bool> = true>
constexpr explicit expected(const expected<U, G>& rhs)
: error_contents_(rhs.error_contents_.has_value() ? rhs.error_contents_
: std::nullopt) {}
template <
class U,
class G,
// Constraints
std::enable_if_t<
std::is_void_v<U> && std::is_constructible_v<E, G> &&
!std::is_constructible_v<unexpected<E>, expected<U, G>&> &&
!std::is_constructible_v<unexpected<E>, expected<U, G>> &&
!std::is_constructible_v<unexpected<E>, const expected<U, G>&> &&
!std::is_constructible_v<unexpected<E>, const expected<U, G>>,
bool> = true,
// Explicit
std::enable_if_t<std::is_convertible_v<G, E>, bool> = true>
constexpr /* implicit */ expected(expected<U, G>&& rhs)
: error_contents_(rhs.error_contents_.has_value()
? std::move(rhs.error_contents_)
: std::nullopt) {}
template <
class U,
class G,
// Constraints
std::enable_if_t<
std::is_void_v<U> && std::is_constructible_v<E, G> &&
!std::is_constructible_v<unexpected<E>, expected<U, G>&> &&
!std::is_constructible_v<unexpected<E>, expected<U, G>> &&
!std::is_constructible_v<unexpected<E>, const expected<U, G>&> &&
!std::is_constructible_v<unexpected<E>, const expected<U, G>>,
bool> = true,
// Explicit
std::enable_if_t<!std::is_convertible_v<G, E>, bool> = true>
constexpr explicit expected(expected<U, G>&& rhs)
: error_contents_(rhs.error_contents_.has_value()
? std::move(rhs.error_contents_)
: std::nullopt) {}
template <class G,
// Constraints
std::enable_if_t<std::is_constructible_v<E, const G&>, bool> = true,
// Explicit
std::enable_if_t<!std::is_convertible_v<const G&, E>, bool> = true>
constexpr explicit expected(const unexpected<G>& e)
: error_contents_(std::in_place, std::forward<const G&>(e.error())) {}
template <class G,
// Constraints
std::enable_if_t<std::is_constructible_v<E, const G&>, bool> = true,
// Explicit
std::enable_if_t<std::is_convertible_v<const G&, E>, bool> = true>
constexpr /* implicit */ expected(const unexpected<G>& e)
: error_contents_(std::in_place, std::forward<const G&>(e.error())) {}
template <class... Args,
std::enable_if_t<std::is_constructible_v<E, Args...>, bool> = true>
constexpr explicit expected(unexpect_t, Args&&... args)
: error_contents_(std::in_place, std::forward<Args>(args)...) {}
template <class U,
class... Args,
std::enable_if_t<
std::is_constructible_v<E, std::initializer_list<U>&, Args...>,
bool> = true>
constexpr explicit expected(unexpect_t,
std::initializer_list<U> il,
Args&&... args)
: error_contents_(std::in_place, il, std::forward<Args>(args)...) {}
template <class G,
// Constraints
std::enable_if_t<std::is_constructible_v<E, G>, bool> = true,
// Explicit
std::enable_if_t<!std::is_convertible_v<G, E>, bool> = true>
constexpr explicit expected(unexpected<G>&& e)
: error_contents_(std::in_place, std::forward<G>(e.error())) {}
template <class G,
// Constraints
std::enable_if_t<std::is_constructible_v<E, G>, bool> = true,
// Explicit
std::enable_if_t<std::is_convertible_v<G, E>, bool> = true>
constexpr /* implicit */ expected(unexpected<G>&& e)
: error_contents_(std::in_place, std::forward<G>(e.error())) {}
constexpr expected& operator=(const expected& rhs) {
error_contents_ = rhs.error_contents_;
return *this;
}
constexpr expected& operator=(expected&& rhs) noexcept(
std::is_nothrow_move_constructible_v<E> &&
std::is_nothrow_move_assignable_v<E>) {
error_contents_ = std::move(rhs.error_contents);
return *this;
}
template <class G>
constexpr expected& operator=(const unexpected<G>& rhs) {
error_contents_ = rhs.error();
return *this;
}
template <class G>
constexpr expected& operator=(unexpected<G>&& rhs) {
error_contents_ = std::move(rhs.error());
return *this;
}
constexpr void swap(expected& rhs) {
error_contents_.swap(rhs.error_contents_);
}
constexpr explicit operator bool() const noexcept { return has_value(); }
constexpr bool has_value() const noexcept {
return !error_contents_.has_value();
}
constexpr void operator*() const noexcept {}
constexpr void value() const& {}
constexpr void value() && {}
constexpr E& error() & { return *error_contents_; }
constexpr E&& error() && { return std::move(*error_contents_); }
constexpr const E& error() const& { return *error_contents_; }
constexpr const E&& error() const&& { return std::move(*error_contents_); }
template <class G = E>
constexpr E error_or(G&& g) const& {
if (has_value()) {
return std::forward<G>(g);
} else {
return error();
}
}
template <class G = E>
constexpr E error_or(G&& g) const&& {
if (has_value()) {
return std::forward<G>(g);
} else {
return std::move(error());
}
}
template <class F>
constexpr auto and_then(F&& f) & {
using U = remove_cvref_t<std::invoke_result_t<F>>;
if (has_value()) {
return std::invoke(std::forward<F>(f));
} else {
return U(unexpect, error());
}
}
template <class F>
constexpr auto and_then(F&& f) && {
using U = remove_cvref_t<std::invoke_result_t<F>>;
if (has_value()) {
return std::invoke(std::forward<F>(f));
} else {
return U(unexpect, std::move(error()));
}
}
template <class F>
constexpr auto and_then(F&& f) const& {
using U = remove_cvref_t<std::invoke_result_t<F>>;
if (has_value()) {
return std::invoke(std::forward<F>(f));
} else {
return U(unexpect, error());
}
}
template <class F>
constexpr auto and_then(F&& f) const&& {
using U = remove_cvref_t<std::invoke_result_t<F>>;
if (has_value()) {
return std::invoke(std::forward<F>(f));
} else {
return U(unexpect, std::move(error()));
}
}
template <class F>
constexpr auto or_else(F&& f) & {
using G = remove_cvref_t<std::invoke_result_t<F, decltype(error())>>;
if (has_value()) {
return G();
} else {
return std::invoke(std::forward<F>(f), error());
}
}
template <class F>
constexpr auto or_else(F&& f) && {
using G = remove_cvref_t<std::invoke_result_t<F, decltype(error())>>;
if (has_value()) {
return G();
} else {
return std::invoke(std::forward<F>(f), std::move(error()));
}
}
template <class F>
constexpr auto or_else(F&& f) const& {
using G = remove_cvref_t<std::invoke_result_t<F, decltype(error())>>;
if (has_value()) {
return G();
} else {
return std::invoke(std::forward<F>(f), error());
}
}
template <class F>
constexpr auto or_else(F&& f) const&& {
using G = remove_cvref_t<std::invoke_result_t<F, decltype(error())>>;
if (has_value()) {
return G();
} else {
return std::invoke(std::forward<F>(f), std::move(error()));
}
}
template <class F>
constexpr auto transform(F&& f) & {
using U = std::remove_cv_t<std::invoke_result_t<F>>;
if (has_value()) {
return transform_helper<U>(std::forward<F>(f));
} else {
return expected<U, E>(unexpect, error());
}
}
template <class F>
constexpr auto transform(F&& f) && {
using U = std::remove_cv_t<std::invoke_result_t<F>>;
if (has_value()) {
return transform_helper<U>(std::forward<F>(f));
} else {
return expected<U, E>(unexpect, std::move(error()));
}
}
template <class F>
constexpr auto transform(F&& f) const& {
using U = std::remove_cv_t<std::invoke_result_t<F>>;
if (has_value()) {
return transform_helper<U>(std::forward<F>(f));
} else {
return expected<U, E>(unexpect, error());
}
}
template <class F>
constexpr auto transform(F&& f) const&& {
using U = std::remove_cv_t<std::invoke_result_t<F>>;
if (has_value()) {
return transform_helper<U>(std::forward<F>(f));
} else {
return expected<U, E>(unexpect, std::move(error()));
}
}
template <class F>
constexpr auto transform_error(F&& f) & {
using G = std::remove_cv_t<std::invoke_result_t<F, decltype(error())>>;
if (has_value()) {
return expected<T, G>();
} else {
return expected<T, G>(unexpect, std::invoke(std::forward<F>(f), error()));
}
}
template <class F>
constexpr auto transform_error(F&& f) && {
using G = std::remove_cv_t<std::invoke_result_t<F, decltype(error())>>;
if (has_value()) {
return expected<T, G>();
} else {
return expected<T, G>(
unexpect, std::invoke(std::forward<F>(f), std::move(error())));
}
}
template <class F>
constexpr auto transform_error(F&& f) const& {
using G = std::remove_cv_t<std::invoke_result_t<F, decltype(error())>>;
if (has_value()) {
return expected<T, G>();
} else {
return expected<T, G>(unexpect, std::invoke(std::forward<F>(f), error()));
}
}
template <class F>
constexpr auto transform_error(F&& f) const&& {
using G = std::remove_cv_t<std::invoke_result_t<F, decltype(error())>>;
if (has_value()) {
return expected<T, G>();
} else {
return expected<T, G>(
unexpect, std::invoke(std::forward<F>(f), std::move(error())));
}
}
private:
// Make all specializations of `expected` friends.
template <class U, class G, class>
friend class expected;
// Helper to handle transform correctly for void(Args...) functions, non-void
// case.
template <class U, class F, std::enable_if_t<!std::is_void_v<U>, bool> = true>
expected<U, E> transform_helper(F&& f) const {
return expected<U, E>(std::in_place, std::invoke(std::forward<F>(f)));
}
// Helper to handle transform correctly for void(Args...) functions, void
// case.
template <class U, class F, std::enable_if_t<std::is_void_v<U>, bool> = true>
expected<U, E> transform_helper(F&& f) const {
std::invoke(std::forward<F>(f));
return expected<U, E>();
}
std::optional<E> error_contents_;
};
template <class T,
class E,
class U,
class G,
std::enable_if_t<std::is_void_v<U>, bool> = true>
constexpr bool operator==(const expected<T, E>& lhs,
const expected<U, G>& rhs) {
if (lhs.has_value() != rhs.has_value())
return false;
return lhs.has_value() || static_cast<bool>(lhs.error() == rhs.error());
}
template <class T, class E, class G>
constexpr bool operator==(const expected<T, E>& lhs, const unexpected<G>& rhs) {
return !lhs.has_value() && static_cast<bool>(lhs.error() == rhs.error());
}
} // namespace pw::internal