/* * 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" // Debug includes #include #include #include template void log_type(T t, std::string const& msg = "") { std::cout << msg << ": " << typeid(t).name() << std::endl; } void debug(std::string const& m) { std::cout << m << std::endl; } /// Debug end template class Continuable; namespace detail { // convert_void_to_continuable forward declaration. /// Corrects void return types from functional types which should be /// Continuable> template struct convert_void_to_continuable; // unary_chainer forward declaration. template struct unary_chainer; // creates an empty callback. template struct create_empty_callback; // trait to identify continuable types template struct is_continuable : public std::false_type { }; template struct is_continuable> : public std::true_type { }; // MSVC 12 has issues to detect the parameter pack otherwise. template struct unary_chainer<_NextRTy, fu::identity<_NextATy...>> { typedef convert_void_to_continuable<_NextRTy> base; typedef typename convert_void_to_continuable<_NextRTy>::type result_t; typedef typename result_t::CallbackFunction callback_t; }; template struct multiple_all_chainer { }; template using unary_chainer_t = unary_chainer< fu::return_type_of_t::type>, fu::argument_type_of_t::type>>; template struct create_empty_callback&&)>> { static auto create() -> Callback { return [](Args...) { }; } }; 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: /// 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 std::enable_if< !detail::is_continuable< typename std::decay<_CTy>::type >::value, typename detail::unary_chainer_t<_CTy>::result_t >::type { // 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); return typename detail::unary_chainer_t<_CTy>::result_t( [functional, callback](typename detail::unary_chainer_t<_CTy>::callback_t&& call_next) { callback([functional, call_next](_ATy&&... args) mutable { // Invoke the next callback detail::unary_chainer_t<_CTy>::base::invoke(functional, std::forward<_ATy>(args)...) .invoke(std::move(call_next)); }); }, std::move(*this)); } /// Waits for this continuable and continues with the given one. template auto then(_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 then(detail::functional_traits<_ATy...>::box_continuable_trait(std::forward<_CTy>(continuable))); } template Continuable& _wrap_all(_CTy&&...) { typedef detail::multiple_all_chainer<_CTy...> type; return *this; } /// Placeholder template auto all(_CTy&&... functionals) -> Continuable& { return *this; } /// Placeholder template Continuable& some(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 { 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(); }; } }; /// 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 { // 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 { 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 convert_void_to_continuable { typedef Continuable<> type; template static type invoke(Fn functional, Args&&... args) { // Invoke the void returning functional functional(std::forward(args)...); // Return a fake void continuable return type([](Callback<>&& callback) { callback(); }); } }; template struct convert_void_to_continuable> { typedef Continuable<_CTy...> type; template static type invoke(Fn functional, Args&&... args) { // Invoke the functional as usual. return functional(std::forward(args)...); } }; } #endif // _CONTINUABLE_H_