/** * \file libimp/expected.h * \author mutouyun (orz@orzz.org) * \brief Provides a way to store either of two values. * \date 2023-02-05 */ #pragma once #include #include // std::exchange #include #include // std::addressof #include // std::nullptr_t #include "libimp/def.h" #include "libimp/construct.h" #include "libimp/generic.h" #include "libimp/byte.h" LIBIMP_NAMESPACE_BEG_ /** * \brief In-place construction tag for unexpected value in expected. * \see https://en.cppreference.com/w/cpp/utility/expected/unexpect_t */ struct unexpected_t { explicit unexpected_t() = default; }; constexpr unexpected_t unexpected {}; /** * \class template expected * \brief Provides a way to store either of two values. * \see https://en.cppreference.com/w/cpp/utility/expected * \tparam T - the type of the expected value. * \tparam E - the type of the unexpected value. */ template class expected; namespace detail_expected { template struct data_union { union { T value_; ///< the expected value E error_; ///< the unexpected value }; data_union(data_union const &) = delete; data_union &operator=(data_union const &) = delete; data_union(std::nullptr_t) noexcept {} ~data_union() {} template data_union(in_place_t, A &&...args) : value_{std::forward(args)...} {} template data_union(unexpected_t, A &&...args) : error_{std::forward(args)...} {} void destruct_value() noexcept { destroy(&value_); } void destruct_error() noexcept { destroy(&error_); } }; template struct data_union { alignas(E) std::array error_; ///< the unexpected value data_union(data_union const &) = delete; data_union &operator=(data_union const &) = delete; data_union(std::nullptr_t) noexcept {} template data_union(in_place_t, A &&...) noexcept {} template data_union(unexpected_t, A &&...args) { construct(&error_, std::forward(args)...); } void destruct_value() noexcept {} void destruct_error() noexcept { destroy(reinterpret_cast(&error_)); } }; template auto destruct(bool /*has_value*/, data_union &/*data*/) noexcept -> typename std::enable_if::value && std::is_trivially_destructible::value>::type { // Do nothing. } template auto destruct(bool has_value, data_union &data) noexcept -> typename std::enable_if::value && std::is_trivially_destructible::value>::type { if (has_value) data.destruct_value(); } template auto destruct(bool has_value, data_union &data) noexcept -> typename std::enable_if< std::is_trivially_destructible::value && !std::is_trivially_destructible::value>::type { if (!has_value) data.destruct_error(); } template auto destruct(bool has_value, data_union &data) noexcept -> typename std::enable_if::value && !std::is_trivially_destructible::value>::type { if (has_value) { data.destruct_value(); } else { data.destruct_error(); } } template struct value_getter : data_union { using data_union::data_union; value_getter(S const &other) : data_union(nullptr) { if (bool(static_cast(other))) { construct>(this, in_place, other.value_); } else { 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_)); } else { 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 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 & { return bool(*static_cast(this)) ? **this : static_cast(std::forward(def)); } template T value_or(U &&def) && { return bool(*static_cast(this)) ? std::move(**this) : static_cast(std::forward(def)); } template T &emplace(A &&...args) { static_cast(this)->reconstruct(in_place, std::forward(args)...); return this->value_; } }; template struct value_getter : data_union { using data_union::data_union; value_getter(S const &other) : data_union(nullptr) { if (bool(static_cast(other))) { construct>(this, in_place); } else { construct>(this, unexpected, other.error_); } } value_getter(S &&other) : data_union(nullptr) { if (bool(static_cast(other))) { construct>(this, in_place); } else { construct>(this, unexpected, std::move(other.error_)); } } void emplace() noexcept { static_cast(this)->reconstruct(in_place); } }; template struct storage : value_getter, T, E> { using getter_t = value_getter, T, E>; bool has_value_; template storage(in_place_t, A &&...args) : getter_t(in_place, std::forward(args)...) , has_value_(true) {} template storage(unexpected_t, A &&...args) : getter_t(unexpected, std::forward(args)...) , has_value_(false) {} storage(storage const &other) : getter_t(other) , has_value_(other.has_value_) {} storage(storage &&other) : getter_t(std::move(other)) , has_value_(std::exchange(other.has_value_, false)) {} bool has_value() const noexcept { return has_value_; } explicit operator bool() const noexcept { 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; template void reconstruct(A &&...args) { destroy(this); construct(this, std::forward(args)...); } }; } // namespace detail_expected LIBIMP_NAMESPACE_END_