diff --git a/include/continuable/continuable-base.hpp b/include/continuable/continuable-base.hpp index 09d05d4..8057450 100644 --- a/include/continuable/continuable-base.hpp +++ b/include/continuable/continuable-base.hpp @@ -39,6 +39,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -444,8 +447,9 @@ public: /// \since 1.0.0 template auto operator&&(continuable_base&& right) && { - return detail::composition::connect(detail::composition::strategy_all_tag{}, - std::move(*this), std::move(right)); + return detail::composition::connect( + detail::composition::composition_strategy_all_tag{}, std::move(*this), + std::move(right)); } /// Invokes both continuable_base objects parallel and calls the @@ -486,8 +490,9 @@ public: /// \since 1.0.0 template auto operator||(continuable_base&& right) && { - return detail::composition::connect(detail::composition::strategy_any_tag{}, - std::move(*this), std::move(right)); + return detail::composition::connect( + detail::composition::composition_strategy_any_tag{}, std::move(*this), + std::move(right)); } /// Invokes both continuable_base objects sequential and calls the @@ -514,8 +519,8 @@ public: /// \since 1.0.0 template auto operator>>(continuable_base&& right) && { - return detail::composition::sequential_connect(std::move(*this), - std::move(right)); + return detail::composition::seq::sequential_connect(std::move(*this), + std::move(right)); } /// Invokes the continuation chain manually even before the @@ -591,21 +596,20 @@ private: return materializeImpl(std::move(*this)); } - template < - typename OData, typename OAnnotation, - std::enable_if_t::value>* = - nullptr> + template ::value>* = nullptr> static auto materializeImpl(continuable_base&& continuable) { return std::move(continuable); } - template < - typename OData, typename OAnnotation, - std::enable_if_t::value>* = - nullptr> + template ::value>* = nullptr> static auto materializeImpl(continuable_base&& continuable) { - return detail::composition::finalize_composition(std::move(continuable)); + using finalizer = detail::composition::composition_finalizer; + return finalizer::finalize(std::move(continuable)); } Data&& consume_data() && { diff --git a/include/continuable/detail/composition.hpp b/include/continuable/detail/composition.hpp index 598abb7..499111b 100644 --- a/include/continuable/detail/composition.hpp +++ b/include/continuable/detail/composition.hpp @@ -31,182 +31,33 @@ #ifndef CONTINUABLE_DETAIL_COMPOSITION_HPP_INCLUDED #define CONTINUABLE_DETAIL_COMPOSITION_HPP_INCLUDED -#include -#include -#include #include #include #include -#include #include -#include #include #include +#include namespace cti { namespace detail { /// The namespace `composition` offers methods to chain continuations together /// with `all`, `any` or `seq` logic. namespace composition { -struct strategy_all_tag {}; -struct strategy_any_tag {}; +struct composition_strategy_all_tag {}; +struct composition_strategy_any_tag {}; +struct composition_strategy_seq_tag {}; template -struct is_strategy : std::false_type {}; +struct is_composition_strategy // ... + : std::false_type {}; template <> -struct is_strategy : std::true_type {}; +struct is_composition_strategy // ... + : std::true_type {}; template <> -struct is_strategy : std::true_type {}; - -namespace detail { -template -constexpr void assign(traits::size_constant /*pos*/, T& /*storage*/) { - // ... -} -template -void assign(traits::size_constant pos, T& storage, Current&& current, - Args&&... args) { - // TODO Improve this -> linear instantiation - std::get(storage) = std::forward(current); - assign(pos + traits::size_constant_of<1>(), storage, - std::forward(args)...); -} - -/// Caches the partial results and invokes the callback when all results -/// are arrived. This class is thread safe. -template -class all_result_submitter : public std::enable_shared_from_this< - all_result_submitter>, - public util::non_movable { - - T callback_; - std::atomic left_; - std::once_flag flag_; - std::tuple result_; - - template - void resolve(traits::size_constant from, traits::size_constant, - PartialArgs&&... args) { - - static_assert(sizeof...(args) == (To - From), - "Submission called with the wrong amount of arguments!"); - - // Assign the values from the result to it's correct positions of the - // tuple. Maybe think about the thread safety again...: - // http://stackoverflow.com/questions/40845699 - assign(from, result_, std::forward(args)...); - - // Complete the current result - complete_one(); - } - - template - void resolve(traits::size_constant, traits::size_constant, - types::dispatch_error_tag tag, types::error_type error) { - - // We never complete the composition, but we forward the first error - // which was raised. - std::call_once(flag_, std::move(callback_), tag, std::move(error)); - } - - // Invokes the callback with the cached result - void invoke() { - assert((left_ == 0U) && "Expected that the submitter is finished!"); - std::atomic_thread_fence(std::memory_order_acquire); - traits::unpack(std::move(result_), [&](auto&&... args) { - std::call_once(flag_, std::move(callback_), - std::forward(args)...); - }); - } - // Completes one result - void complete_one() { - assert((left_ > 0U) && "Expected that the submitter isn't finished!"); - - auto const current = --left_; - if (!current) { - invoke(); - } - } - - template - struct partial_all_callback { - std::shared_ptr me_; - - template - void operator()(PartialArgs&&... args) && { - me_->resolve(traits::size_constant{}, traits::size_constant{}, - std::forward(args)...); - } - - template - void set_value(PartialArgs&&... args) { - std::move (*this)(std::forward(args)...); - } - - void set_exception(types::error_type error) { - std::move (*this)(types::dispatch_error_tag{}, std::move(error)); - } - }; - -public: - explicit all_result_submitter(T callback) - : callback_(std::move(callback)), left_(Submissions) { - } - - /// Creates a submitter which submits it's result into the tuple - template - auto create_callback(traits::size_constant /*from*/, - traits::size_constant /*to*/) { - return partial_all_callback{this->shared_from_this()}; - } -}; - -/// Invokes the callback with the first arriving result -template -class any_result_submitter - : public std::enable_shared_from_this>, - public util::non_movable { - - T callback_; - std::once_flag flag_; - - struct any_callback { - std::shared_ptr me_; - - template - void operator()(PartialArgs&&... args) && { - me_->invoke(std::forward(args)...); - } - - template - void set_value(PartialArgs&&... args) { - std::move (*this)(std::forward(args)...); - } - - void set_exception(types::error_type error) { - std::move (*this)(types::dispatch_error_tag{}, std::move(error)); - } - }; - -public: - explicit any_result_submitter(T callback) : callback_(std::move(callback)) { - } - - /// Creates a submitter which submits it's result to the callback - auto create_callback() { - return any_callback{this->shared_from_this()}; - } - -private: - // Invokes the callback with the given arguments - template - void invoke(ActualArgs&&... args) { - std::call_once(flag_, std::move(callback_), - std::forward(args)...); - } -}; -} // namespace detail +struct is_composition_strategy // ... + : std::true_type {}; /// Adds the given continuation tuple to the left composition template @@ -222,8 +73,9 @@ auto chain_composition(std::tuple leftPack, /// Basically we can encounter 3 cases: /// - The continuable isn't in any strategy: /// -> make a tuple containing the continuable as only element -template ::value>* = nullptr> +template < + typename Strategy, typename Data, typename Annotation, + std::enable_if_t::value>* = nullptr> auto normalize(Strategy /*strategy*/, continuable_base&& continuation) { @@ -232,8 +84,9 @@ auto normalize(Strategy /*strategy*/, } /// - The continuable is in a different strategy then the current one: /// -> materialize it -template ::value>* = nullptr> +template < + typename Strategy, typename Data, typename Annotation, + std::enable_if_t::value>* = nullptr> auto normalize(Strategy /*strategy*/, continuable_base&& continuation) { @@ -273,186 +126,11 @@ auto connect(Strategy strategy, continuable_base&& left, return base::attorney::create(std::move(data), strategy, ownership_); } -/// Creates a submitter which caches the intermediate results of `all` chains -template -auto make_all_result_submitter(Callback&& callback, - traits::size_constant, - traits::identity) { - return std::make_shared, Submissions, Args...>>( - std::forward(callback)); -} +/// All strategies should specialize this class in order to provide +/// a finalize static method. +template +struct composition_finalizer; -/// A callable object to merge multiple signature hints together -struct entry_merger { - template - constexpr auto operator()(T&... entries) const noexcept { - return traits::merge(hints::hint_of(traits::identity_of(entries))...); - } -}; - -/// Finalizes the all logic of a given composition -template -auto finalize_composition( - continuable_base&& continuation) { - - auto ownership_ = base::attorney::ownership_of(continuation); - - auto composition = base::attorney::consume_data(std::move(continuation)); - - // Merge all signature hints together - constexpr auto const signature = traits::unpack(composition, entry_merger{}); - - return base::attorney::create( - [ signature, - composition = std::move(composition) ](auto&& callback) mutable { - // We mark the current 2-dimensional position through a pair: - // std::pair, size_constant> - // ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ - // Continuation pos Result pos - constexpr auto const begin = std::make_pair( - traits::size_constant_of<0>(), traits::size_constant_of<0>()); - constexpr auto const pack = traits::identify{}; - constexpr auto const end = traits::pack_size_of(pack); - auto const condition = [=](auto pos) { return pos.first < end; }; - - // Create the result submitter which caches all results and invokes - // the final callback upon completion. - auto submitter = make_all_result_submitter( - std::forward(callback), end, signature); - - // Invoke every continuation with it's callback of the submitter - traits::static_while(begin, condition, [&](auto current) mutable { - auto entry = - std::move(std::get(composition)); - - // This is the length of the arguments of the current continuable - constexpr auto const arg_size = - traits::pack_size_of(hints::hint_of(traits::identity_of(entry))); - - // The next position in the result tuple - constexpr auto const next = current.second + arg_size; - - // Invoke the continuation with the associated submission callback - base::attorney::invoke_continuation( - std::move(entry), - submitter->create_callback(current.second, next)); - - return std::make_pair(current.first + traits::size_constant_of<1>(), - next); - }); - }, - signature, std::move(ownership_)); -} - -/// Creates a submitter that continues `any` chains -template -auto make_any_result_submitter(Callback&& callback) { - return std::make_shared< - detail::any_result_submitter>>( - std::forward(callback)); -} - -template -struct check_pack_empty { - static constexpr auto const is_empty = - (decltype(traits::pack_size_of(std::declval())){} == - traits::size_constant_of<0>()); - static_assert(is_empty.value, "Expected all continuations to have the same" - "count of arguments!"); -}; - -/// A callable object to determine the shared result between all continuations -struct determine_shared_result { - template - constexpr auto operator()(T&... args) const noexcept { - return common_result_of(hints::signature_hint_tag<>{}, - hints::hint_of(traits::identity_of(args))...); - } - -private: - template - static constexpr auto common_result_of(Signature signature, - hints::signature_hint_tag<>, - Args... /*args*/) { - /// Assert that the other signatures are empty too which means all - /// signatures had the same size. - (void)std::initializer_list{0, ((void)check_pack_empty{}, 0)...}; - return signature; - } - - template - static constexpr T first_of(traits::identity) noexcept; - - /// Determine the common result between all continuation which are chained - /// with an `any` strategy, consider two continuations: - /// c1 with `void(int)` and c2 with `void(float)`, the common result shared - /// between both continuations is `void(int)`. - template - static constexpr auto common_result_of(Signature signature, First first, - Args... args) { - using common_type = - traits::identity>; - - return common_result_of(traits::push(signature, common_type{}), - traits::pop_first(first), - traits::pop_first(args)...); - } -}; - -/// Finalizes the any logic of a given composition -template -auto finalize_composition( - continuable_base&& continuation) { - - auto ownership = base::attorney::ownership_of(continuation); - - auto composition = base::attorney::consume_data(std::move(continuation)); - - constexpr auto const signature = - traits::unpack(composition, determine_shared_result{}); - - return base::attorney::create( - [composition = std::move(composition)](auto&& callback) mutable { - - // Create the submitter which calls the given callback once at the first - // callback invocation. - auto submitter = make_any_result_submitter( - std::forward(callback)); - - traits::static_for_each_in(std::move(composition), - [&](auto&& entry) mutable { - // Invoke the continuation with a - // submission callback - base::attorney::invoke_continuation( - std::forward(entry), - submitter->create_callback()); - }); - }, - signature, std::move(ownership)); -} - -/// Connects the left and the right continuable to a sequence -/// -/// \note This is implemented in an eager way because we would not gain -/// any profit from chaining sequences lazily. -template -auto sequential_connect(Left&& left, Right&& right) { - left.freeze(right.is_frozen()); - right.freeze(); - - return std::forward(left).then([right = std::forward(right)]( - auto&&... args) mutable { - return std::move(right).then([previous = std::make_tuple( - std::forward(args)...)]( - auto&&... args) mutable { - return traits::merge( - std::move(previous), - std::make_tuple(std::forward(args)...)); - }); - }); -} } // namespace composition } // namespace detail } // namespace cti diff --git a/include/continuable/detail/composition_all.hpp b/include/continuable/detail/composition_all.hpp new file mode 100644 index 0000000..34bcb50 --- /dev/null +++ b/include/continuable/detail/composition_all.hpp @@ -0,0 +1,234 @@ + +/* + + /~` _ _ _|_. _ _ |_ | _ + \_,(_)| | | || ||_|(_||_)|(/_ + + https://github.com/Naios/continuable + v3.0.0 + + Copyright(c) 2015 - 2018 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_COMPOSITION_ALL_HPP_INCLUDED +#define CONTINUABLE_DETAIL_COMPOSITION_ALL_HPP_INCLUDED + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace cti { +namespace detail { +namespace composition { +namespace all { +template +constexpr void assign(traits::size_constant /*pos*/, T& /*storage*/) { + // ... +} +template +void assign(traits::size_constant pos, T& storage, Current&& current, + Args&&... args) { + // TODO Improve this -> linear instantiation + std::get(storage) = std::forward(current); + assign(pos + traits::size_constant_of<1>(), storage, + std::forward(args)...); +} + +/// Caches the partial results and invokes the callback when all results +/// are arrived. This class is thread safe. +template +class all_result_submitter : public std::enable_shared_from_this< + all_result_submitter>, + public util::non_movable { + + T callback_; + std::atomic left_; + std::once_flag flag_; + std::tuple result_; + + template + void resolve(traits::size_constant from, traits::size_constant, + PartialArgs&&... args) { + + static_assert(sizeof...(args) == (To - From), + "Submission called with the wrong amount of arguments!"); + + // Assign the values from the result to it's correct positions of the + // tuple. Maybe think about the thread safety again...: + // http://stackoverflow.com/questions/40845699 + assign(from, result_, std::forward(args)...); + + // Complete the current result + complete_one(); + } + + template + void resolve(traits::size_constant, traits::size_constant, + types::dispatch_error_tag tag, types::error_type error) { + + // We never complete the composition, but we forward the first error + // which was raised. + std::call_once(flag_, std::move(callback_), tag, std::move(error)); + } + + // Invokes the callback with the cached result + void invoke() { + assert((left_ == 0U) && "Expected that the submitter is finished!"); + std::atomic_thread_fence(std::memory_order_acquire); + traits::unpack(std::move(result_), [&](auto&&... args) { + std::call_once(flag_, std::move(callback_), + std::forward(args)...); + }); + } + // Completes one result + void complete_one() { + assert((left_ > 0U) && "Expected that the submitter isn't finished!"); + + auto const current = --left_; + if (!current) { + invoke(); + } + } + + template + struct partial_all_callback { + std::shared_ptr me_; + + template + void operator()(PartialArgs&&... args) && { + me_->resolve(traits::size_constant{}, traits::size_constant{}, + std::forward(args)...); + } + + template + void set_value(PartialArgs&&... args) { + std::move (*this)(std::forward(args)...); + } + + void set_exception(types::error_type error) { + std::move (*this)(types::dispatch_error_tag{}, std::move(error)); + } + }; + +public: + explicit all_result_submitter(T callback) + : callback_(std::move(callback)), left_(Submissions) { + } + + /// Creates a submitter which submits it's result into the tuple + template + auto create_callback(traits::size_constant /*from*/, + traits::size_constant /*to*/) { + return partial_all_callback{this->shared_from_this()}; + } +}; + +/// Creates a submitter which caches the intermediate results of `all` chains +template +auto make_all_result_submitter(Callback&& callback, + traits::size_constant, + traits::identity) { + return std::make_shared, + Submissions, Args...>>( + std::forward(callback)); +} + +/// A callable object to merge multiple signature hints together +struct entry_merger { + template + constexpr auto operator()(T&... entries) const noexcept { + return traits::merge(hints::hint_of(traits::identity_of(entries))...); + } +}; +} // namespace all + +/// Finalizes the all logic of a given composition +template <> +struct composition_finalizer { + /// Finalizes the all logic of a given composition + template + static auto finalize(Continuable&& continuation) { + + auto ownership_ = base::attorney::ownership_of(continuation); + + auto composition = + base::attorney::consume_data(std::forward(continuation)); + + // Merge all signature hints together + constexpr auto const signature = + traits::unpack(composition, all::entry_merger{}); + + return base::attorney::create( + [ signature, + composition = std::move(composition) ](auto&& callback) mutable { + // We mark the current 2-dimensional position through a pair: + // std::pair, size_constant> + // ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ + // Continuation pos Result pos + constexpr auto const begin = std::make_pair( + traits::size_constant_of<0>(), traits::size_constant_of<0>()); + constexpr auto const pack = traits::identify{}; + constexpr auto const end = traits::pack_size_of(pack); + auto const condition = [=](auto pos) { return pos.first < end; }; + + // Create the result submitter which caches all results and invokes + // the final callback upon completion. + auto submitter = all::make_all_result_submitter( + std::forward(callback), end, signature); + + // Invoke every continuation with it's callback of the submitter + traits::static_while(begin, condition, [&](auto current) mutable { + auto entry = std::move( + std::get(composition)); + + // This is the length of the arguments of the current continuable + constexpr auto const arg_size = traits::pack_size_of( + hints::hint_of(traits::identity_of(entry))); + + // The next position in the result tuple + constexpr auto const next = current.second + arg_size; + + // Invoke the continuation with the associated submission callback + base::attorney::invoke_continuation( + std::move(entry), + submitter->create_callback(current.second, next)); + + return std::make_pair(current.first + traits::size_constant_of<1>(), + next); + }); + }, + signature, std::move(ownership_)); + } +}; +} // namespace composition +} // namespace detail +} // namespace cti + +#endif // CONTINUABLE_DETAIL_COMPOSITION_ALL_HPP_INCLUDED diff --git a/include/continuable/detail/composition_any.hpp b/include/continuable/detail/composition_any.hpp new file mode 100644 index 0000000..c01144f --- /dev/null +++ b/include/continuable/detail/composition_any.hpp @@ -0,0 +1,191 @@ + +/* + + /~` _ _ _|_. _ _ |_ | _ + \_,(_)| | | || ||_|(_||_)|(/_ + + https://github.com/Naios/continuable + v3.0.0 + + Copyright(c) 2015 - 2018 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_COMPOSITION_ANY_HPP_INCLUDED +#define CONTINUABLE_DETAIL_COMPOSITION_ANY_HPP_INCLUDED + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace cti { +namespace detail { +namespace composition { +namespace any { +/// Invokes the callback with the first arriving result +template +class any_result_submitter + : public std::enable_shared_from_this>, + public util::non_movable { + + T callback_; + std::once_flag flag_; + + struct any_callback { + std::shared_ptr me_; + + template + void operator()(PartialArgs&&... args) && { + me_->invoke(std::forward(args)...); + } + + template + void set_value(PartialArgs&&... args) { + std::move (*this)(std::forward(args)...); + } + + void set_exception(types::error_type error) { + std::move (*this)(types::dispatch_error_tag{}, std::move(error)); + } + }; + +public: + explicit any_result_submitter(T callback) : callback_(std::move(callback)) { + } + + /// Creates a submitter which submits it's result to the callback + auto create_callback() { + return any_callback{this->shared_from_this()}; + } + +private: + // Invokes the callback with the given arguments + template + void invoke(ActualArgs&&... args) { + std::call_once(flag_, std::move(callback_), + std::forward(args)...); + } +}; + +/// Creates a submitter that continues `any` chains +template +auto make_any_result_submitter(Callback&& callback) { + return std::make_shared< + any_result_submitter>>( + std::forward(callback)); +} + +template +struct check_pack_empty { + static constexpr auto const is_empty = + (decltype(traits::pack_size_of(std::declval())){} == + traits::size_constant_of<0>()); + static_assert(is_empty.value, "Expected all continuations to have the same" + "count of arguments!"); +}; + +/// A callable object to determine the shared result between all continuations +struct determine_shared_result { + template + constexpr auto operator()(T&... args) const noexcept { + return common_result_of(hints::signature_hint_tag<>{}, + hints::hint_of(traits::identity_of(args))...); + } + +private: + template + static constexpr auto common_result_of(Signature signature, + hints::signature_hint_tag<>, + Args... /*args*/) { + /// Assert that the other signatures are empty too which means all + /// signatures had the same size. + (void)std::initializer_list{0, ((void)check_pack_empty{}, 0)...}; + return signature; + } + + template + static constexpr T first_of(traits::identity) noexcept; + + /// Determine the common result between all continuation which are chained + /// with an `any` strategy, consider two continuations: + /// c1 with `void(int)` and c2 with `void(float)`, the common result shared + /// between both continuations is `void(int)`. + template + static constexpr auto common_result_of(Signature signature, First first, + Args... args) { + using common_type = + traits::identity>; + + return common_result_of(traits::push(signature, common_type{}), + traits::pop_first(first), + traits::pop_first(args)...); + } +}; +} // namespace any + +/// Finalizes the any logic of a given composition +template <> +struct composition_finalizer { + template + static auto finalize(Continuable&& continuation) { + + auto ownership = base::attorney::ownership_of(continuation); + + auto composition = + base::attorney::consume_data(std::forward(continuation)); + + constexpr auto const signature = + traits::unpack(composition, any::determine_shared_result{}); + + return base::attorney::create( + [composition = std::move(composition)](auto&& callback) mutable { + + // Create the submitter which calls the given callback once at the + // first callback invocation. + auto submitter = any::make_any_result_submitter( + std::forward(callback)); + + traits::static_for_each_in(std::move(composition), + [&](auto&& entry) mutable { + // Invoke the continuation with a + // submission callback + base::attorney::invoke_continuation( + std::forward(entry), + submitter->create_callback()); + }); + }, + signature, std::move(ownership)); + } +}; +} // namespace composition +} // namespace detail +} // namespace cti + +#endif // CONTINUABLE_DETAIL_COMPOSITION_ANY_HPP_INCLUDED diff --git a/include/continuable/detail/composition_seq.hpp b/include/continuable/detail/composition_seq.hpp new file mode 100644 index 0000000..0a04464 --- /dev/null +++ b/include/continuable/detail/composition_seq.hpp @@ -0,0 +1,82 @@ + +/* + + /~` _ _ _|_. _ _ |_ | _ + \_,(_)| | | || ||_|(_||_)|(/_ + + https://github.com/Naios/continuable + v3.0.0 + + Copyright(c) 2015 - 2018 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_COMPOSITION_SEQ_HPP_INCLUDED +#define CONTINUABLE_DETAIL_COMPOSITION_SEQ_HPP_INCLUDED + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace cti { +namespace detail { +namespace composition { +namespace seq { +/// Connects the left and the right continuable to a sequence +/// +/// \note This is implemented in an eager way because we would not gain +/// any profit from chaining sequences lazily. +template +auto sequential_connect(Left&& left, Right&& right) { + left.freeze(right.is_frozen()); + right.freeze(); + + return std::forward(left).then([right = std::forward(right)]( + auto&&... args) mutable { + return std::move(right).then([previous = std::make_tuple( + std::forward(args)...)]( + auto&&... args) mutable { + return traits::merge( + std::move(previous), + std::make_tuple(std::forward(args)...)); + }); + }); +} +} // namespace seq + +/// Finalizes the seq logic of a given composition +template <> +struct composition_finalizer { + /// TODO +}; +} // namespace composition +} // namespace detail +} // namespace cti + +#endif // CONTINUABLE_DETAIL_COMPOSITION_SEQ_HPP_INCLUDED