From 18e8a79242900dbfc8eb62179321ae547a4286e4 Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sun, 5 Feb 2023 19:54:48 +0800 Subject: [PATCH] add: [imp] expected (TBD) --- include/libimp/expected.h | 248 ++++++++++++++++++++++++++++++++++ include/libimp/generic.h | 19 ++- test/imp/test_imp_utility.cpp | 11 +- 3 files changed, 275 insertions(+), 3 deletions(-) create mode 100644 include/libimp/expected.h diff --git a/include/libimp/expected.h b/include/libimp/expected.h new file mode 100644 index 0000000..ad68b68 --- /dev/null +++ b/include/libimp/expected.h @@ -0,0 +1,248 @@ +/** + * \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_ diff --git a/include/libimp/generic.h b/include/libimp/generic.h index 37c813c..0d6190f 100644 --- a/include/libimp/generic.h +++ b/include/libimp/generic.h @@ -10,20 +10,35 @@ #include #include "libimp/def.h" +#include "libimp/detect_plat.h" LIBIMP_NAMESPACE_BEG_ /** * \brief Utility metafunction that maps a sequence of any types to the type void * \see https://en.cppreference.com/w/cpp/types/void_t -*/ + */ template using void_t = void; +/** + * \brief To indicate that the contained object should be constructed in-place. + * \see https://en.cppreference.com/w/cpp/utility/in_place + */ +#if defined(LIBIMP_CPP_17) +using std::in_place_t; +using std::in_place; +#else /*!LIBIMP_CPP_17*/ +struct in_place_t { + explicit in_place_t() = default; +}; +constexpr in_place_t in_place {}; +#endif/*!LIBIMP_CPP_17*/ + /** * \brief A general pattern for supporting customisable functions * \see https://www.open-std.org/jtc1/sc22/WG21/docs/papers/2019/p1895r0.pdf -*/ + */ namespace detail { void tag_invoke(); diff --git a/test/imp/test_imp_utility.cpp b/test/imp/test_imp_utility.cpp index e7c2527..9ab676b 100644 --- a/test/imp/test_imp_utility.cpp +++ b/test/imp/test_imp_utility.cpp @@ -10,6 +10,7 @@ #include "libimp/countof.h" #include "libimp/horrible_cast.h" #include "libimp/detect_plat.h" +#include "libimp/generic.h" TEST(utility, construct) { struct Foo { @@ -124,4 +125,12 @@ TEST(utility, horrible_cast) { #endif // imp::horrible_cast(0ll); -} \ No newline at end of file +} + +#if defined(LIBIMP_CPP_17) +TEST(utility, in_place) { + EXPECT_TRUE((std::is_same::value)); + [](imp::in_place_t) {}(std::in_place); + [](std::in_place_t) {}(imp::in_place); +} +#endif/*LIBIMP_CPP_17*/