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