From 1e39bd85ddd5a855972dd23b47b9f14b16e8ea07 Mon Sep 17 00:00:00 2001 From: Denis Blank Date: Sat, 4 Apr 2020 20:41:32 +0200 Subject: [PATCH] Replace the result<...> implementation by a lighter one * result<...> only moveable now, the conditional copy capabilities were removed * If you require the optional copy capability use a std::variant implementation of your choice * Closes #13 --- include/continuable/continuable-result.hpp | 75 ++-- .../connection/connection-aggregated.hpp | 34 +- .../detail/utility/flat-variant.hpp | 379 ------------------ .../detail/utility/result-trait.hpp | 6 +- .../detail/utility/result-variant.hpp | 217 ++++++++++ test/unit-test/CMakeLists.txt | 1 - .../single/test-continuable-flat-variant.cpp | 161 -------- .../single/test-continuable-result.cpp | 44 -- 8 files changed, 283 insertions(+), 634 deletions(-) delete mode 100644 include/continuable/detail/utility/flat-variant.hpp create mode 100644 include/continuable/detail/utility/result-variant.hpp delete mode 100644 test/unit-test/single/test-continuable-flat-variant.cpp diff --git a/include/continuable/continuable-result.hpp b/include/continuable/continuable-result.hpp index c3c5289..56868c3 100644 --- a/include/continuable/continuable-result.hpp +++ b/include/continuable/continuable-result.hpp @@ -34,8 +34,8 @@ #include #include #include -#include #include +#include #include #include @@ -49,6 +49,11 @@ namespace cti { /// or was cancelled. /// \{ +/// A tag which represents present void values in result. +/// +/// \since 4.0.0 +using void_arg_t = detail::void_arg_t; + /// A class which is convertible to any \ref result and that definitely holds no /// value so the real result gets invalidated when this object is passed to it. /// @@ -137,43 +142,53 @@ public: template class result { using trait_t = detail::result_trait; - using surrogate_t = typename trait_t::surrogate_t; template - explicit result(detail::init_arg_t, Args&&... values) - : variant_(trait_t::wrap(std::forward(values)...)) {} - explicit result(detail::init_arg_t, exception_t exception) - : variant_(std::move(exception)) {} + explicit result(detail::init_result_arg_t arg, Args&&... values) + : variant_(arg, trait_t::wrap(std::forward(values)...)) {} + explicit result(detail::init_exception_arg_t arg, exception_t exception) + : variant_(arg, std::move(exception)) {} public: using value_t = typename trait_t::value_t; + using value_placeholder_t = typename trait_t::surrogate_t; template explicit result(FirstArg&& first, Args&&... values) - : variant_(trait_t::wrap(std::forward(first), + : variant_(detail::init_result_arg_t{}, + trait_t::wrap(std::forward(first), std::forward(values)...)) {} result() = default; - result(result const&) = default; + result(result const&) = delete; result(result&&) = default; - result& operator=(result const&) = default; + result& operator=(result const&) = delete; result& operator=(result&&) = default; ~result() = default; explicit result(exception_t exception) - : variant_(std::move(exception)) {} + : variant_(detail::init_exception_arg_t{}, std::move(exception)) {} /* implicit */ result(empty_result) {} /* implicit */ result(exceptional_result exceptional_result) - : variant_(std::move(exceptional_result.get_exception())) {} + : variant_(detail::init_exception_arg_t{}, + std::move(exceptional_result.get_exception())) {} /* implicit */ result(cancellation_result) - : variant_(exception_t{}) {} + : variant_(detail::init_exception_arg_t{}, exception_t{}) {} result& operator=(empty_result) { - set_empty(); + variant_.set_empty(); return *this; } - result& operator=(exceptional_result exceptional_result) { - set_exception(std::move(exceptional_result.get_exception())); + result& operator=(value_placeholder_t value) { + variant_.set_value(std::move(value)); + return *this; + } + result& operator=(exceptional_result exception) { + variant_.set_exception(std::move(exception.get_exception())); + return *this; + } + result& operator=(cancellation_result) { + variant_.set_exception({}); return *this; } @@ -183,15 +198,15 @@ public: } /// Set the result to a the state which holds the corresponding value void set_value(T... values) { - variant_ = trait_t::wrap(std::move(values)...); + variant_.set_value(trait_t::wrap(std::move(values)...)); } /// Set the result into a state which holds the corresponding exception void set_exception(exception_t exception) { - variant_ = std::move(exception); + variant_.set_exception(std::move(exception)); } /// Set the result into a state which holds the cancellation token void set_canceled() { - variant_ = exception_t{}; + variant_.set_exception(exception_t{}); } /// Returns true if the state of the result is empty @@ -200,11 +215,11 @@ public: } /// Returns true if the state of the result holds the result bool is_value() const noexcept { - return variant_.template is(); + return variant_.is_value(); } /// Returns true if the state of the result holds a present exception bool is_exception() const noexcept { - return variant_.template is(); + return variant_.is_exception(); } /// \copydoc is_value @@ -215,15 +230,15 @@ public: /// Returns the values of the result, if the result doesn't hold the value /// the behaviour is undefined but will assert in debug mode. decltype(auto) get_value() & noexcept { - return trait_t::unwrap(variant_.template cast()); + return trait_t::unwrap(variant_.get_value()); } ///\copydoc get_value decltype(auto) get_value() const& noexcept { - return trait_t::unwrap(variant_.template cast()); + return trait_t::unwrap(variant_.get_value()); } ///\copydoc get_value decltype(auto) get_value() && noexcept { - return trait_t::unwrap(std::move(variant_).template cast()); + return trait_t::unwrap(std::move(variant_.get_value())); } ///\copydoc get_value @@ -236,30 +251,30 @@ public: } ///\copydoc get_value decltype(auto) operator*() && noexcept { - return std::move(*this).get_value(); + return std::move(variant_.get_value()); } /// Returns the exception of the result, if the result doesn't hold an /// exception the behaviour is undefined but will assert in debug mode. exception_t& get_exception() & noexcept { - return variant_.template cast(); + return variant_.get_exception(); } /// \copydoc get_exception exception_t const& get_exception() const& noexcept { - return variant_.template cast(); + return variant_.get_exception(); } /// \copydoc get_exception exception_t&& get_exception() && noexcept { - return std::move(variant_).template cast(); + return std::move(variant_.get_exception()); } /// Creates a present result from the given values static result from(T... values) { - return result{detail::init_arg_t{}, std::move(values)...}; + return result{detail::init_result_arg_t{}, std::move(values)...}; } /// Creates a present result from the given exception static result from(exception_arg_t, exception_t exception) { - return result{detail::init_arg_t{}, std::move(exception)}; + return result{detail::init_exception_arg_t{}, std::move(exception)}; } /// Creates an empty result @@ -268,7 +283,7 @@ public: } private: - detail::container::flat_variant variant_; + detail::result_variant variant_; }; /// Returns the value at position I of the given result diff --git a/include/continuable/detail/connection/connection-aggregated.hpp b/include/continuable/detail/connection/connection-aggregated.hpp index dc258bd..ef84e32 100644 --- a/include/continuable/detail/connection/connection-aggregated.hpp +++ b/include/continuable/detail/connection/connection-aggregated.hpp @@ -38,7 +38,6 @@ #include #include #include -#include #include namespace cti { @@ -55,22 +54,25 @@ namespace connection { /// - single async value -> single value /// - multiple async value -> tuple of async values. namespace aggregated { + /// Guards a type to be default constructible, /// and wraps it into an optional type if it isn't default constructible. template using lazy_value_t = std::conditional_t::value, - T, container::flat_variant>; + T, result>; template -decltype(auto) unpack_lazy(T&& value) { +decltype(auto) unpack_lazy(std::true_type /*is_default_constructible*/, + T&& value) { return std::forward(value); } template -T&& unpack_lazy(container::flat_variant&& value) { - assert(value.template is() && +T&& unpack_lazy(std::false_type /*is_default_constructible*/, + result&& value) { + assert(value.is_value() && "The connection was finalized before all values were present!"); - return std::move(value.template cast()); + return std::move(value).get_value(); } template @@ -82,8 +84,7 @@ class continuable_box>> { public: explicit continuable_box(continuable_base>&& continuable) - : continuable_(std::move(continuable)) { - } + : continuable_(std::move(continuable)) {} auto const& peek() const { return continuable_; @@ -93,13 +94,13 @@ public: return std::move(continuable_); } - void assign() { - } + void assign() {} auto unbox() && { return spread_this(); } }; + template class continuable_box>> { @@ -109,8 +110,7 @@ class continuable_box>> { public: explicit continuable_box( continuable_base>&& continuable) - : continuable_(std::move(continuable)) { - } + : continuable_(std::move(continuable)) {} auto const& peek() const { return continuable_; @@ -125,7 +125,8 @@ public: } auto unbox() && { - return unpack_lazy(std::move(first_)); + return unpack_lazy(std::is_default_constructible{}, + std::move(first_)); } }; template @@ -138,8 +139,7 @@ class continuable_box< public: explicit continuable_box( continuable_base>&& continuable) - : continuable_(std::move(continuable)) { - } + : continuable_(std::move(continuable)) {} auto const& peek() const { return continuable_; @@ -159,7 +159,9 @@ public: [](auto&&... args) { return spread_this(std::forward(args)...); }, - unpack_lazy(std::move(args_))); + unpack_lazy( + std::is_default_constructible>{}, + std::move(args_))); } }; diff --git a/include/continuable/detail/utility/flat-variant.hpp b/include/continuable/detail/utility/flat-variant.hpp deleted file mode 100644 index 3d6823a..0000000 --- a/include/continuable/detail/utility/flat-variant.hpp +++ /dev/null @@ -1,379 +0,0 @@ - -/* - - /~` _ _ _|_. _ _ |_ | _ - \_,(_)| | | || ||_|(_||_)|(/_ - - https://github.com/Naios/continuable - v4.0.0 - - Copyright(c) 2015 - 2019 Denis Blank - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files(the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and / or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions : - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -**/ - -#ifndef CONTINUABLE_DETAIL_FLAT_VARIANT_HPP_INCLUDED -#define CONTINUABLE_DETAIL_FLAT_VARIANT_HPP_INCLUDED - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cti { -namespace detail { -namespace container { -namespace detail { -// We don't want to pull the algorithm header in -constexpr std::size_t max_element_of(std::initializer_list list) { - std::size_t m = 0; - for (auto current : list) { - if (current > m) { - m = current; - } - } - return m; -} - -/// Workarround for a regression introduced in ~ MSVC 15.8.1 -template -using size_of_helper = std::integral_constant; -template -using align_of_helper = std::integral_constant; - -template -constexpr auto storage_of_impl(identity) { - constexpr auto size = max_element_of({(size_of_helper::value)...}); - constexpr auto align = max_element_of({(align_of_helper::value)...}); - return std::aligned_storage_t{}; -} - -/// Declares the aligned storage union for the given types -template -using storage_of_t = decltype(storage_of_impl(identity{})); - -/// The value fpr the empty slot -using slot_t = std::uint8_t; - -/// The value which is used to mark the empty slot -using empty_slot = - std::integral_constant::max()>; - -template -struct flat_variant_base { - storage_of_t storage_; - slot_t slot_; - - constexpr flat_variant_base() : slot_(empty_slot::value) { - } - - flat_variant_base(flat_variant_base const&) noexcept { - } - flat_variant_base(flat_variant_base&&) noexcept { - } - flat_variant_base& operator=(flat_variant_base const&) { - return *this; - } - flat_variant_base& operator=(flat_variant_base&&) { - return *this; - } -}; - -template -struct flat_variant_move_base { - constexpr flat_variant_move_base() = default; - - flat_variant_move_base(flat_variant_move_base const&) = default; - explicit flat_variant_move_base(flat_variant_move_base&& right) { - Base& me = *static_cast(this); - Base& other = *static_cast(&right); - - if (other.is_empty()) { - me.set_slot(empty_slot::value); - } else { - - other.visit([&](auto&& value) { -#ifndef NDEBUG - me.set_slot(empty_slot::value); -#endif - // NOLINTNEXTLINE(misc-move-forwarding-reference) - me.init(std::move(value), other.get_slot()); - }); - } - - other.destroy(); - } - flat_variant_move_base& operator=(flat_variant_move_base const&) = default; - flat_variant_move_base& operator=(flat_variant_move_base&& right) { - Base& me = *static_cast(this); - Base& other = *static_cast(&right); - - me.weak_destroy(); - - if (other.is_empty()) { - me.set_slot(empty_slot::value); - } else { - other.visit([&](auto&& value) { - // ... - me.init(std::move(value), other.get_slot()); - }); - } - other.destroy(); - return *this; - } -}; -template -struct flat_variant_copy_base : flat_variant_move_base { - constexpr flat_variant_copy_base() = default; - - flat_variant_copy_base(flat_variant_copy_base&&) = default; - explicit flat_variant_copy_base(flat_variant_copy_base const& right) - : flat_variant_move_base() - // TODO noexcept(Base::is_nothrow_move_constructible) - { - Base& me = *static_cast(this); - Base const& other = *static_cast(&right); - - if (other.is_empty()) { - me.set_slot(empty_slot::value); - } else { - other.visit([&](auto&& value) { -#ifndef NDEBUG - me.set_slot(empty_slot::value); -#endif - me.init(std::move(value), other.get_slot()); - }); - } - } - flat_variant_copy_base& operator=(flat_variant_copy_base&&) = default; - flat_variant_copy_base& operator=(flat_variant_copy_base const& right) - // TODO noexcept(Base::is_nothrow_move_constructible) - { - Base& me = *static_cast(this); - Base const& other = *static_cast(&right); - - me.weak_destroy(); - - if (other.is_empty()) { - me.set_slot(empty_slot::value); - } else { - other.visit([&](auto&& value) { - // ... - me.init(std::move(value), other.get_slot()); - }); - } - return *this; - } -}; -template -struct flat_variant_copy_base : flat_variant_move_base { - constexpr flat_variant_copy_base() = default; - - flat_variant_copy_base(flat_variant_copy_base const&) = delete; - explicit flat_variant_copy_base(flat_variant_copy_base&& right) = default; - flat_variant_copy_base& operator=(flat_variant_copy_base const&) = delete; - flat_variant_copy_base& operator=(flat_variant_copy_base&&) = default; -}; - -/// Deduces to a true_type if all parameters T satisfy the predicate. -template