/* * Copyright (C) 2015 Naios * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef _CONTINUABLE_H_ #define _CONTINUABLE_H_ #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 { }; /// Creates an empty callback. template struct create_empty_callback; template struct create_empty_callback&&)>> { static auto create() -> Callback { return [](Args&&...) { }; } }; template struct unary_chainer_t; template struct multiple_when_all_chainer_t; /// Functional traits forward declaration. template struct functional_traits; } // 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; bool _released; template void invoke(_CTy&& callback) { if (!_released) { // Invalidate this _released = true; // Invoke this _callback_insert(std::forward<_CTy>(callback)); } } public: /// Developing Continuable() : _released(true) { } /// Deleted copy construct Continuable(Continuable const&) = delete; /// Move construct Continuable(Continuable&& right) : _released(right._released), _callback_insert(std::move(right._callback_insert)) { right._released = true; } // Construct through a ForwardFunction template Continuable(_FTy&& callback_insert) : _callback_insert(std::forward<_FTy>(callback_insert)), _released(false) { } template Continuable(_FTy&& callback_insert, Continuable<_RATy...>&& right) : _callback_insert(std::forward<_FTy>(callback_insert)), _released(right._released) { right._released = true; } /// 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(detail::create_empty_callback::create()); } } /// Deleted copy assign Continuable& operator= (Continuable const&) = delete; /// Move construct assign Continuable& operator= (Continuable&& right) { _released = right._released; right._released = true; _callback_insert = std::move(right._callback_insert); return *this; } /// Waits for this continuable and invokes the given callback. template auto then(_CTy&& functional) -> typename detail::unary_chainer_t<_CTy, _ATy...>::continuable_t { static_assert(std::is_same, typename detail::unary_chainer_t<_CTy, _ATy...>::arguments_t>::value, "Given function signature isn't correct, for now it must match strictly!"); // Transfer the insert function to the local scope. // Also use it as an r-value reference to try to get move semantics with c++11 lambdas. ForwardFunction&& callback = std::move(_callback_insert); auto&& corrected = detail::functional_traits<_ATy...>:: correct(std::forward<_CTy>(functional)); return typename detail::unary_chainer_t<_CTy, _ATy...>::continuable_t( [corrected, callback](typename detail::unary_chainer_t<_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 *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 { 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. /// Can be used to start a chain with aggregate methods. /// empty_continuable() /// .all(...) /// .some(...) /// .any(...) inline auto make_continuable() -> Continuable<> { return make_continuable([](Callback<>&& callback) { callback(); }); } namespace detail { /// Helper trait for unary chains like `Continuable::then` template struct unary_chainer_t { // Corrected user given functional typedef decltype(detail::functional_traits<_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; }; 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; }; template struct void_wrap_trait; /// Trait needed for functional_traits::remove_void_trait template struct void_wrap_trait> { template static std::function(_ATy...)> wrap(_CTy&& functional) { return [functional](_ATy&&... args) { // Invoke the original callback functional(std::forward<_ATy>(args)...); // Return an empty continuable return make_continuable(); }; } }; /// Position wrapper class to pass ints as type template struct partial_result { static std::size_t const position = Position; typedef Tuple tuple; }; /// Continuable processing detail implementation template struct functional_traits { /// Wrap void returning functionals to returns an empty continuable. template static auto remove_void_trait(_CTy&& functional) -> typename std::enable_if< std::is_void< fu::return_type_of_t< typename std::decay<_CTy>::type > >::value, decltype( detail::void_wrap_trait< fu::argument_type_of_t< typename std::decay<_CTy>::type > >::wrap(std::declval<_CTy>()) ) >::type { return detail::void_wrap_trait< fu::argument_type_of_t< typename std::decay<_CTy>::type > >::wrap(std::forward<_CTy>(functional)); } /// Route continuable returning functionals through. template static auto remove_void_trait(_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); } /// Wrap continuables into the continuable returning functional type. template static auto box_continuable_trait(_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 copyable but moveable continuables. 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&&...) { return std::move(*shared_continuable); }; } /// Route functionals through template inline static auto box_continuable_trait(_CTy&& continuable) -> typename std::enable_if< !detail::is_continuable< typename std::decay<_CTy>::type >::value, typename std::decay<_CTy>::type >::type { static_assert(std::is_rvalue_reference<_CTy&&>::value, "Given continuable must be passed as r-value!"); return std::forward<_CTy>(continuable); } /// Correct user given continuable functionals. /// Converts plan continuables to continuable retuning functions. /// Converts void return to empty continuable. template static auto correct(_CTy&& functional) -> decltype(remove_void_trait(box_continuable_trait(std::declval<_CTy>()))) { return remove_void_trait(box_continuable_trait(std::forward<_CTy>(functional))); } template struct multiple_result_maker; template struct multiple_result_maker, fu::identity> { typedef fu::identity arguments_t; typedef fu::identity partial_results_t; static std::size_t const size = Position; }; template struct multiple_result_maker : multiple_result_maker< Position + 1, typename concat_identities< Args, typename unary_chainer_t::callback_arguments_t >::type, typename concat_identities< Pack, fu::identity< partial_result< Position, typename identity_to_tuple< typename unary_chainer_t::callback_arguments_t >::type > > >::type, Rest... > { }; template using result_maker_of_t = multiple_result_maker<0, fu::identity<>, fu::identity<>, Args...>; }; template struct multiple_when_all_chainer_t_make_result; template struct multiple_when_all_chainer_t_make_result> { typedef Continuable continuable_t; typedef std::function return_t; template static return_t create(_CTy&&... functionals) { return [=](_ATy&&... args) { // Fake continuable return continuable_t(); }; } }; /// Helper trait for multiple chains like `Continuable::all` template struct multiple_when_all_chainer_t, fu::identity<_CTy...>> { typedef typename functional_traits<_ATy...>::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; static std::size_t const size = result_maker::size; typedef typename multiple_when_all_chainer_t_make_result 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_