From bc8e01df4286f5f8ff9103fbebad801f355db689 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Sun, 3 Jul 2022 22:14:13 +0200 Subject: Shorten switchOnType, function_traits and connect* ...thanks to C++20 awesomeness. A notable change is that wrap_in_function() (and respectively function_traits<>::function_type) and fn_return_t alias are gone. The former are no more needed because connectUntil/connectSingleShot no more use std::function. The latter has been relatively underused and with the optimisation of switchOnType hereby, could be completely replaced with std::invoke_result_t. Rewriting connect* functions using constexpr and auto parameters made the implementation 30% more compact and much easier to understand (though still with a couple of - now thoroughly commented - tricky places). Dropping std::function<> from it may also bring some (quite modest, likely) performance benefits. --- lib/events/event.h | 64 ++++++++++++++++++------------------------------------ 1 file changed, 21 insertions(+), 43 deletions(-) (limited to 'lib/events') diff --git a/lib/events/event.h b/lib/events/event.h index ec21c6aa..a966e613 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -341,56 +341,34 @@ inline auto eventCast(const BasePtrT& eptr) : nullptr; } -// A trivial generic catch-all "switch" -template -inline auto switchOnType(const BaseEventT& event, FnT&& fn) - -> decltype(fn(event)) -{ - return fn(event); -} - namespace _impl { - // Using bool instead of auto below because auto apparently upsets MSVC - template - constexpr bool needs_downcast = - std::is_base_of_v>> - && !std::is_same_v>>; -} - -// A trivial type-specific "switch" for a void function -template -inline auto switchOnType(const BaseT& event, FnT&& fn) - -> std::enable_if_t<_impl::needs_downcast - && std::is_void_v>> -{ - using event_type = fn_arg_t; - if (is>(event)) - fn(static_cast(event)); + template + concept Invocable_With_Downcast = + std::derived_from>, BaseT>; } -// A trivial type-specific "switch" for non-void functions with an optional -// default value; non-voidness is guarded by defaultValue type -template -inline auto switchOnType(const BaseT& event, FnT&& fn, - fn_return_t&& defaultValue = {}) - -> std::enable_if_t<_impl::needs_downcast, fn_return_t> +template +inline auto switchOnType(const BaseT& event, TailT&& tail) { - using event_type = fn_arg_t; - if (is>(event)) - return fn(static_cast(event)); - return std::move(defaultValue); + if constexpr (std::is_invocable_v) { + return tail(event); + } else if constexpr (_impl::Invocable_With_Downcast) { + using event_type = fn_arg_t; + if (is>(event)) + return tail(static_cast(event)); + return std::invoke_result_t(); // Default-constructed + } else { // Treat it as a value to return + return std::forward(tail); + } } -// A switch for a chain of 2 or more functions -template -inline std::common_type_t, fn_return_t> -switchOnType(const BaseT& event, FnT1&& fn1, FnT2&& fn2, FnTs&&... fns) +template +inline auto switchOnType(const BaseT& event, FnT1&& fn1, FnTs&&... fns) { using event_type1 = fn_arg_t; if (is>(event)) - return fn1(static_cast(event)); - return switchOnType(event, std::forward(fn2), - std::forward(fns)...); + return fn1(static_cast(event)); + return switchOnType(event, std::forward(fns)...); } template @@ -405,8 +383,8 @@ inline auto visit(const BaseT& event, FnTs&&... fns) // TODO: replace with ranges::for_each once all standard libraries have it template inline auto visitEach(RangeT&& events, FnTs&&... fns) - -> std::enable_if_t(fns)...))>> + requires std::is_void_v< + decltype(switchOnType(**begin(events), std::forward(fns)...))> { for (auto&& evtPtr: events) switchOnType(*evtPtr, std::forward(fns)...); -- cgit v1.2.3 From 64948b6840032b04ef00bfe207baa29dd445d141 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Thu, 7 Jul 2022 18:32:48 +0200 Subject: Avoid std::derived_from and std::bind_front Apple Clang doesn't have those yet. --- lib/events/event.h | 2 +- lib/qt_connection_util.h | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'lib/events') diff --git a/lib/events/event.h b/lib/events/event.h index a966e613..05eb51e9 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -344,7 +344,7 @@ inline auto eventCast(const BasePtrT& eptr) namespace _impl { template concept Invocable_With_Downcast = - std::derived_from>, BaseT>; + std::is_base_of_v>>; } template diff --git a/lib/qt_connection_util.h b/lib/qt_connection_util.h index 7477273f..edcdc572 100644 --- a/lib/qt_connection_util.h +++ b/lib/qt_connection_util.h @@ -46,7 +46,7 @@ namespace _impl { template concept PmfSlot = (fn_arg_count_v > 0 - && std::derived_from>>); + && std::is_base_of_v>, ReceiverT>); } // namespace _impl //! \brief Create a connection that self-disconnects when its slot returns true @@ -78,12 +78,21 @@ inline auto connectSingleShot(auto* sender, auto signal, ContextT* context, Qt::ConnectionType(connType | Qt::SingleShotConnection)); #else - // In case for usual Qt slots passed as pointers-to-members the receiver + // In case of classic Qt pointer-to-member-function slots the receiver // object has to be pre-bound to the slot to make it self-contained if constexpr (_impl::PmfSlot) { + auto&& boundSlot = +# if __cpp_lib_bind_front // Needs Apple Clang 13 (other platforms are fine) + std::bind_front(slot, context); +# else + [context, slot](const auto&... args) + requires requires { (context->*slot)(args...); } + { + (context->*slot)(args...); + }; +# endif return _impl::connect<_impl::SingleShot>(sender, signal, context, - std::bind_front(slot, context), - connType); + std::move(boundSlot), connType); } else { return _impl::connect<_impl::SingleShot>(sender, signal, context, slot, connType); -- cgit v1.2.3