diff --git a/include/continuable/detail/composition-all.hpp b/include/continuable/detail/composition-all.hpp index b6ef905..cae3c64 100644 --- a/include/continuable/detail/composition-all.hpp +++ b/include/continuable/detail/composition-all.hpp @@ -108,15 +108,12 @@ class result_submitter assert((left_ == 0U) && "Expected that the submitter is finished!"); std::atomic_thread_fence(std::memory_order_acquire); - auto cleaned = - map_pack(remapping::unpack_result_guards{}, std::move(result_)); - // Call the final callback with the cleaned result - traits::unpack(std::move(cleaned), [&](auto&&... args) { - std::call_once(flag_, std::move(callback_), - std::forward(args)...); + std::call_once(flag_, [&](auto&&... args) { + remapping::finalize_data(std::move(callback_), std::move(result_)); }); } + // Completes one result void complete_one() { assert((left_ > 0U) && "Expected that the submitter isn't finished!"); @@ -127,16 +124,16 @@ class result_submitter } } - template + template struct partial_all_callback { - Target* target; + Box* box; std::shared_ptr me; template void operator()(Args&&... args) && { // Assign the result to the target - *target = remapping::wrap(std::forward(args)...); + box->assign(std::forward(args)...); // Complete one result me->complete_one(); @@ -157,11 +154,11 @@ public: } /// Creates a submitter which submits it's result into the storage - template - auto create_callback(Target* target) { + template + auto create_callback(Box* box) { left_.fetch_add(1, std::memory_order_seq_cst); - return partial_all_callback>{target, - this->shared_from_this()}; + return partial_all_callback>{box, + this->shared_from_this()}; } /// Initially the counter is created with an initial count of 1 in order @@ -171,7 +168,7 @@ public: complete_one(); } - constexpr Result* result_ptr() noexcept { + constexpr auto& head() noexcept { return &result_; } }; @@ -180,12 +177,11 @@ template struct continuable_dispatcher { std::shared_ptr& submitter; - template >::value>* = nullptr> - void operator()(Index* index, Target* target) const { + template >::value>* = nullptr> + void operator()(Box&& box) const { // Retrieve a callback from the submitter and attach it to the continuable - std::move(*index).next(submitter->create_callback(target)).done(); + box.fetch().next(submitter->create_callback(std::addressof(box))).done(); } }; } // namespace all @@ -205,7 +201,7 @@ struct composition_finalizer { (auto&& callback) mutable { // Create the target result from the composition - auto result = remapping::create_result_pack(std::move(composition)); + auto result = remapping::box_continuables(std::move(composition)); using submitter_t = all::result_submitter, @@ -217,10 +213,8 @@ struct composition_finalizer { // Dispatch the continuables and store its partial result // in the whole result - // TODO Fix use after move here - remapping::relocate_index_pack( - all::continuable_dispatcher{state}, &composition, - state->result_ptr()); + traverse_pack(all::continuable_dispatcher{state}, + state->head()); // Finalize the composition if all results arrived in-place state->accept(); diff --git a/include/continuable/detail/composition-remapping.hpp b/include/continuable/detail/composition-remapping.hpp index 5405b98..2c476fe 100644 --- a/include/continuable/detail/composition-remapping.hpp +++ b/include/continuable/detail/composition-remapping.hpp @@ -55,171 +55,154 @@ namespace composition { /// - multiple async value -> tuple of async values. namespace remapping { // Guard object for representing void results -struct void_result_guard {}; -// Guard object for representing multiple results -template -struct multi_result_guard { - std::tuple result_; +template +class continuable_box; +template +class continuable_box>> { - multi_result_guard& operator=(std::tuple result) { - result_ = std::move(result); - return *this; + continuable_base> continuable_; + +public: + explicit continuable_box( + continuable_base>&& continuable) + : continuable_(std::move(continuable)) { } -}; -// Callable object that maps void_result_guard zo zero arguments -struct unpack_result_guards { - auto operator()(void_result_guard) const noexcept { + continuable_base>&& fetch() { + return std::move(continuable_); + } + + void assign() { + } + + auto unbox() && { return spread_this(); } - template - auto operator()(multi_result_guard guard) const noexcept { - // Spread the result of the continuable into the current depth. - return traits::unpack(std::move(guard.result_), [](auto&&... args) { +}; +template +class continuable_box< + continuable_base>> { + + continuable_base> continuable_; + First first_; + +public: + explicit continuable_box( + continuable_base>&& continuable) + : continuable_(std::move(continuable)) { + } + + continuable_base>&& fetch() { + return std::move(continuable_); + } + + void assign(First first) { + first_ = std::move(first); + } + + auto unbox() && { + return std::move(first_); + } +}; +template +class continuable_box< + continuable_base>> { + + continuable_base> + continuable_; + std::tuple args_; + +public: + explicit continuable_box( + continuable_base>&& + continuable) + : continuable_(std::move(continuable)) { + } + + continuable_base>&& + fetch() { + return std::move(continuable_); + } + + void assign(First first, Second second, Rest... rest) { + args_ = std::make_tuple(std::move(first), std::move(second), + std::move(rest)...); + } + + auto unbox() && { + return traits::unpack(std::move(args_), [](auto&&... args) { return spread_this(std::forward(args)...); }); } }; -constexpr void_result_guard wrap() { - return {}; -} -template -constexpr decltype(auto) wrap(First&& first) { - return std::forward(first); -} -template -constexpr decltype(auto) wrap(First&& first, Second&& second, Rest&&... rest) { - return std::make_tuple(std::forward(first), - std::forward(second), - std::forward(rest)...); -} +template +struct is_continuable_box : std::false_type {}; +template +struct is_continuable_box> : std::true_type {}; namespace detail { -struct result_extractor_mapper { - /// Create slots for a void result which is removed later. - /// This is required due to the fact that each continuable has exactly - /// one matching valuen inside the result tuple. - static constexpr auto initialize(hints::signature_hint_tag<>) noexcept { - return void_result_guard{}; - } - /// Initialize a single value - template - static constexpr auto initialize(hints::signature_hint_tag) { - return First{}; - } - /// Initialize a multiple values as tuple - template - static constexpr auto - initialize(hints::signature_hint_tag) { - // TODO Fix non default constructible values - return multi_result_guard{ - std::make_tuple(First{}, Second{}, Args{}...)}; - } - - /// Remap a continuable to its corresponding result values - /// A void result is mapped to a guard type, single values to the value - /// itself and multiple ones to a tuple of values. +/// Maps a deeply nested pack of continuables to a continuable_box +struct continuable_box_packer { template < typename T, std::enable_if_t>::value>* = nullptr> - auto operator()(T&& /*continuable*/) { - auto constexpr const hint = hints::hint_of(traits::identify{}); - return initialize(hint); + auto operator()(T&& continuable) { + return continuable_box>{std::forward(continuable)}; } }; - -/// Relocates the target of a deeply nested pack of indexed_continuable objects -/// to the given target. -template -struct result_relocator_mapper { - Evaluator evaluator; - - template - void traverse_one(std::false_type, Index*, Result*) { - // Don't do anything when dealing with casual objects - } - template - void traverse_one(std::true_type, Index* index, Result* result) { - - // Call the evaluator with the address of the indexed object and its target - evaluator(index, result); - } - template - void traverse(traversal::container_category_tag, Index* index, - Result* result) { - - traverse_one(traits::is_invocable{}, index, - result); - } - - /// Traverse a homogeneous container - template - void traverse(traversal::container_category_tag, - Index* index, Result* result) { - auto index_itr = index->begin(); - auto const index_end = index->end(); - - auto result_itr = result->begin(); - auto const result_end = result->end(); - - using element_t = std::decay_tbegin())>; - traversal::container_category_of_t constexpr const tag; - - for (; index_itr != index_end; ++index_itr, ++result_itr) { - assert(result_itr != result_end); - traverse(tag, &*index_itr, &*result_itr); - } - } - - template - void traverse_tuple_like(std::integer_sequence, - Index* index, Result* result) { - - (void)std::initializer_list{ - ((void)traverse(traversal::container_category_of_t< - std::decay_t(*index))>>{}, - &std::get(*index), &std::get(*result)), - 0)...}; - - (void)index; - (void)result; - } - - /// Traverse tuple like container - template - void traverse(traversal::container_category_tag, Index* index, - Result* result) { - - std::make_index_sequence::value> constexpr const i{}; - - traverse_tuple_like(i, index, result); +/// Maps a deeply nested pack of continuable_boxes to its result +struct continuable_box_unpacker { + template < + typename T, + std::enable_if_t>::value>* = nullptr> + auto operator()(T&& box) { + return std::forward(box).unpack(); } }; } // namespace detail -/// Returns the result pack of the given deeply nested pack. -/// This invalidates all non-continuable values contained inside the pack. -/// -/// This consumes all non continuables inside the pack. +/// Returns the boxed pack of the given deeply nested pack. +/// This transforms all continuables into a continuable_box which is +/// capable of caching the result from the corresponding continuable. template -constexpr auto create_result_pack(Args&&... args) { - return cti::map_pack(detail::result_extractor_mapper{}, +constexpr auto box_continuables(Args&&... args) { + return cti::map_pack(detail::continuable_box_packer{}, std::forward(args)...); } -/// Sets the target pointers of indexed_continuable's inside the index pack -/// to point to their given counterparts inside the given target. -template -constexpr void relocate_index_pack(Relocator&& relocator, Index* index, - Target* target) { +/// Returns the unboxed pack of the given deeply nested boxed pack. +/// This transforms all continuable_boxes into its result. +template +constexpr auto unbox_continuables(Args&&... args) { + return cti::map_pack(detail::continuable_box_unpacker{}, + std::forward(args)...); +} - constexpr traversal::container_category_of_t> const tag; +namespace detail { +template +void finalize_impl(traits::identity, Callback&& callback, Data&&) { + std::forward(callback)(); +} +template +void finalize_impl(traits::identity, Callback&& callback, Data&& data) { + // Call the final callback with the cleaned result + traits::unpack(unbox_continuables(std::forward(data)), + std::forward(callback)); +} +} // namespace detail - detail::result_relocator_mapper> mapper{ - std::forward(relocator)}; +template +void finalize_data(Callback&& callback, Data&& data) { + using result_t = + decltype(traits::unpack(unbox_continuables(std::forward(data)), + std::forward(callback))); - mapper.traverse(tag, index, target); + // Guard the final result against void + return detail::finalize_impl(traits::identity{}, + std::forward(data), + std::forward(callback)); } } // namespace remapping } // namespace composition diff --git a/include/continuable/detail/composition-seq.hpp b/include/continuable/detail/composition-seq.hpp index fb95337..fbe64b1 100644 --- a/include/continuable/detail/composition-seq.hpp +++ b/include/continuable/detail/composition-seq.hpp @@ -69,65 +69,10 @@ auto sequential_connect(Left&& left, Right&& right) { }); } -/// Contains an continuable together with a location where the -/// result shall be stored. -template -struct indexed_continuable { - Continuable continuable; - Target* target; -}; - -template -struct is_indexed_continuable : std::false_type {}; -template -struct is_indexed_continuable> - : std::true_type {}; - -/// Maps a deeply nested pack of continuables to an indexed continuable -struct result_indexer_mapper { - /// Index a given continuable together with its target location - template < - typename T, - std::enable_if_t>::value>* = nullptr> - auto operator()(T&& continuable) { - auto constexpr const hint = hints::hint_of(traits::identify{}); - - using target = - decltype(remapping::detail::result_extractor_mapper::initialize(hint)); - - using type = indexed_continuable, target>; - - // We have to pass the continuables as l-value so we can move the whole pack - // afterwards as r-value, thus we move the continuable from a l-value here. - // NOLINTNEXTLINE(misc-move-forwarding-reference) - return type{std::move(continuable), nullptr}; - } -}; - -/// Returns the result pack of the given deeply nested pack. -/// This invalidates all non-continuable values contained inside the pack. -/// -/// This consumes all continuables inside the pack. -template -constexpr auto create_index_pack(Args&&... args) { - return cti::map_pack(result_indexer_mapper{}, std::forward(args)...); -} - -struct index_relocator { - template >::value>* = nullptr> - void operator()(Index* index, Target* target) const noexcept { - // Assign the address of the target to the indexed continuable - index->target = target; - } -}; - -template +template struct sequential_dispatch_data { Callback callback; - Index index; - Result result; + Box box; }; template @@ -139,37 +84,29 @@ class sequential_dispatch_visitor public: explicit sequential_dispatch_visitor(Data&& data) : data_(std::move(data)) { - // Assign the address of each result target to the corresponding - // indexed continuable. - remapping::relocate_index_pack(index_relocator{}, &data_.index, - &data_.result); } virtual ~sequential_dispatch_visitor() = default; /// Returns the pack that should be traversed auto& head() { - return data_.index; + return data_.box; } - template >::value>* = nullptr> - bool operator()(async_traverse_visit_tag, Index&& /*index*/) { + template >::value>* = nullptr> + bool operator()(async_traverse_visit_tag, Box&& /*box*/) { return false; } - template - void operator()(async_traverse_detach_tag, Index&& index, N&& next) { - assert(index.target && "The target should be non null here!" - "Probably this is caused through a bug in " - "result_relocator_mapper!"); - - std::move(index.continuable) - .then([ target = index.target, + template + void operator()(async_traverse_detach_tag, Box&& box, N&& next) { + box->fetch() + .then([ box = std::addressof(box), next = std::forward(next) ](auto&&... args) mutable { // Assign the result to the target - *target = remapping::wrap(std::forward(args)...); + box->assign(std::forward(args)...); // Continue the asynchronous sequential traversal next(); @@ -182,24 +119,10 @@ public: .done(); } - void finalize(traits::identity) { - std::move(data_.callback)(); - } - template - void finalize(traits::identity) { - auto cleaned = - map_pack(remapping::unpack_result_guards{}, std::move(data_.result)); - - // Call the final callback with the cleaned result - traits::unpack(std::move(cleaned), std::move(data_.callback)); - } - template void operator()(async_traverse_complete_tag, T&& /*pack*/) { - // Guard the final result against void - using result_t = decltype( - map_pack(remapping::unpack_result_guards{}, std::move(data_.result))); - finalize(traits::identity{}); + return remapping::finalize_data(std::move(data_.callback), + std::move(data_.box)); } }; } // namespace seq @@ -220,21 +143,19 @@ struct composition_finalizer { return [composition = std::forward(composition)] // ... (auto&& callback) mutable { - auto index = seq::create_index_pack(composition); - auto result = remapping::create_result_pack(std::move(composition)); + auto boxed = remapping::box_continuables(std::move(composition)); // The data from which the visitor is constructed in-place using data_t = seq::sequential_dispatch_data, - std::decay_t, - std::decay_t>; + std::decay_t>; // The visitor type using visitor_t = seq::sequential_dispatch_visitor; - traverse_pack_async(async_traverse_in_place_tag{}, - data_t{std::forward(callback), - std::move(index), std::move(result)}); + traverse_pack_async( + async_traverse_in_place_tag{}, + data_t{std::forward(callback), std::move(boxed)}); }; } };