From fad02bd00ebc42f3233c835f3afddc361fc6ccc9 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 10 Oct 2023 20:07:35 +0200 Subject: [PATCH] Library: extract hook argument adaption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit extract into helper function to improve legibility. This code is rather tricky since on invocation the hook is only provided but not invoked. Rather, to adapt the argument types, it is wrapped into a λ for adaptation, which then must be again bound *by value* into yet another λ, since the Launch configuration builder is comprised of a chain of captured functors, to be invoked later from the body of the thread-wrapper object; this indirect procedure is necessary to ensure all members are initialised *before* the new thread starts --- src/lib/thread.hpp | 57 +++++++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/src/lib/thread.hpp b/src/lib/thread.hpp index d66b1a2d6..6418ef387 100644 --- a/src/lib/thread.hpp +++ b/src/lib/thread.hpp @@ -495,36 +495,51 @@ namespace lib { } private: + /** + * Helper to adapt a user provided hook to be usable as lifecycle hook. + * @tparam HOOK type of the user provided λ or functor + * @tparam FUN type of the function maintained in #PolicyLifecycleHook + * @note the user provided functor can take any type as argument, which + * is reachable by static cast from the thread-wrapper. Especially + * this allows both for low-level and userclass-internal hooks. + */ + template + auto + adaptedHook (FUN Policy::*, HOOK&& hook) + { + static_assert(1 == _Fun::ARITY); + static_assert(1 >= _Fun::ARITY); + // argument type expected by the hooks down in the policy class + using Arg = typename _Fun::Args::List::Head; + // distinguish if user provided functor takes zero or one argument + if constexpr (0 == _Fun::ARITY) + return [hook = forward(hook)](Arg){ hook(); }; + else + { // instance type expected by the user-provided hook + using Target = typename _Fun::Args::List::Head; + return [hook = forward(hook)] + (Arg& threadWrapper) + { // build a two-step cast path from the low-level wrapper to user type + ThreadLifecycle& base = static_cast (threadWrapper); + Target& target = static_cast (base); + hook (target); + }; + } + } + + /** add a config layer to store a user-provided functor into the polic baseclass(es) */ template Launch&& addHook (FUN Policy::*storedHook, HOOK&& hook) { - static_assert(1 == _Fun::ARITY); - static_assert(1 >= _Fun::ARITY); - using Arg = typename _Fun::Args::List::Head; - FUN adapted; - if constexpr (0 == _Fun::ARITY) - { - adapted = [hook = forward(hook)](Arg){ hook(); }; - } - else - { - using Target = typename _Fun::Args::List::Head; - adapted = [hook = forward(hook)] - (Arg& threadWrapper) - { - ThreadLifecycle& base = static_cast (threadWrapper); - Target& target = static_cast (base); - hook (target); - }; - } - return addLayer ([storedHook, hook = move(adapted)] + return addLayer ([storedHook, hook = adaptedHook (storedHook, forward (hook))] (ThreadLifecycle& wrapper) { - wrapper.*storedHook = move(hook); + wrapper.*storedHook = move (hook); }); } + /** generic helper to add another »onion layer« to this config builder */ Launch&& addLayer (Act action) {