upd: [imp] improve expected

This commit is contained in:
mutouyun 2023-02-12 16:28:52 +08:00
parent 18e8a79242
commit 3b781db42e
2 changed files with 110 additions and 31 deletions

View File

@ -42,6 +42,9 @@ namespace detail_expected {
template <typename T, typename E> template <typename T, typename E>
struct data_union { struct data_union {
using const_value_t = typename std::add_const<T>::type;
using const_error_t = typename std::add_const<E>::type;
union { union {
T value_; ///< the expected value T value_; ///< the expected value
E error_; ///< the unexpected value E error_; ///< the unexpected value
@ -60,10 +63,22 @@ struct data_union {
void destruct_value() noexcept { destroy(&value_); } void destruct_value() noexcept { destroy(&value_); }
void destruct_error() noexcept { destroy(&error_); } void destruct_error() noexcept { destroy(&error_); }
const_value_t & value() const & noexcept { return value_; }
T & value() & noexcept { return value_; }
const_value_t &&value() const && noexcept { return std::move(value_); }
T && value() && noexcept { return std::move(value_); }
const_error_t & error() const & noexcept { return error_; }
E & error() & noexcept { return error_; }
const_error_t &&error() const && noexcept { return std::move(error_); }
E && error() && noexcept { return std::move(error_); }
}; };
template <typename E> template <typename E>
struct data_union<void, E> { struct data_union<void, E> {
using const_error_t = typename std::add_const<E>::type;
alignas(E) std::array<byte, sizeof(E)> error_; ///< the unexpected value alignas(E) std::array<byte, sizeof(E)> error_; ///< the unexpected value
data_union(data_union const &) = delete; data_union(data_union const &) = delete;
@ -80,6 +95,11 @@ struct data_union<void, E> {
void destruct_value() noexcept {} void destruct_value() noexcept {}
void destruct_error() noexcept { destroy(reinterpret_cast<E *>(&error_)); } void destruct_error() noexcept { destroy(reinterpret_cast<E *>(&error_)); }
const_error_t & error() const & noexcept { return *reinterpret_cast<E *>(error_.data()); }
E & error() & noexcept { return *reinterpret_cast<E *>(error_.data()); }
const_error_t &&error() const && noexcept { return std::move(*reinterpret_cast<E *>(error_.data())); }
E && error() && noexcept { return std::move(*reinterpret_cast<E *>(error_.data())); }
}; };
template <typename T, typename E> template <typename T, typename E>
@ -120,32 +140,27 @@ struct value_getter : data_union<T, E> {
value_getter(S const &other) : data_union<T, E>(nullptr) { value_getter(S const &other) : data_union<T, E>(nullptr) {
if (bool(static_cast<S &>(other))) { if (bool(static_cast<S &>(other))) {
construct<data_union<T, E>>(this, in_place, other.value_); construct<data_union<T, E>>(this, in_place, other.value());
} else { } else {
construct<data_union<T, E>>(this, unexpected, other.error_); construct<data_union<T, E>>(this, unexpected, other.error());
} }
} }
value_getter(S &&other) : data_union<T, E>(nullptr) { value_getter(S &&other) : data_union<T, E>(nullptr) {
if (bool(static_cast<S &>(other))) { if (bool(static_cast<S &>(other))) {
construct<data_union<T, E>>(this, in_place, std::move(other.value_)); construct<data_union<T, E>>(this, in_place, std::move(other).value());
} else { } else {
construct<data_union<T, E>>(this, unexpected, std::move(other.error_)); construct<data_union<T, E>>(this, unexpected, std::move(other).error());
} }
} }
T const & value() const & noexcept { return this->value_; } T const *operator->() const noexcept { return std::addressof(this->value()); }
T & value() & noexcept { return this->value_; } T * operator->() noexcept { return std::addressof(this->value()); }
T const &&value() const && noexcept { return std::move(this->value_); }
T && value() && noexcept { return std::move(this->value_); }
T const *operator->() const noexcept { return std::addressof(this->value_); } T const & operator*() const & noexcept { return this->value(); }
T * operator->() noexcept { return std::addressof(this->value_); } T & operator*() & noexcept { return this->value(); }
T const &&operator*() const && noexcept { return std::move(this->value()); }
T const & operator*() const & noexcept { return this->value_; } T && operator*() && noexcept { return std::move(this->value()); }
T & operator*() & noexcept { return this->value_; }
T const &&operator*() const && noexcept { return std::move(this->value_); }
T && operator*() && noexcept { return std::move(this->value_); }
template <typename U> template <typename U>
T value_or(U &&def) const & { T value_or(U &&def) const & {
@ -159,7 +174,23 @@ struct value_getter : data_union<T, E> {
template <typename... A> template <typename... A>
T &emplace(A &&...args) { T &emplace(A &&...args) {
static_cast<S *>(this)->reconstruct(in_place, std::forward<A>(args)...); static_cast<S *>(this)->reconstruct(in_place, std::forward<A>(args)...);
return this->value_; return this->value();
}
void swap(S &other) {
if (bool(*static_cast<S *>(this)) && bool(other)) {
std::swap(this->value(), other.value());
} else if (!*static_cast<S *>(this) && !other) {
std::swap(this->error(), other.error());
} else if (!*static_cast<S *>(this) && bool(other)) {
E err(std::move(this->error()));
this->emplace(std::move(other.value()));
other.reconstruct(unexpected, std::move(err));
} else /*if (bool(*this) && !other)*/ {
E err(std::move(other.error()));
other.emplace(std::move(this->value()));
static_cast<S *>(this)->reconstruct(unexpected, std::move(err));
}
} }
}; };
@ -171,7 +202,7 @@ struct value_getter<S, void, E> : data_union<void, E> {
if (bool(static_cast<S &>(other))) { if (bool(static_cast<S &>(other))) {
construct<data_union<T, E>>(this, in_place); construct<data_union<T, E>>(this, in_place);
} else { } else {
construct<data_union<T, E>>(this, unexpected, other.error_); construct<data_union<T, E>>(this, unexpected, other.error());
} }
} }
@ -179,13 +210,29 @@ struct value_getter<S, void, E> : data_union<void, E> {
if (bool(static_cast<S &>(other))) { if (bool(static_cast<S &>(other))) {
construct<data_union<T, E>>(this, in_place); construct<data_union<T, E>>(this, in_place);
} else { } else {
construct<data_union<T, E>>(this, unexpected, std::move(other.error_)); construct<data_union<T, E>>(this, unexpected, std::move(other).error());
} }
} }
void emplace() noexcept { void emplace() noexcept {
static_cast<S *>(this)->reconstruct(in_place); static_cast<S *>(this)->reconstruct(in_place);
} }
void swap(S &other) {
if (bool(*static_cast<S *>(this)) && bool(other)) {
return;
} else if (!*static_cast<S *>(this) && !other) {
std::swap(this->error(), other.error());
} else if (!*static_cast<S *>(this) && bool(other)) {
E err(std::move(this->error()));
this->emplace();
other.reconstruct(unexpected, std::move(err));
} else /*if (bool(*this) && !other)*/ {
E err(std::move(other.error()));
other.emplace();
static_cast<S *>(this)->reconstruct(unexpected, std::move(err));
}
}
}; };
template <typename T, typename E> template <typename T, typename E>
@ -220,19 +267,6 @@ struct storage : value_getter<storage<T, E>, T, E> {
return this->has_value(); return this->has_value();
} }
typename std::add_const<E>::type &error() const & noexcept {
return this->error_;
}
E &error() & noexcept {
return this->error_;
}
typename std::add_const<E>::type &&error() const && noexcept {
return std::move(this->error_);
}
E &&error() && noexcept {
return std::move(this->error_);
}
protected: protected:
friend getter_t; friend getter_t;
@ -243,6 +277,42 @@ protected:
} }
}; };
/// \brief The invoke forwarding helper.
template <typename F, typename... A>
auto invoke(F &&f, A &&...args) noexcept(
noexcept(std::forward<F>(f)(std::forward<A>(args)...)))
-> decltype(std::forward<F>(f)(std::forward<A>(args)...)) {
return std::forward<F>(f)(std::forward<A>(args)...);
}
template <typename F, typename... A>
auto invoke(F &&f, A &&...args) noexcept(
noexcept(std::forward<F>(f)()))
-> decltype(std::forward<F>(f)()) {
return std::forward<F>(f)();
}
/// \brief and_then helper.
template <typename E, typename F,
typename R = decltype(invoke(std::declval<F>(), *std::declval<E>()))>
R and_then(E &&exp, F &&f) {
static_assert(is_specialized<expected, R>::value, "F must return an `expected`.");
return bool(exp) ? invoke(std::forward<F>(f), *std::forward<E>(exp))
: R(unexpected, std::forward<E>(exp).error());
}
/// \brief or_else helper.
template <typename E, typename F,
typename R = decltype(invoke(std::declval<F>(), std::declval<E>().error()))>
R or_else(E &&exp, F &&f) {
static_assert(is_specialized<expected, R>::value, "F must return an `expected`.");
return bool(exp) ? std::forward<E>(exp)
: invoke(std::forward<F>(f), std::forward<E>(exp).error());
}
} // namespace detail_expected } // namespace detail_expected
LIBIMP_NAMESPACE_END_ LIBIMP_NAMESPACE_END_

View File

@ -75,4 +75,13 @@ using is_not_match =
typename std::enable_if<!detail::is_same_first<T, typename std::enable_if<!detail::is_same_first<T,
typename std::decay<A>::type...>::value>::type; typename std::decay<A>::type...>::value>::type;
/**
* \brief Determines whether a type is specialized from a particular template.
*/
template <template <typename...> class Tt, typename T>
struct is_specialized : std::false_type {};
template <template <typename...> class Tt, typename... A>
struct is_specialized<Tt, Tt<A...>> : std::true_type {};
LIBIMP_NAMESPACE_END_ LIBIMP_NAMESPACE_END_