/** * Copyright 2015 Denis Blank * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _CONTINUABLE_H_ #define _CONTINUABLE_H_ // debug #include #include #include #include "Callback.h" template class Continuable; namespace detail { /// Trait to identify continuable types template struct is_continuable : std::false_type { }; template struct is_continuable> : std::true_type { }; template struct continuable_corrector; template struct continuable_unwrap; } // detail /// A continuable provides useful methods to react on the result of callbacks /// and allows to chain multiple callback calls to a chain. template class Continuable { // Make all templates of Continuable to a friend. template friend class Continuable; public: typedef Callback<_ATy...> CallbackFunction; typedef std::function&&)> ForwardFunction; private: /// Functional which expects a callback that is inserted from the Continuable /// to chain everything together ForwardFunction _callback_insert; /// Was the continuable released (invoked or transfered ownership) already? bool _released; template void invoke(_CTy&& callback) { if (!_released) { // Invalidate this _released = true; // Invoke this _callback_insert(std::forward<_CTy>(callback)); } } /// Internal constructor for continuation template Continuable(_FTy&& callback_insert, Continuable<_RATy...>&& right) : _callback_insert(std::forward<_FTy>(callback_insert)), _released(right._released) { right._released = true; } public: /// Deleted copy construct Continuable(Continuable const&) = delete; /// Move construct Continuable(Continuable&& right) : _callback_insert(std::move(right._callback_insert)), _released(right._released) { right._released = true; } // Construct through a ForwardFunction template Continuable(_FTy&& callback_insert) : _callback_insert(std::forward<_FTy>(callback_insert)), _released(false) { } /// Destructor which calls the dispatch chain if needed. ~Continuable() { // Dispatch everything. if (!_released) { // Set released to true to prevent multiple calls _released = true; // Invoke everything with an empty callback _callback_insert([](_ATy&&...) { }); } } /// Deleted copy assign Continuable& operator= (Continuable const&) = delete; /// Deleted move assign Continuable& operator= (Continuable&&) = delete; /// Waits for this continuable and invokes the given callback. template auto then(_CTy&& functional) -> typename detail::continuable_unwrap<_CTy, _ATy...>::continuable_t { ForwardFunction&& callback = std::move(_callback_insert); auto&& corrected = detail::continuable_corrector<_ATy...>::correct(std::forward<_CTy>(functional)); return typename detail::continuable_unwrap<_CTy, _ATy...>::continuable_t( [corrected, callback](typename detail::continuable_unwrap<_CTy, _ATy...>::callback_t&& call_next) { callback([corrected, call_next](_ATy&&... args) mutable { // Invoke the next callback corrected(std::forward<_ATy>(args)...).invoke(std::move(call_next)); }); }, std::move(*this)); } /* template auto all(_CTy&&... functionals) -> typename detail::multiple_when_all_chainer_t< fu::identity<_ATy...>, fu::identity<_CTy...> >::make_result::continuable_t { return then( detail::multiple_when_all_chainer_t< fu::identity<_ATy...>, fu::identity<_CTy...> >::make_when_all(std::forward<_CTy>(functionals)...)); } /// Placeholder template Continuable some(std::size_t const count, _CTy&&...) { return std::move(*this); } /// Placeholder template auto any(_CTy&&... functionals) -> Continuable // FIXME gcc build &-> decltype(some(1, std::declval<_CTy>()...)) { // Equivalent to invoke `some` with count 1. return some(1, std::forward<_CTy>(functionals)...); } /// Validates the Continuable inline Continuable Validate() { _released = false; return *this; } /// Invalidates the Continuable inline Continuable& Invalidate() { _released = true; return *this; } */ }; namespace detail { // TODO remove this template struct ContinuableFactory; template struct ContinuableFactory<_RTy, ::fu::identity&&>> { template static auto CreateFrom(_FTy&& functional) -> Continuable<_ATy...> { return Continuable<_ATy...>( typename Continuable<_ATy...>::ForwardFunction(std::forward<_FTy>(functional))); } }; template using continuable_factory_t = ContinuableFactory< ::fu::return_type_of_t<_FTy>, ::fu::argument_type_of_t<_FTy>>; } /// Wraps a functional object which expects a r-value callback as argument into a continuable. /// The callable is invoked when the continuable shall continue. /// For example: /// make_continuable([](Callback&& callback) /// { /// /* Continue here */ /// callback(5); /// }); template inline auto make_continuable(_FTy&& functional) -> decltype(detail::continuable_factory_t<_FTy>::CreateFrom(std::declval<_FTy>())) { return detail::continuable_factory_t<_FTy>::CreateFrom(std::forward<_FTy>(functional)); } /// Creates an empty continuable. inline auto make_continuable() -> Continuable<> { return make_continuable([](Callback<>&& callback) { callback(); }); } namespace detail { namespace correctors { /// Corrects functionals with non expected signatures /// to match the expected ones. /// Used in `partial_signature_corrector` template struct partial_signature_corrector { /// Corrector template static auto correct(_CTy&& functional) -> typename std::enable_if< !std::is_same< fu::argument_type_of_t< typename std::decay<_CTy>::type >, fu::identity<_ATy...> >::value, std::function< fu::return_type_of_t< typename std::decay<_CTy>::type >(_ATy...) > >::type { // Make use of std::bind's signature erasure return std::bind(std::forward<_CTy>(functional)); } // Route through template static auto correct(_CTy&& functional) -> typename std::enable_if< std::is_same< fu::argument_type_of_t< typename std::decay<_CTy>::type >, fu::identity<_ATy...> >::value, _CTy >::type { return std::forward<_CTy>(functional); } }; } // namespace correctors /// Corrector for given Continuables. template struct continuable_corrector { /// Wrap void returning functionals to return an empty continuable. /// Example: /// void(int, float) to Continuable<>(int, float) template static auto void_returning_corrector(_CTy&& functional) -> typename std::enable_if< std::is_void< fu::return_type_of_t< typename std::decay<_CTy>::type > >::value, std::function(_ATy...)> >::type { return [functional](_ATy&&... args) { // Invoke the original callback functional(std::forward<_ATy>(args)...); // Return an empty continuable return make_continuable(); }; } /// Route continuable returning functionals through. template static auto void_returning_corrector(_CTy&& functional) -> typename std::enable_if< !std::is_void< fu::return_type_of_t< typename std::decay<_CTy>::type > >::value, _CTy>::type { return std::forward<_CTy>(functional); } template static inline auto partial_signature_corrector(_CTy&& functional) -> decltype(correctors::partial_signature_corrector<_ATy...>::correct(std::declval<_CTy>())) { return correctors::partial_signature_corrector<_ATy...>::correct(std::forward<_CTy>(functional)); } /// Wrap Continuables into the continuable returning functional type. template static auto unboxed_continuable_corrector(_CTy&& continuable) -> typename std::enable_if< detail::is_continuable< typename std::decay<_CTy>::type >::value, std::function< typename std::decay<_CTy>::type(_ATy...) > >::type { static_assert(std::is_rvalue_reference<_CTy&&>::value, "Given continuable must be passed as r-value!"); // Trick C++11 lambda capture rules for non move-only Continuables. // TODO Use the stack instead of heap variables. std::shared_ptr::type> shared_continuable = std::make_shared::type>(std::forward<_CTy>(continuable)); // Create a fake function which returns the value on invoke. return [shared_continuable](_ATy&&...) mutable { return std::move(*shared_continuable); }; } /// `unboxed_continuable_corrector`: Converts plain Continuables to continuable retuning functions. template static auto correct_stage(_CTy&& functional) -> typename std::enable_if< detail::is_continuable< typename std::decay<_CTy>::type >::value, decltype(unboxed_continuable_corrector(std::declval<_CTy>())) >::type { return unboxed_continuable_corrector(std::forward<_CTy>(functional)); } /// `partial_signature_corrector`: Converts functionals with not matching args signature. /// `void_returning_corrector`: Converts void return to empty continuable. template static auto correct_stage(_CTy&& functional) -> typename std::enable_if< !detail::is_continuable< typename std::decay<_CTy>::type >::value, decltype(void_returning_corrector(partial_signature_corrector(std::declval<_CTy>()))) >::type { return void_returning_corrector(partial_signature_corrector(std::forward<_CTy>(functional))); } /// Accepts and corrects user given functionals through several stages into the form: /// Continuable<_CArgs...>(_FArgs) template static inline auto correct(_CTy&& functional) -> decltype(correct_stage(std::declval<_CTy>())) { return correct_stage(std::forward<_CTy>(functional)); } }; // struct continuable_corrector // Unwraps the corrected type of Continuables template struct continuable_unwrap { // Corrected user given functional typedef decltype(continuable_corrector<_ATy...>:: correct(std::declval::type>())) corrected_t; typedef fu::return_type_of_t continuable_t; typedef fu::argument_type_of_t arguments_t; typedef typename continuable_t::CallbackFunction callback_t; typedef fu::argument_type_of_t callback_arguments_t; }; // struct continuable_unwrap /* template struct concat_identities; template struct concat_identities, fu::identity> { typedef fu::identity type; }; template struct identity_to_tuple; template struct identity_to_tuple> { typedef std::tuple type; }; /// Position wrapper class to pass ints as type template struct partial_result { enum my_size : std::size_t { size = Position }; typedef Tuple tuple; }; /// Continuable processing detail implementation template struct functional_traits { template struct multiple_result_maker; template struct multiple_result_maker, fu::identity> { typedef fu::identity arguments_t; typedef fu::identity partial_results_t; enum my_size : std::size_t { size = Position }; }; template struct multiple_result_maker : multiple_result_maker< Position + std::tuple_size< typename identity_to_tuple< typename continuable_unwrap::callback_arguments_t >::type >::value, typename concat_identities< Args, typename continuable_unwrap::callback_arguments_t >::type, typename concat_identities< Pack, fu::identity< partial_result< Position, typename identity_to_tuple< typename continuable_unwrap::callback_arguments_t >::type > > >::type, Rest... > { }; template using result_maker_of_t = multiple_result_maker<0, fu::identity<>, fu::identity<>, Args...>; }; template class multiple_result_storage_t, fu::identity<_RTy...>, fu::identity<_PTy...>> { template friend struct multiple_result_storage_invoker_t; std::size_t partitions_left; std::tuple<_RTy...> result; Callback<_RTy...> callback; std::mutex lock; public: multiple_result_storage_t(std::size_t partitions, Callback<_RTy...> callback_) : partitions_left(partitions), callback(callback_) { } void try_invoke() { // TODO Improve the lock here std::lock_guard guard(lock); { // Never call callbacks twice! // assert(partitions_left); // If all partitions have completed invoke the final callback. if (--partitions_left == 0) { fu::invoke_from_tuple(std::move(callback), std::move(result)); } } } }; template struct multiple_result_storage_invoker_t, fu::identity<_RTy...>, fu::identity<_PTy...>> { template using move_position_to = multiple_result_storage_invoker_t, fu::identity<_RTy...>, fu::identity<_PTy...>>; template inline static void partial_set(Tuple& result, Current&& current) { // Store a single callback result in the tuple std::get(result) = std::forward(current); } template inline static void partial_set(Tuple& result, Current&& current, Rest&&... rest) { // Set the result... partial_set(result, std::forward(current)); // ...and continue with the next parameter. move_position_to::partial_set(result, std::forward(rest)...); } // Do nothing when trying to store empty packs... inline static void store(std::tuple<_RTy...>&) { } // Store the args in the result tuple template inline static void store(std::tuple<_RTy...>& result, Args&&... args) { partial_set(result, std::forward(args)...); } template static void invoke(std::shared_ptr, fu::identity<_RTy...>, fu::identity<_PTy...>>> storage, Continuable&& continuable) { // Invoke the continuable continuable.invoke([storage](Args&&... args) { // Route its result to the cache. store(storage->result, std::forward(args)...); // Try to invoke the final callback. storage->try_invoke(); }); } }; template struct multiple_when_all_chainer_t_make_result; template struct multiple_when_all_chainer_t_make_result, fu::identity<_RTy...>, fu::identity<_PTy...>> { typedef Continuable<_RTy...> continuable_t; typedef std::function return_t; typedef functional_traits<_ATy...> traits_t; typedef multiple_result_storage_t, fu::identity<_RTy...>, fu::identity<_PTy...>> ResultStorage; template using invoker_at = multiple_result_storage_invoker_t, fu::identity<_RTy...>, fu::identity<_PTy...>>; typedef std::shared_ptr shared_result_t; typedef std::tuple<_ATy...> shared_args_t; template struct distributor; template struct distributor, Stack...> { /// Real function invocation template inline static void invoke(shared_result_t storage, Arguments&& args, _CTy&& current) { // Invoke the continuable from the result storage invoker_at::invoke( storage, fu::invoke_from_tuple( traits_t::correct(std::forward<_CTy>(current)), std::forward(args))); } /// Invoke and pass recursive to itself template inline static void invoke(shared_result_t storage, Arguments&& args, _CTy&& current, Rest&&... rest) { // Invoke the current continuable... invoke(storage, std::forward(args), std::forward<_CTy>(current)); // And continue with the next distributor::invoke(storage, std::forward(args), std::forward(rest)...); } }; template struct sequenced_invoke; template struct sequenced_invoke> { template inline static void invoke(shared_result_t result, Arguments&& arguments, TupleFunctional&& functional) { // Invoke the distributor which invokes all given continuables. distributor<_PTy...>::invoke( result, std::forward(arguments), std::get(std::forward(functional))...); } }; /// Creates a faked function which invokes all sub continuables template static return_t create(_CTy&&... functionals) { // C++11 workaround for move semantics of non copyable types // TODO Use the stack instead of heap variables. auto shared_functionals = std::make_shared>( std::make_tuple(std::forward<_CTy>(functionals)...) ); return [=](_ATy&&... args) mutable { // TODO Use the stack instead of heap variables. auto shared_args = std::make_shared>( std::forward_as_tuple(std::forward<_ATy>(args)...)); // Fake continuable which wraps all continuables together return make_continuable([=](Callback<_RTy...>&& callback) mutable { sequenced_invoke< fu::sequence_of_t< sizeof...(_CTy) > >::invoke( std::make_shared(sizeof...(_CTy), callback), std::move(*shared_args), std::move(*shared_functionals) ); }); }; } }; /// Helper trait for multiple chains like `Continuable::all` template struct multiple_when_all_chainer_t, fu::identity<_CTy...>> { typedef typename functional_traits<_ATy...>::template result_maker_of_t<_CTy...> result_maker; typedef typename result_maker::arguments_t arguments_t; typedef typename result_maker::partial_results_t partial_results_t; typedef multiple_when_all_chainer_t_make_result, arguments_t, partial_results_t> make_result; // Creates one continuable from multiple ones static auto make_when_all(_CTy&&... args) -> typename make_result::return_t { return make_result::create(std::forward<_CTy>(args)...); } }; */ } #endif // _CONTINUABLE_H_