From 25dc1907db4f0bb25e62d638f893b045baaa9058 Mon Sep 17 00:00:00 2001 From: Bart Wyatt Date: Thu, 3 May 2018 21:58:27 -0400 Subject: [PATCH] add a new dispatch method which calls the best provider by priority ONLY made it easier to use different dispatch policies made it possible to have void methods --- include/appbase/method.hpp | 187 ++++++++++++++++++++++++++++--------- 1 file changed, 143 insertions(+), 44 deletions(-) diff --git a/include/appbase/method.hpp b/include/appbase/method.hpp index e7c7408e1..b7f2d0373 100644 --- a/include/appbase/method.hpp +++ b/include/appbase/method.hpp @@ -11,31 +11,16 @@ namespace appbase { using erased_method_ptr = std::unique_ptr; - namespace impl { - template - struct dispatch_policy_helper_impl { - using result_type = Ret; - }; - - template - struct method_traits; - - template - struct method_traits { - using result_type = typename dispatch_policy_helper_impl::result_type; - using args_tuple_type = std::tuple; - - }; - } - /** * Basic DispatchPolicy that will try providers sequentially until one succeeds * without throwing an exception. that result becomes the result of the method */ template - struct first_success_policy { - using result_type = typename impl::method_traits::result_type; - std::string err; + struct first_success_policy; + + template + struct first_success_policy { + using result_type = Ret; /** * Iterate through the providers, calling (dereferencing) each @@ -48,7 +33,8 @@ namespace appbase { * @return */ template - result_type operator()(InputIterator first, InputIterator last) { + Ret operator()(InputIterator first, InputIterator last) { + std::string err; while (first != last) { try { return *first; // de-referencing the iterator causes the provider to run @@ -67,6 +53,136 @@ namespace appbase { } }; + template + struct first_success_policy { + using result_type = void; + + /** + * Iterate through the providers, calling (dereferencing) each + * if the provider throws, then store then try the next provider + * if none succeed throw an error with the aggregated error descriptions + * + * @tparam InputIterator + * @param first + * @param last + * @return + */ + template + void operator()(InputIterator first, InputIterator last) { + std::string err; + + while (first != last) { + try { + *first; // de-referencing the iterator causes the provider to run + } catch (...) { + if (!err.empty()) { + err += "\",\""; + } + + err += boost::current_exception_diagnostic_information(); + } + + ++first; + } + + throw std::length_error(std::string("No Result Available, All providers returned exceptions[") + err + "]"); + } + }; + + + /** + * Basic DispatchPolicy that will only call the first provider throwing or returning that providers results + */ + template + struct first_provider_policy; + + template + struct first_provider_policy { + using result_type = Ret; + + /** + * Call the first provider as ordered by registered priority, return its result + * throw its exceptions + * + * @tparam InputIterator + * @param first + * @param + * @return + */ + template + Ret operator()(InputIterator first, InputIterator) { + return *first; + } + }; + + template + struct first_provider_policy { + using result_type = void; + + /** + * Call the first provider as ordered by registered priority, return its result + * throw its exceptions + * + * @tparam InputIterator + * @param first + * @param + * @return + */ + template + void operator()(InputIterator first, InputIterator) { + *first; + } + }; + + namespace impl { + template + class method_caller; + + template + class method_caller { + public: + using signal_type = boost::signals2::signal; + using result_type = Ret; + + method_caller() + {} + + /** + * call operator from boost::signals2 + * + * @throws exception depending on the DispatchPolicy + */ + Ret operator()(Args&&... args) + { + return _signal(std::forward(args)...); + } + + signal_type _signal; + }; + + template + class method_caller { + public: + using signal_type = boost::signals2::signal; + using result_type = void; + + method_caller() + {} + + /** + * call operator from boost::signals2 + * + * @throws exception depending on the DispatchPolicy + */ + void operator()(Args&&... args) + { + _signal(std::forward(args)...); + } + + signal_type _signal; + }; + } + /** * A method is a loosely linked application level function. * Callers can grab a method and call it @@ -78,12 +194,8 @@ namespace appbase { * @tparam DispatchPolicy - the policy for dispatching this method */ template - class method final { + class method final : public impl::method_caller { public: - using traits = impl::method_traits; - using args_tuple_type = typename traits::args_tuple_type; - using result_type = typename traits::result_type; - /** * Type that represents a registered provider for a method allowing * for ownership via RAII and also explicit unregistered actions @@ -139,18 +251,7 @@ namespace appbase { */ template handle register_provider(T provider, int priority = 0) { - return handle(_signal.connect(priority, provider)); - } - - /** - * inhereted call operator from boost::signals2 - * - * @throws exception depending on the DispatchPolicy - */ - template - auto operator()(Args&&... args) -> typename std::enable_if_t, args_tuple_type>::value, result_type> - { - return _signal(std::forward(args)...); + return handle(this->_signal.connect(priority, provider)); } protected: @@ -186,8 +287,6 @@ namespace appbase { return erased_method_ptr(new method(), &deleter); } - boost::signals2::signal _signal; - friend class appbase::application; }; @@ -198,14 +297,14 @@ namespace appbase { * @tparam FunctionSig - the signature of the method * @tparam DispatchPolicy - dispatch policy that dictates how providers for a method are accessed defaults to @ref first_success_policy */ - template< typename Tag, typename FunctionSig, typename DispatchPolicy = first_success_policy> + template< typename Tag, typename FunctionSig, template class DispatchPolicy = first_success_policy> struct method_decl { - using method_type = method; + using method_type = method>; using tag_type = Tag; }; - template - std::true_type is_method_decl_impl(const method_decl*); + template class DispatchPolicy> + std::true_type is_method_decl_impl(const method_decl*); std::false_type is_method_decl_impl(...);