forked from TartanLlama/expected
Tag dispatch swap
This commit is contained in:
187
tl/expected.hpp
187
tl/expected.hpp
@@ -71,30 +71,31 @@
|
|||||||
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
|
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
|
||||||
std::is_trivially_destructible<T>
|
std::is_trivially_destructible<T>
|
||||||
|
|
||||||
// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks std::vector
|
// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks
|
||||||
// for non-copyable types
|
// std::vector for non-copyable types
|
||||||
#elif (defined(__GNUC__) && __GNUC__ < 8 && \
|
#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
|
||||||
!defined(__clang__))
|
|
||||||
#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
|
#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
|
||||||
#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
|
#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
|
||||||
namespace tl {
|
namespace tl {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
template<class T>
|
template <class T>
|
||||||
struct is_trivially_copy_constructible : std::is_trivially_copy_constructible<T>{};
|
struct is_trivially_copy_constructible
|
||||||
|
: std::is_trivially_copy_constructible<T> {};
|
||||||
#ifdef _GLIBCXX_VECTOR
|
#ifdef _GLIBCXX_VECTOR
|
||||||
template<class T, class A>
|
template <class T, class A>
|
||||||
struct is_trivially_copy_constructible<std::vector<T,A>>
|
struct is_trivially_copy_constructible<std::vector<T, A>>
|
||||||
: std::is_trivially_copy_constructible<T>{};
|
: std::is_trivially_copy_constructible<T> {};
|
||||||
#endif
|
#endif
|
||||||
}
|
} // namespace detail
|
||||||
}
|
} // namespace tl
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
|
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
|
||||||
tl::detail::is_trivially_copy_constructible<T>
|
tl::detail::is_trivially_copy_constructible<T>
|
||||||
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
|
#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
|
||||||
std::is_trivially_copy_assignable<T>
|
std::is_trivially_copy_assignable<T>
|
||||||
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible<T>
|
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
|
||||||
|
std::is_trivially_destructible<T>
|
||||||
#else
|
#else
|
||||||
/// \exclude
|
/// \exclude
|
||||||
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
|
#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
|
||||||
@@ -219,7 +220,7 @@ static constexpr unexpect_t unexpect{};
|
|||||||
|
|
||||||
/// \exclude
|
/// \exclude
|
||||||
namespace detail {
|
namespace detail {
|
||||||
template<typename E>
|
template <typename E>
|
||||||
[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) {
|
[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) {
|
||||||
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
|
#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
|
||||||
throw std::forward<E>(e);
|
throw std::forward<E>(e);
|
||||||
@@ -235,7 +236,8 @@ template<typename E>
|
|||||||
#ifndef TL_TRAITS_MUTEX
|
#ifndef TL_TRAITS_MUTEX
|
||||||
#define TL_TRAITS_MUTEX
|
#define TL_TRAITS_MUTEX
|
||||||
// C++14-style aliases for brevity
|
// C++14-style aliases for brevity
|
||||||
template <class T> using remove_const_t = typename std::remove_const<T>::type;
|
template <class T>
|
||||||
|
using remove_const_t = typename std::remove_const<T>::type;
|
||||||
template <class T>
|
template <class T>
|
||||||
using remove_reference_t = typename std::remove_reference<T>::type;
|
using remove_reference_t = typename std::remove_reference<T>::type;
|
||||||
template <class T> using decay_t = typename std::decay<T>::type;
|
template <class T> using decay_t = typename std::decay<T>::type;
|
||||||
@@ -275,9 +277,11 @@ template <class F, class, class... Us> struct invoke_result_impl;
|
|||||||
|
|
||||||
template <class F, class... Us>
|
template <class F, class... Us>
|
||||||
struct invoke_result_impl<
|
struct invoke_result_impl<
|
||||||
F, decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
|
F,
|
||||||
|
decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
|
||||||
Us...> {
|
Us...> {
|
||||||
using type = decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
|
using type =
|
||||||
|
decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class F, class... Us>
|
template <class F, class... Us>
|
||||||
@@ -398,14 +402,10 @@ using is_move_constructible_or_void =
|
|||||||
is_void_or<T, std::is_move_constructible<T>>;
|
is_void_or<T, std::is_move_constructible<T>>;
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
using is_copy_assignable_or_void =
|
using is_copy_assignable_or_void = is_void_or<T, std::is_copy_assignable<T>>;
|
||||||
is_void_or<T, std::is_copy_assignable<T>>;
|
|
||||||
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
using is_move_assignable_or_void =
|
using is_move_assignable_or_void = is_void_or<T, std::is_move_assignable<T>>;
|
||||||
is_void_or<T, std::is_move_assignable<T>>;
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
@@ -837,9 +837,7 @@ struct expected_operations_base : expected_storage_base<T, E> {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
constexpr void destroy_val() {
|
constexpr void destroy_val() { get().~T(); }
|
||||||
get().~T();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// This base class provides some handy member functions which can be used in
|
// This base class provides some handy member functions which can be used in
|
||||||
@@ -893,7 +891,7 @@ struct expected_operations_base<void, E> : expected_storage_base<void, E> {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
constexpr void destroy_val() {
|
constexpr void destroy_val() {
|
||||||
//no-op
|
// no-op
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1225,7 +1223,9 @@ class expected : private detail::expected_move_assign_base<T, E>,
|
|||||||
T *valptr() { return std::addressof(this->m_val); }
|
T *valptr() { return std::addressof(this->m_val); }
|
||||||
const T *valptr() const { return std::addressof(this->m_val); }
|
const T *valptr() const { return std::addressof(this->m_val); }
|
||||||
unexpected<E> *errptr() { return std::addressof(this->m_unexpect); }
|
unexpected<E> *errptr() { return std::addressof(this->m_unexpect); }
|
||||||
const unexpected<E> *errptr() const { return std::addressof(this->m_unexpect); }
|
const unexpected<E> *errptr() const {
|
||||||
|
return std::addressof(this->m_unexpect);
|
||||||
|
}
|
||||||
|
|
||||||
template <class U = T,
|
template <class U = T,
|
||||||
detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
|
detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
|
||||||
@@ -1381,8 +1381,8 @@ public:
|
|||||||
/// \group map
|
/// \group map
|
||||||
/// \synopsis template <class F> constexpr auto map(F &&f) &&;
|
/// \synopsis template <class F> constexpr auto map(F &&f) &&;
|
||||||
template <class F>
|
template <class F>
|
||||||
TL_EXPECTED_11_CONSTEXPR decltype(
|
TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
|
||||||
expected_map_impl(std::declval<expected>(), std::declval<F &&>()))
|
std::declval<F &&>()))
|
||||||
map(F &&f) && {
|
map(F &&f) && {
|
||||||
return expected_map_impl(std::move(*this), std::forward<F>(f));
|
return expected_map_impl(std::move(*this), std::forward<F>(f));
|
||||||
}
|
}
|
||||||
@@ -1494,8 +1494,8 @@ public:
|
|||||||
/// \requires `F` is invokable with `E`, and `std::invoke_result_t<F>`
|
/// \requires `F` is invokable with `E`, and `std::invoke_result_t<F>`
|
||||||
/// must be void or convertible to `expcted<T,E>`.
|
/// must be void or convertible to `expcted<T,E>`.
|
||||||
/// \effects If `*this` has a value, returns `*this`.
|
/// \effects If `*this` has a value, returns `*this`.
|
||||||
/// Otherwise, if `f` returns `void`, calls `std::forward<F>(f)(E)` and returns
|
/// Otherwise, if `f` returns `void`, calls `std::forward<F>(f)(E)` and
|
||||||
/// `std::nullopt`. Otherwise, returns `std::forward<F>(f)(E)`.
|
/// returns `std::nullopt`. Otherwise, returns `std::forward<F>(f)(E)`.
|
||||||
///
|
///
|
||||||
/// \group or_else
|
/// \group or_else
|
||||||
template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & {
|
template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & {
|
||||||
@@ -1837,51 +1837,66 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class OT = T, class OE = E>
|
private:
|
||||||
detail::enable_if_t<
|
using t_is_void = std::true_type;
|
||||||
detail::is_swappable<OT>::value && detail::is_swappable<OE>::value &&
|
using t_is_not_void = std::false_type;
|
||||||
std::is_nothrow_move_constructible<OT>::value &&
|
using t_is_nothrow_move_constructible = std::true_type;
|
||||||
!std::is_nothrow_move_constructible<OE>::value && !std::is_void<OT>::value>
|
using move_constructing_t_can_throw = std::false_type;
|
||||||
swap(expected &rhs) noexcept(
|
using e_is_nothrow_move_constructible = std::true_type;
|
||||||
detail::is_nothrow_swappable<T>::value
|
using move_constructing_e_can_throw = std::false_type;
|
||||||
&&std::is_nothrow_move_constructible<E>::value
|
|
||||||
&&detail::is_nothrow_swappable<E>::value) {
|
void swap_where_both_have_value(expected &rhs, t_is_void) noexcept {
|
||||||
if (has_value() && rhs.has_value()) {
|
// swapping void is a no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap_where_both_have_value(expected &rhs, t_is_not_void) {
|
||||||
using std::swap;
|
using std::swap;
|
||||||
swap(val(), rhs.val());
|
swap(val(), rhs.val());
|
||||||
} else if (!has_value() && rhs.has_value()) {
|
}
|
||||||
rhs.swap(*this);
|
|
||||||
} else if (has_value()) {
|
void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept (
|
||||||
|
std::is_nothrow_move_constructible<E>::value) {
|
||||||
|
::new (errptr()) unexpected_type(std::move(rhs.err()));
|
||||||
|
rhs.err().~unexpected_type();
|
||||||
|
std::swap(this->m_has_val, rhs.m_has_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap_where_only_one_has_value(expected &rhs, t_is_not_void) {
|
||||||
|
swap_where_only_one_has_value_and_t_is_not_void(
|
||||||
|
rhs, std::is_nothrow_move_constructible<T>::type{},
|
||||||
|
std::is_nothrow_move_constructible<E>::type{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap_where_only_one_has_value_and_t_is_not_void(
|
||||||
|
expected &rhs, t_is_nothrow_move_constructible,
|
||||||
|
e_is_nothrow_move_constructible) noexcept {
|
||||||
|
auto temp = std::move(val());
|
||||||
|
val().~T();
|
||||||
|
::new (errptr()) unexpected_type(std::move(rhs.err()));
|
||||||
|
rhs.err().~unexpected_type();
|
||||||
|
::new (rhs.valptr()) T(std::move(temp));
|
||||||
|
std::swap(this->m_has_val, rhs.m_has_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap_where_only_one_has_value_and_t_is_not_void(
|
||||||
|
expected &rhs, t_is_nothrow_move_constructible,
|
||||||
|
move_constructing_e_can_throw) {
|
||||||
auto temp = std::move(val());
|
auto temp = std::move(val());
|
||||||
val().~T();
|
val().~T();
|
||||||
try {
|
try {
|
||||||
::new (errptr()) unexpected_type(std::move(rhs.err()));
|
::new (errptr()) unexpected_type(std::move(rhs.err()));
|
||||||
rhs.err().~unexpected_type();
|
rhs.err().~unexpected_type();
|
||||||
::new (rhs.valptr()) T(temp);
|
::new (rhs.valptr()) T(std::move(temp));
|
||||||
std::swap(this->m_has_val, rhs.m_has_val);
|
std::swap(this->m_has_val, rhs.m_has_val);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
val() = std::move(temp);
|
val() = std::move(temp);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
using std::swap;
|
|
||||||
swap(err(), rhs.err());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class OT = T, class OE = E>
|
void swap_where_only_one_has_value_and_t_is_not_void(
|
||||||
detail::enable_if_t<
|
expected &rhs, move_constructing_t_can_throw,
|
||||||
detail::is_swappable<OT>::value && detail::is_swappable<OE>::value &&
|
t_is_nothrow_move_constructible) {
|
||||||
std::is_nothrow_move_constructible<OE>::value && !std::is_void<OT>::value>
|
|
||||||
swap(expected &rhs) noexcept(std::is_nothrow_move_constructible<T>::value
|
|
||||||
&&detail::is_nothrow_swappable<T>::value &&
|
|
||||||
detail::is_nothrow_swappable<E>::value) {
|
|
||||||
if (has_value() && rhs.has_value()) {
|
|
||||||
using std::swap;
|
|
||||||
swap(val(), rhs.val());
|
|
||||||
} else if (!has_value() && rhs.has_value()) {
|
|
||||||
rhs.swap(*this);
|
|
||||||
} else if (has_value()) {
|
|
||||||
auto temp = std::move(rhs.err());
|
auto temp = std::move(rhs.err());
|
||||||
rhs.err().~unexpected_type();
|
rhs.err().~unexpected_type();
|
||||||
try {
|
try {
|
||||||
@@ -1893,30 +1908,25 @@ public:
|
|||||||
rhs.err() = std::move(temp);
|
rhs.err() = std::move(temp);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
using std::swap;
|
|
||||||
swap(err(), rhs.err());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
template <class OT = T, class OE = E>
|
template <class OT = T, class OE = E>
|
||||||
detail::enable_if_t<detail::is_swappable<OT>::value &&
|
detail::enable_if_t<detail::is_swappable<OT>::value &&
|
||||||
detail::is_swappable<OE>::value &&
|
detail::is_swappable<OE>::value &&
|
||||||
std::is_void<OT>::value &&
|
(std::is_nothrow_move_constructible<OT>::value ||
|
||||||
(std::is_move_constructible<OT>::value ||
|
std::is_nothrow_move_constructible<OE>::value)>
|
||||||
std::is_move_constructible<OE>::value)>
|
|
||||||
swap(expected &rhs) noexcept(
|
swap(expected &rhs) noexcept(
|
||||||
std::is_nothrow_move_constructible<E>::value
|
std::is_nothrow_move_constructible<T>::value
|
||||||
|
&&detail::is_nothrow_swappable<T>::value
|
||||||
|
&&std::is_nothrow_move_constructible<E>::value
|
||||||
&&detail::is_nothrow_swappable<E>::value) {
|
&&detail::is_nothrow_swappable<E>::value) {
|
||||||
if (has_value() && rhs.has_value()) {
|
if (has_value() && rhs.has_value()) {
|
||||||
// swapping void is a no-op
|
swap_where_both_have_value(rhs, std::is_void<T>::type{});
|
||||||
return;
|
|
||||||
} else if (!has_value() && rhs.has_value()) {
|
} else if (!has_value() && rhs.has_value()) {
|
||||||
rhs.swap(*this);
|
rhs.swap(*this);
|
||||||
} else if (has_value()) {
|
} else if (has_value()) {
|
||||||
::new (errptr()) unexpected_type(std::move(rhs.err()));
|
swap_where_only_one_has_value(rhs, std::is_void<T>::type{});
|
||||||
rhs.err().~unexpected_type();
|
|
||||||
std::swap(this->m_has_val, rhs.m_has_val);
|
|
||||||
} else {
|
} else {
|
||||||
using std::swap;
|
using std::swap;
|
||||||
swap(err(), rhs.err());
|
swap(err(), rhs.err());
|
||||||
@@ -2313,9 +2323,9 @@ template <class Exp, class F,
|
|||||||
detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
|
detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
|
||||||
constexpr auto or_else_impl(Exp &&exp, F &&f) {
|
constexpr auto or_else_impl(Exp &&exp, F &&f) {
|
||||||
static_assert(detail::is_expected<Ret>::value, "F must return an expected");
|
static_assert(detail::is_expected<Ret>::value, "F must return an expected");
|
||||||
return exp.has_value()
|
return exp.has_value() ? std::forward<Exp>(exp)
|
||||||
? std::forward<Exp>(exp)
|
: detail::invoke(std::forward<F>(f),
|
||||||
: detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
|
std::forward<Exp>(exp).error());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Exp, class F,
|
template <class Exp, class F,
|
||||||
@@ -2323,9 +2333,9 @@ template <class Exp, class F,
|
|||||||
std::declval<Exp>().error())),
|
std::declval<Exp>().error())),
|
||||||
detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
|
detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
|
||||||
detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
|
detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
|
||||||
return exp.has_value()
|
return exp.has_value() ? std::forward<Exp>(exp)
|
||||||
? std::forward<Exp>(exp)
|
: (detail::invoke(std::forward<F>(f),
|
||||||
: (detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error()),
|
std::forward<Exp>(exp).error()),
|
||||||
std::forward<Exp>(exp));
|
std::forward<Exp>(exp));
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@@ -2335,9 +2345,9 @@ template <class Exp, class F,
|
|||||||
detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
|
detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
|
||||||
auto or_else_impl(Exp &&exp, F &&f) -> Ret {
|
auto or_else_impl(Exp &&exp, F &&f) -> Ret {
|
||||||
static_assert(detail::is_expected<Ret>::value, "F must return an expected");
|
static_assert(detail::is_expected<Ret>::value, "F must return an expected");
|
||||||
return exp.has_value()
|
return exp.has_value() ? std::forward<Exp>(exp)
|
||||||
? std::forward<Exp>(exp)
|
: detail::invoke(std::forward<F>(f),
|
||||||
: detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
|
std::forward<Exp>(exp).error());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Exp, class F,
|
template <class Exp, class F,
|
||||||
@@ -2345,9 +2355,9 @@ template <class Exp, class F,
|
|||||||
std::declval<Exp>().error())),
|
std::declval<Exp>().error())),
|
||||||
detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
|
detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
|
||||||
detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
|
detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
|
||||||
return exp.has_value()
|
return exp.has_value() ? std::forward<Exp>(exp)
|
||||||
? std::forward<Exp>(exp)
|
: (detail::invoke(std::forward<F>(f),
|
||||||
: (detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error()),
|
std::forward<Exp>(exp).error()),
|
||||||
std::forward<Exp>(exp));
|
std::forward<Exp>(exp));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -2403,7 +2413,8 @@ constexpr bool operator!=(const unexpected<E> &e, const expected<T, E> &x) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class T, class E,
|
template <class T, class E,
|
||||||
detail::enable_if_t<(std::is_void<T>::value || std::is_move_constructible<T>::value) &&
|
detail::enable_if_t<(std::is_void<T>::value ||
|
||||||
|
std::is_move_constructible<T>::value) &&
|
||||||
detail::is_swappable<T>::value &&
|
detail::is_swappable<T>::value &&
|
||||||
std::is_move_constructible<E>::value &&
|
std::is_move_constructible<E>::value &&
|
||||||
detail::is_swappable<E>::value> * = nullptr>
|
detail::is_swappable<E>::value> * = nullptr>
|
||||||
|
Reference in New Issue
Block a user