/* * 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 namespace detail { // ContinuableState forward declaration. /// The internal state of the continuable /// which is used to save certain internal types. template struct ContinuableState; // ContinuableImpl forward declaration. template class _ContinuableImpl; // 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; template struct ContinuableState, _Proxy> { }; typedef ContinuableState, void> DefaultContinuableState; // 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 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 class _ContinuableImpl, std::function> { // Make all instances of _ContinuableImpl to a friend. template friend class _ContinuableImpl; 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 _ContinuableImpl& operator()(_CTy callback) { // Invalidate this _released = true; // Invoke this _callback_insert(std::forward<_CTy>(callback)); return *this; } public: /// Deleted copy construct _ContinuableImpl(_ContinuableImpl const&) = delete; /// Move construct _ContinuableImpl(_ContinuableImpl&& right) : _released(right._released), _callback_insert(std::move(right._callback_insert)) { right._released = true; } // Construct through a ForwardFunction template _ContinuableImpl(_FTy&& callback_insert) : _callback_insert(std::forward<_FTy>(callback_insert)), _released(false) { } template _ContinuableImpl(_FTy&& callback_insert, _ContinuableImpl<_RSTy, _RCTy>&& right) : _callback_insert(std::forward<_FTy>(callback_insert)), _released(right._released) { right._released = true; } /// Destructor which calls the dispatch chain if needed. ~_ContinuableImpl() { // Dispatch everything. if (!_released) { // log_type(_callback_insert, "invoke chain"); // Set released to true to prevent multiple calls _released = true; debug("invoking (once)"); // Invoke everything with an empty callback _callback_insert(create_empty_callback::create()); } } /// Deleted copy assign template _ContinuableImpl& operator= (_ContinuableImpl const&) = delete; /// Move construct assign _ContinuableImpl& operator= (_ContinuableImpl&& right) { _released = right._released; right._released = true; _callback_insert = std::move(right._callback_insert); return *this; } template auto then(_CTy&& functional) -> typename unary_chainer_t<_CTy>::result_t { // 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. ForwardFunction&& callback = std::move(_callback_insert); return typename unary_chainer_t<_CTy>::result_t( [functional, callback](typename unary_chainer_t<_CTy>::callback_t&& call_next) { callback([functional, call_next](_ATy&&... args) mutable { typename unary_chainer_t<_CTy>::result_t continuable = unary_chainer_t<_CTy>::base::invoke(functional, std::forward<_ATy>(args)...); // Invoke the next callback continuable(std::move(call_next)); }); }, std::move(*this)); } /* // TODO Accept only correct callbacks template Continuable> all(_CTy&&...) { // TODO Transmute the returned callback here. return Continuable>(std::move(*this)); } */ /* /// Validates the Continuable inline _ContinuableImpl& Validate() { _released = false; return *this; } /// Invalidates the Continuable inline _ContinuableImpl& Invalidate() { _released = true; return *this; } */ }; template<> struct convert_void_to_continuable { typedef _ContinuableImpl> 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<_ContinuableImpl<_State, _CTy>> { typedef _ContinuableImpl<_State, _CTy> type; template static type invoke(Fn functional, Args&&... args) { // Invoke the functional as usual. return functional(std::forward(args)...); } }; } /// A continuable provides useful methods to react on the result of callbacks /// and allows to chain multiple callback calls to a chain. template using Continuable = detail::_ContinuableImpl< detail::DefaultContinuableState, Callback>; 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>>; } // detail /// 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)); } #endif // _CONTINUABLE_H_