diff --git a/include/continuable/detail/awaiting.hpp b/include/continuable/detail/awaiting.hpp index f9d7921..6c4a922 100644 --- a/include/continuable/detail/awaiting.hpp +++ b/include/continuable/detail/awaiting.hpp @@ -58,7 +58,7 @@ using std::experimental::coroutine_handle; /// for waiting on a continuable in a stackless coroutine. template class awaitable { - using trait_t = util::expected_result_trait_t; + using trait_t = container::expected_result_trait_t; /// The continuable which is invoked upon suspension Continuable continuable_; diff --git a/include/continuable/detail/expected.hpp b/include/continuable/detail/expected.hpp index 1c1820b..5ebe083 100644 --- a/include/continuable/detail/expected.hpp +++ b/include/continuable/detail/expected.hpp @@ -31,231 +31,75 @@ #ifndef CONTINUABLE_DETAIL_EXPECTED_HPP_INCLUDED #define CONTINUABLE_DETAIL_EXPECTED_HPP_INCLUDED -#include -#include #include #include +#include #include -#include #include namespace cti { namespace detail { -namespace util { -namespace detail { -enum class slot_t { empty, value, error }; - -template -using storage_of_t = // - std::aligned_storage_t< - (sizeof(types::error_type) > sizeof(T) ? sizeof(types::error_type) - : sizeof(T)), - (alignof(types::error_type) > alignof(T) ? alignof(types::error_type) - : alignof(T))>; - -template -struct expected_base { - storage_of_t storage_; - slot_t slot_; - - constexpr expected_base() : slot_(slot_t::empty) { - } - - expected_base(expected_base const&) noexcept { - } - expected_base(expected_base&&) noexcept { - } - expected_base& operator=(expected_base const&) { - return *this; - } - expected_base& operator=(expected_base&&) { - return *this; - } -}; - -template -struct expected_move_base { - constexpr expected_move_base() = default; - - expected_move_base(expected_move_base const&) = default; - explicit expected_move_base(expected_move_base&& right) { - Base& me = *static_cast(this); - Base& other = *static_cast(&right); - assert(!other.is_empty()); - -#ifndef _NDEBUG - me.set(slot_t::empty); -#endif - - other.visit([&](auto&& value) { - // ... - me.init(std::move(value)); - }); - me.set(other.get()); - other.destroy(); - } - expected_move_base& operator=(expected_move_base const&) = default; - expected_move_base& operator=(expected_move_base&& right) { - Base& me = *static_cast(this); - Base& other = *static_cast(&right); - assert(!other.is_empty()); - - me.weak_destroy(); - - other.visit([&](auto&& value) { - // ... - me.init(std::move(value)); - }); - me.set(other.get()); - other.destroy(); - return *this; - } -}; -template -struct expected_copy_base : expected_move_base { - constexpr expected_copy_base() = default; - - expected_copy_base(expected_copy_base&&) = default; - explicit expected_copy_base(expected_copy_base const& right) - : expected_move_base() - // TODO noexcept(Base::is_nothrow_move_constructible) - { - Base& me = *static_cast(this); - Base const& other = *static_cast(&right); - assert(!other.is_empty()); - -#ifndef _NDEBUG - me.set(slot_t::empty); -#endif - - other.visit([&](auto&& value) { - // ... - me.init(std::move(value)); - }); - me.set(other.get()); - } - expected_copy_base& operator=(expected_copy_base&&) = default; - expected_copy_base& operator=(expected_copy_base const& right) - // TODO noexcept(Base::is_nothrow_move_constructible) - { - Base& me = *static_cast(this); - Base const& other = *static_cast(&right); - assert(!other.is_empty()); - - me.weak_destroy(); - - other.visit([&](auto&& value) { - // ... - me.init(std::move(value)); - }); - me.set(other.get()); - return *this; - } -}; -template -struct expected_copy_base : expected_move_base { - constexpr expected_copy_base() = default; - - expected_copy_base(expected_copy_base const&) = delete; - explicit expected_copy_base(expected_copy_base&& right) = default; - expected_copy_base& operator=(expected_copy_base const&) = delete; - expected_copy_base& operator=(expected_copy_base&& right) = default; -}; -} // namespace detail - +namespace container { /// A class similar to the one in the expected proposal, /// however it is capable of carrying an exception_ptr if /// exceptions are used. template -class expected - : detail::expected_copy_base< - expected, std::is_copy_constructible::value && - std::is_copy_constructible::value>, - detail::expected_base { - - template - friend class expected; - template - friend struct detail::expected_move_base; - template - friend struct detail::expected_copy_base; - - template - expected(V&& value, detail::slot_t const slot) { - using type = std::decay_t; - new (&this->storage_) type(std::forward(value)); - set(slot); - } +class expected { + flat_variant variant_; public: - constexpr expected() = default; - expected(expected const&) = default; - expected(expected&&) = default; + explicit expected() = default; + explicit expected(expected const&) = default; + explicit expected(expected&&) = default; expected& operator=(expected const&) = default; expected& operator=(expected&&) = default; + ~expected() = default; - ~expected() noexcept( - std::is_nothrow_destructible::value&& - std::is_nothrow_destructible::value) { - weak_destroy(); + explicit expected(T value) : variant_(std::move(value)) { } - - explicit expected(T value) // - : expected(std::move(value), detail::slot_t::value) { - } - explicit expected(types::error_type error) // - : expected(std::move(error), detail::slot_t::error) { + explicit expected(types::error_type exception) + : variant_(std::move(exception)) { } expected& operator=(T value) { - set_value(std::move(value)); + variant_ = std::move(value); return *this; } - expected& operator=(types::error_type error) { - set_exception(std::move(error)); + expected& operator=(types::error_type exception) { + variant_ = std::move(exception); return *this; } + void set_value(T value) { + variant_ = std::move(value); + } + void set_exception(types::error_type exception) { + variant_ = std::move(exception); + } + bool is_value() const noexcept { - assert(!is_empty()); - return is(detail::slot_t::value); + return variant_.template is(); } bool is_exception() const noexcept { - assert(!is_empty()); - return is(detail::slot_t::error); + return variant_.template is(); } explicit constexpr operator bool() const noexcept { return is_value(); } - void set_value(T value) { - weak_destroy(); - init(std::move(value)); - set(detail::slot_t::value); - } - void set_exception(types::error_type error) { - weak_destroy(); - init(std::move(error)); - set(detail::slot_t::error); - } - T& get_value() noexcept { - assert(is_value()); - return cast(); + return variant_.template cast(); } T const& get_value() const noexcept { - assert(is_value()); - return cast(); + return variant_.template cast(); } types::error_type& get_exception() noexcept { - assert(is_exception()); - return cast(); + return variant_.template cast(); } types::error_type const& get_exception() const noexcept { - assert(is_exception()); - return cast(); + return variant_.template cast(); } T& operator*() noexcept { @@ -264,80 +108,6 @@ public: T const& operator*() const noexcept { return get_value(); } - -private: - template - void visit(V&& visitor) { - switch (this->slot_) { - case detail::slot_t::value: - return std::forward(visitor)(cast()); - case detail::slot_t::error: - return std::forward(visitor)(cast()); - default: - // We don't visit when there is no value - break; - } - } - template - void visit(V&& visitor) const { - switch (this->slot_) { - case detail::slot_t::value: - return std::forward(visitor)(cast()); - case detail::slot_t::error: - return std::forward(visitor)(cast()); - default: - // We don't visit when there is no value - break; - } - } - - bool is_empty() const noexcept { - return is(detail::slot_t::empty); - } - - template - V& cast() noexcept { - assert(!is_empty()); - return *reinterpret_cast(&this->storage_); - } - template - V const& cast() const noexcept { - assert(!is_empty()); - return *reinterpret_cast(&this->storage_); - } - - template - void init(V&& value) { - assert(is_empty()); - using type = std::decay_t; - new (&this->storage_) type(std::forward(value)); - } - void destroy() { - weak_destroy(); - -#ifdef NDEBUG - set(detail::slot_t::empty); -#endif - } - void weak_destroy() { - visit([&](auto&& value) { - using type = std::decay_t; - value.~type(); - }); - -#ifndef NDEBUG - set(detail::slot_t::empty); -#endif - } - detail::slot_t get() const noexcept { - return this->slot_; - } - bool is(detail::slot_t const slot) const noexcept { - return get() == slot; - } - void set(detail::slot_t const slot) { - this->slot_ = slot; - } }; namespace detail { @@ -387,7 +157,7 @@ struct expected_result_trait> { template using expected_result_trait_t = detail::expected_result_trait{}))>; -} // namespace util +} // namespace container } // namespace detail } // namespace cti diff --git a/include/continuable/detail/flat-variant.hpp b/include/continuable/detail/flat-variant.hpp index 765a720..384e286 100644 --- a/include/continuable/detail/flat-variant.hpp +++ b/include/continuable/detail/flat-variant.hpp @@ -137,7 +137,7 @@ struct flat_variant_copy_base : flat_variant_move_base { flat_variant_copy_base(flat_variant_copy_base&&) = default; explicit flat_variant_copy_base(flat_variant_copy_base const& right) - : flat_variant_copy_base() + : flat_variant_move_base() // TODO noexcept(Base::is_nothrow_move_constructible) { Base& me = *static_cast(this); @@ -189,6 +189,14 @@ using every = traits::conjunction...>; /// A class similar to the one in the variant proposal, /// however it is capable of carrying an empty state by default. +template +class flat_variant; + +template +struct is_flat_variant : std::false_type {}; +template +struct is_flat_variant> : std::true_type {}; + template class flat_variant : detail::flat_variant_copy_base< @@ -222,17 +230,21 @@ public: weak_destroy(); } - template , T...>::value> - // Since the flat_variant is never a part of the contained - // values itself this overload is safed against the linted issue. + template < + typename V, + std::enable_if_t>::value>* = nullptr, + std::size_t Index = traits::index_of_t, T...>::value> + // Since the flat_variant isn't allowed through SFINAE + // this overload is safed against the linted issue. // NOLINTNEXTLINE(misc-forwarding-reference-overload) explicit flat_variant(V&& value) : flat_variant(std::forward(value), Index) { } - template , T...>::value> + template < + typename V, + std::enable_if_t>::value>* = nullptr, + std::size_t Index = traits::index_of_t, T...>::value> flat_variant& operator=(V&& value) { weak_destroy(); init(std::forward(value)); diff --git a/test/unit-test/single/test-continuable-expected.cpp b/test/unit-test/single/test-continuable-expected.cpp index f3e68d6..d01297c 100644 --- a/test/unit-test/single/test-continuable-expected.cpp +++ b/test/unit-test/single/test-continuable-expected.cpp @@ -30,7 +30,7 @@ #include using cti::detail::types::error_type; -using cti::detail::util::expected; +using cti::detail::container::expected; static int const CANARY = 373671;