diff --git a/GaudiAlg/GaudiAlg/Consumer.h b/GaudiAlg/GaudiAlg/Consumer.h index c6540f9224d0551759e3d89f1930f39edf12eaff..3069c024b99abe0174a9b97688d274ce935f2010 100644 --- a/GaudiAlg/GaudiAlg/Consumer.h +++ b/GaudiAlg/GaudiAlg/Consumer.h @@ -5,14 +5,15 @@ #include "GaudiAlg/FunctionalUtilities.h" #include <utility> -namespace Gaudi { - namespace Functional { +namespace Gaudi::Functional { - template <typename Signature, typename Traits_ = Traits::useDefaults> + namespace details { + + template <typename Signature, typename Traits_, bool isLegacy> class Consumer; template <typename... In, typename Traits_> - class Consumer<void( const In&... ), Traits_> + class Consumer<void( const In&... ), Traits_, true> : public details::DataHandleMixin<void, details::filter_evtcontext<In...>, Traits_> { public: using details::DataHandleMixin<void, details::filter_evtcontext<In...>, Traits_>::DataHandleMixin; @@ -31,7 +32,33 @@ namespace Gaudi { // ... instead, they must implement the following operator virtual void operator()( const In&... ) const = 0; }; - } // namespace Functional -} // namespace Gaudi + + template <typename... In, typename Traits_> + class Consumer<void( const In&... ), Traits_, false> + : public details::DataHandleMixin<void, details::filter_evtcontext<In...>, Traits_> { + public: + using details::DataHandleMixin<void, details::filter_evtcontext<In...>, Traits_>::DataHandleMixin; + + // derived classes are NOT allowed to implement execute ... + StatusCode execute( const EventContext& ctx ) const override final { + try { + details::filter_evtcontext_t<In...>::apply( *this, ctx, this->m_inputs ); + } catch ( GaudiException& e ) { + ( e.code() ? this->warning() : this->error() ) << e.message() << endmsg; + return e.code(); + } + return StatusCode::SUCCESS; + } + + // ... instead, they must implement the following operator + virtual void operator()( const In&... ) const = 0; + }; + + } // namespace details + + template <typename Signature, typename Traits_ = Traits::useDefaults> + using Consumer = details::Consumer<Signature, Traits_, details::isLegacy<Traits_>>; + +} // namespace Gaudi::Functional #endif diff --git a/GaudiAlg/GaudiAlg/FilterPredicate.h b/GaudiAlg/GaudiAlg/FilterPredicate.h index 21110bb23992f0805765444eab244eb2a0a98b6c..14d64757b37bbc063623c3a8edcb745445db15ea 100644 --- a/GaudiAlg/GaudiAlg/FilterPredicate.h +++ b/GaudiAlg/GaudiAlg/FilterPredicate.h @@ -6,14 +6,15 @@ #include <type_traits> #include <utility> -namespace Gaudi { - namespace Functional { +namespace Gaudi ::Functional { - template <typename T, typename Traits_ = Traits::useDefaults> + namespace details { + + template <typename T, typename Traits_, bool isLegacy> class FilterPredicate; template <typename... In, typename Traits_> - class FilterPredicate<bool( const In&... ), Traits_> + class FilterPredicate<bool( const In&... ), Traits_, true> : public details::DataHandleMixin<void, std::tuple<In...>, Traits_> { public: using details::DataHandleMixin<void, std::tuple<In...>, Traits_>::DataHandleMixin; @@ -32,7 +33,34 @@ namespace Gaudi { // ... instead, they must implement the following operator virtual bool operator()( const In&... ) const = 0; }; - } // namespace Functional -} // namespace Gaudi + + template <typename... In, typename Traits_> + class FilterPredicate<bool( const In&... ), Traits_, false> + : public details::DataHandleMixin<void, std::tuple<In...>, Traits_> { + public: + using details::DataHandleMixin<void, std::tuple<In...>, Traits_>::DataHandleMixin; + + // derived classes are NOT allowed to implement execute ... + StatusCode execute( const EventContext& ctx ) const override final { + try { + this->execState( ctx ).setFilterPassed( + details::filter_evtcontext_t<In...>::apply( *this, ctx, this->m_inputs ) ); + return StatusCode::SUCCESS; + } catch ( GaudiException& e ) { + ( e.code() ? this->warning() : this->error() ) << e.message() << endmsg; + return e.code(); + } + } + + // ... instead, they must implement the following operator + virtual bool operator()( const In&... ) const = 0; + }; + + } // namespace details + + template <typename Signature, typename Traits_ = Traits::useDefaults> + using FilterPredicate = details::FilterPredicate<Signature, Traits_, details::isLegacy<Traits_>>; + +} // namespace Gaudi::Functional #endif diff --git a/GaudiAlg/GaudiAlg/FunctionalDetails.h b/GaudiAlg/GaudiAlg/FunctionalDetails.h index b2666cf1c975485dbfba984ebd3657e0f10812ae..b7ea480c5c9f4203ae4494a2866d26235e0dc0c8 100644 --- a/GaudiAlg/GaudiAlg/FunctionalDetails.h +++ b/GaudiAlg/GaudiAlg/FunctionalDetails.h @@ -14,542 +14,560 @@ #include "GaudiKernel/ThreadLocalContext.h" #include "GaudiKernel/detected.h" -// Boost -#include "boost/optional.hpp" - // Range V3 #include <range/v3/view/const.hpp> #include <range/v3/view/zip.hpp> -namespace Gaudi { - namespace Functional { - namespace details { - - // CRJ : Stuff for zipping - namespace zip { - - /// Print the parameter - template <typename OS, typename Arg> - void printSizes( OS& out, Arg&& arg ) { - out << "SizeOf'" << System::typeinfoName( typeid( Arg ) ) << "'=" << std::forward<Arg>( arg ).size(); - } - - /// Print the parameters - template <typename OS, typename Arg, typename... Args> - void printSizes( OS& out, Arg&& arg, Args&&... args ) { - printSizes( out, arg ); - out << ", "; - printSizes( out, args... ); - } - - /// Resolve case there is only one container in the range - template <typename A> - inline bool check_sizes( const A& ) noexcept { - return true; - } - - /// Compare sizes of two containers - template <typename A, typename B> - inline bool check_sizes( const A& a, const B& b ) noexcept { - return a.size() == b.size(); - } - - /// Compare sizes of 3 or more containers - template <typename A, typename B, typename... C> - inline bool check_sizes( const A& a, const B& b, const C&... c ) noexcept { - return ( check_sizes( a, b ) && check_sizes( b, c... ) ); - } - - /// Verify the data container sizes have the same sizes - template <typename... Args> - inline decltype( auto ) verifySizes( Args&... args ) { - if ( UNLIKELY( !check_sizes( args... ) ) ) { - std::ostringstream mess; - mess << "Zipped containers have different sizes : "; - printSizes( mess, args... ); - throw GaudiException( mess.str(), "Gaudi::Functional::details::zip::verifySizes", StatusCode::FAILURE ); - } - } - - /// Zips multiple containers together to form a single range - template <typename... Args> - inline decltype( auto ) range( Args&&... args ) { +namespace Gaudi::Functional::details { + + // CRJ : Stuff for zipping + namespace zip { + + /// Print the parameter + template <typename OS, typename Arg> + void printSizes( OS& out, Arg&& arg ) { + out << "SizeOf'" << System::typeinfoName( typeid( Arg ) ) << "'=" << std::forward<Arg>( arg ).size(); + } + + /// Print the parameters + template <typename OS, typename Arg, typename... Args> + void printSizes( OS& out, Arg&& arg, Args&&... args ) { + printSizes( out, arg ); + out << ", "; + printSizes( out, args... ); + } + + /// Resolve case there is only one container in the range + template <typename A> + inline bool check_sizes( const A& ) noexcept { + return true; + } + + /// Compare sizes of two containers + template <typename A, typename B> + inline bool check_sizes( const A& a, const B& b ) noexcept { + return a.size() == b.size(); + } + + /// Compare sizes of 3 or more containers + template <typename A, typename B, typename... C> + inline bool check_sizes( const A& a, const B& b, const C&... c ) noexcept { + return ( check_sizes( a, b ) && check_sizes( b, c... ) ); + } + + /// Verify the data container sizes have the same sizes + template <typename... Args> + inline decltype( auto ) verifySizes( Args&... args ) { + if ( UNLIKELY( !check_sizes( args... ) ) ) { + std::ostringstream mess; + mess << "Zipped containers have different sizes : "; + printSizes( mess, args... ); + throw GaudiException( mess.str(), "Gaudi::Functional::details::zip::verifySizes", StatusCode::FAILURE ); + } + } + + /// Zips multiple containers together to form a single range + template <typename... Args> + inline decltype( auto ) range( Args&&... args ) { #ifndef NDEBUG - verifySizes( args... ); + verifySizes( args... ); #endif - return ranges::view::zip( std::forward<Args>( args )... ); - } + return ranges::view::zip( std::forward<Args>( args )... ); + } - /// Zips multiple containers together to form a single const range - template <typename... Args> - inline decltype( auto ) const_range( Args&&... args ) { + /// Zips multiple containers together to form a single const range + template <typename... Args> + inline decltype( auto ) const_range( Args&&... args ) { #ifndef NDEBUG - verifySizes( args... ); + verifySizes( args... ); #endif - return ranges::view::const_( ranges::view::zip( std::forward<Args>( args )... ) ); - } - } // namespace zip - - using std::as_const; - using std::disjunction; - ///////////////////////////////////////// - namespace details2 { - // note: boost::optional in boost 1.66 does not have 'has_value()'... - // that requires boost 1.68 or later... so for now, use operator bool() instead ;-( - template <typename T> - using is_optional_ = decltype( bool( std::declval<T>() ), std::declval<T>().value() ); - } // namespace details2 - template <typename Arg> - using is_optional = typename Gaudi::cpp17::is_detected<details2::is_optional_, Arg>; - - template <typename Arg> - using require_is_optional = std::enable_if_t<is_optional<Arg>::value>; - - template <typename Arg> - using require_is_not_optional = std::enable_if_t<!is_optional<Arg>::value>; - - namespace details2 { - template <typename T, typename = void> - struct remove_optional { - using type = T; - }; - - template <typename T> - struct remove_optional<T, std::enable_if_t<is_optional<T>::value>> { - using type = typename T::value_type; - }; - } // namespace details2 - - template <typename T> - using remove_optional_t = typename details2::remove_optional<T>::type; - - constexpr struct invoke_optionally_t { - template <typename F, typename Arg, typename = require_is_not_optional<Arg>> - decltype( auto ) operator()( F&& f, Arg&& arg ) const { - return std::invoke( std::forward<F>( f ), std::forward<Arg>( arg ) ); - } - template <typename F, typename Arg, typename = require_is_optional<Arg>> - void operator()( F&& f, Arg&& arg ) const { - if ( arg ) std::invoke( std::forward<F>( f ), *std::forward<Arg>( arg ) ); - } - } invoke_optionally{}; - ///////////////////////////////////////// - - template <typename Out1, typename Out2, - typename = std::enable_if_t<std::is_constructible<Out1, Out2>::value && - std::is_base_of<DataObject, Out1>::value>> - Out1* put( DataObjectHandle<Out1>& out_handle, Out2&& out ) { - return out_handle.put( std::make_unique<Out1>( std::forward<Out2>( out ) ) ); + return ranges::view::const_( ranges::view::zip( std::forward<Args>( args )... ) ); + } + } // namespace zip + + ///////////////////////////////////////// + namespace details2 { + // note: boost::optional in boost 1.66 does not have 'has_value()'... + // that requires boost 1.68 or later... so for now, use operator bool() instead ;-( + template <typename T> + using is_optional_ = decltype( bool( std::declval<T>() ), std::declval<T>().value() ); + } // namespace details2 + template <typename Arg> + using is_optional = typename Gaudi::cpp17::is_detected<details2::is_optional_, Arg>; + + template <typename Arg> + using require_is_optional = std::enable_if_t<is_optional<Arg>::value>; + + template <typename Arg> + using require_is_not_optional = std::enable_if_t<!is_optional<Arg>::value>; + + namespace details2 { + template <typename T, typename = void> + struct remove_optional { + using type = T; + }; + + template <typename T> + struct remove_optional<T, std::enable_if_t<is_optional<T>::value>> { + using type = typename T::value_type; + }; + } // namespace details2 + + template <typename T> + using remove_optional_t = typename details2::remove_optional<T>::type; + + constexpr struct invoke_optionally_t { + template <typename F, typename Arg, typename = require_is_not_optional<Arg>> + decltype( auto ) operator()( F&& f, Arg&& arg ) const { + return std::invoke( std::forward<F>( f ), std::forward<Arg>( arg ) ); + } + template <typename F, typename Arg, typename = require_is_optional<Arg>> + void operator()( F&& f, Arg&& arg ) const { + if ( arg ) std::invoke( std::forward<F>( f ), *std::forward<Arg>( arg ) ); + } + } invoke_optionally{}; + ///////////////////////////////////////// + + template < + typename Out1, typename Out2, + typename = std::enable_if_t<std::is_constructible<Out1, Out2>::value && std::is_base_of<DataObject, Out1>::value>> + Out1* put( const DataObjectHandle<Out1>& out_handle, Out2&& out ) { + return out_handle.put( std::make_unique<Out1>( std::forward<Out2>( out ) ) ); + } + + template <typename Out1, typename Out2, typename = std::enable_if_t<std::is_constructible<Out1, Out2>::value>> + void put( const DataObjectHandle<AnyDataWrapper<Out1>>& out_handle, Out2&& out ) { + out_handle.put( std::forward<Out2>( out ) ); + } + + // optional put + template <typename OutHandle, typename OptOut, typename = require_is_optional<OptOut>> + void put( const OutHandle& out_handle, OptOut&& out ) { + if ( out ) put( out_handle, *std::forward<OptOut>( out ) ); + } + ///////////////////////////////////////// + // adapt to differences between eg. std::vector (which has push_back) and KeyedContainer (which has insert) + // adapt to getting a T, and a container wanting T* by doing new T{ std::move(out) } + // adapt to getting a optional<T> + + constexpr struct insert_t { + // for Container<T*>, return T + template <typename Container> + using c_remove_ptr_t = std::remove_pointer_t<typename Container::value_type>; + + template <typename Container, typename Value> + auto operator()( Container& c, Value&& v ) const -> decltype( c.push_back( v ) ) { + return c.push_back( std::forward<Value>( v ) ); + } + + template <typename Container, typename Value> + auto operator()( Container& c, Value&& v ) const -> decltype( c.insert( v ) ) { + return c.insert( std::forward<Value>( v ) ); + } + + // Container<T*> with T&& as argument + template <typename Container, typename = std::enable_if_t<std::is_pointer<typename Container::value_type>::value>> + auto operator()( Container& c, c_remove_ptr_t<Container>&& v ) const { + return operator()( c, new c_remove_ptr_t<Container>{std::move( v )} ); + } + + } insert{}; + + ///////////////////////////////////////// + + constexpr struct deref_t { + template <typename In, typename = std::enable_if_t<!std::is_pointer<In>::value>> + const In& operator()( const In& in ) const { + return in; + } + + template <typename In> + const In& operator()( const In* in ) const { + assert( in != nullptr ); + return *in; + } + } deref{}; + + ///////////////////////////////////////// + // if Container is a pointer, then we're optional items + namespace details2 { + template <typename Container, typename Value> + void push_back( Container& c, const Value& v, std::true_type ) { + c.push_back( v ); + } + template <typename Container, typename Value> + void push_back( Container& c, const Value& v, std::false_type ) { + c.push_back( &v ); + } + + template <typename In> + struct get_from_handle { + template <template <typename> class Handle, typename I, + typename = std::enable_if_t<std::is_convertible<I, In>::value>> + auto operator()( const Handle<I>& h ) -> const In& { + return *h.get(); } - - template <typename Out1, typename Out2, typename = std::enable_if_t<std::is_constructible<Out1, Out2>::value>> - void put( DataObjectHandle<AnyDataWrapper<Out1>>& out_handle, Out2&& out ) { - out_handle.put( std::forward<Out2>( out ) ); - } - - // optional put - template <typename OutHandle, typename OptOut, typename = require_is_optional<OptOut>> - void put( OutHandle& out_handle, OptOut&& out ) { - if ( out ) put( out_handle, *std::forward<OptOut>( out ) ); + template <template <typename> class Handle, typename I, + typename = std::enable_if_t<std::is_convertible<I*, In>::value>> + auto operator()( const Handle<I>& h ) -> const In { + return h.getIfExists(); + } // In is-a pointer + }; + + template <typename T> + T* deref_if( T* const t, std::false_type ) { + return t; + } + template <typename T> + T& deref_if( T* const t, std::true_type ) { + return *t; + } + } // namespace details2 + + template <typename Container> + class vector_of_const_ { + static constexpr bool is_pointer = std::is_pointer<Container>::value; + using val_t = std::add_const_t<std::remove_pointer_t<Container>>; + using ptr_t = std::add_pointer_t<val_t>; + using ref_t = std::add_lvalue_reference_t<val_t>; + using ContainerVector = std::vector<ptr_t>; + ContainerVector m_containers; + + public: + using value_type = std::conditional_t<is_pointer, ptr_t, val_t>; + using size_type = typename ContainerVector::size_type; + class iterator { + using it_t = typename ContainerVector::const_iterator; + it_t m_i; + friend class vector_of_const_; + iterator( it_t iter ) : m_i( iter ) {} + using ret_t = std::conditional_t<is_pointer, ptr_t, ref_t>; + + public: + using iterator_category = typename it_t::iterator_category; + using value_type = typename it_t::iterator_category; + using reference = typename it_t::reference; + using pointer = typename it_t::pointer; + using difference_type = typename it_t::difference_type; + + friend bool operator!=( const iterator& lhs, const iterator& rhs ) { return lhs.m_i != rhs.m_i; } + friend bool operator==( const iterator& lhs, const iterator& rhs ) { return lhs.m_i == rhs.m_i; } + friend auto operator-( const iterator& lhs, const iterator& rhs ) { return lhs.m_i - rhs.m_i; } + ret_t operator*() const { return details2::deref_if( *m_i, std::integral_constant<bool, !is_pointer>{} ); } + iterator& operator++() { + ++m_i; + return *this; } - ///////////////////////////////////////// - // adapt to differences between eg. std::vector (which has push_back) and KeyedContainer (which has insert) - // adapt to getting a T, and a container wanting T* by doing new T{ std::move(out) } - // adapt to getting a optional<T> - - constexpr struct insert_t { - // for Container<T*>, return T - template <typename Container> - using c_remove_ptr_t = std::remove_pointer_t<typename Container::value_type>; - - template <typename Container, typename Value> - auto operator()( Container& c, Value&& v ) const -> decltype( c.push_back( v ) ) { - return c.push_back( std::forward<Value>( v ) ); - } - - template <typename Container, typename Value> - auto operator()( Container& c, Value&& v ) const -> decltype( c.insert( v ) ) { - return c.insert( std::forward<Value>( v ) ); - } - - // Container<T*> with T&& as argument - template <typename Container, - typename = std::enable_if_t<std::is_pointer<typename Container::value_type>::value>> - auto operator()( Container& c, c_remove_ptr_t<Container>&& v ) const { - return operator()( c, new c_remove_ptr_t<Container>{std::move( v )} ); - } - - } insert{}; - - ///////////////////////////////////////// - - constexpr struct deref_t { - template <typename In, typename = std::enable_if_t<!std::is_pointer<In>::value>> - const In& operator()( const In& in ) const { - return in; - } - - template <typename In> - const In& operator()( const In* in ) const { - assert( in != nullptr ); - return *in; - } - } deref{}; - - ///////////////////////////////////////// - // if Container is a pointer, then we're optional items - namespace details2 { - template <typename Container, typename Value> - void push_back( Container& c, const Value& v, std::true_type ) { - c.push_back( v ); - } - template <typename Container, typename Value> - void push_back( Container& c, const Value& v, std::false_type ) { - c.push_back( &v ); - } - - template <typename In> - struct get_from_handle { - template <template <typename> class Handle, typename I, - typename = std::enable_if_t<std::is_convertible<I, In>::value>> - auto operator()( const Handle<I>& h ) -> const In& { - return *h.get(); - } - template <template <typename> class Handle, typename I, - typename = std::enable_if_t<std::is_convertible<I*, In>::value>> - auto operator()( const Handle<I>& h ) -> const In { - return h.getIfExists(); - } // In is-a pointer - }; - - template <typename T> - T* deref_if( T* const t, std::false_type ) { - return t; - } - template <typename T> - T& deref_if( T* const t, std::true_type ) { - return *t; - } - } // namespace details2 - - template <typename Container> - class vector_of_const_ { - static constexpr bool is_pointer = std::is_pointer<Container>::value; - using val_t = std::add_const_t<std::remove_pointer_t<Container>>; - using ptr_t = std::add_pointer_t<val_t>; - using ref_t = std::add_lvalue_reference_t<val_t>; - using ContainerVector = std::vector<ptr_t>; - ContainerVector m_containers; - - public: - using value_type = std::conditional_t<is_pointer, ptr_t, val_t>; - using size_type = typename ContainerVector::size_type; - class iterator { - using it_t = typename ContainerVector::const_iterator; - it_t m_i; - friend class vector_of_const_; - iterator( it_t iter ) : m_i( iter ) {} - using ret_t = std::conditional_t<is_pointer, ptr_t, ref_t>; - - public: - using iterator_category = typename it_t::iterator_category; - using value_type = typename it_t::iterator_category; - using reference = typename it_t::reference; - using pointer = typename it_t::pointer; - using difference_type = typename it_t::difference_type; - - friend bool operator!=( const iterator& lhs, const iterator& rhs ) { return lhs.m_i != rhs.m_i; } - friend bool operator==( const iterator& lhs, const iterator& rhs ) { return lhs.m_i == rhs.m_i; } - friend auto operator-( const iterator& lhs, const iterator& rhs ) { return lhs.m_i - rhs.m_i; } - ret_t operator*() const { return details2::deref_if( *m_i, std::integral_constant<bool, !is_pointer>{} ); } - iterator& operator++() { - ++m_i; - return *this; - } - iterator& operator--() { - --m_i; - return *this; - } - bool is_null() const { return !*m_i; } - explicit operator bool() const { return !is_null(); } - }; - vector_of_const_() = default; - void reserve( size_type size ) { m_containers.reserve( size ); } - template <typename T> // , typename = std::is_convertible<T,std::conditional_t<is_pointer,ptr_t,val_t>> - void push_back( T&& container ) { - details2::push_back( m_containers, std::forward<T>( container ), std::integral_constant<bool, is_pointer>{} ); - } // note: does not copy its argument, so we're not really a container... - iterator begin() const { return m_containers.begin(); } - iterator end() const { return m_containers.end(); } - size_type size() const { return m_containers.size(); } - - template <typename X = Container> - std::enable_if_t<!std::is_pointer<X>::value, ref_t> operator[]( size_type i ) const { - return *m_containers[i]; - } - - template <typename X = Container> - std::enable_if_t<std::is_pointer<X>::value, ptr_t> operator[]( size_type i ) const { - return m_containers[i]; - } - - template <typename X = Container> - std::enable_if_t<!std::is_pointer<X>::value, ref_t> at( size_type i ) const { - return *m_containers[i]; - } - - template <typename X = Container> - std::enable_if_t<std::is_pointer<X>::value, ptr_t> at( size_type i ) const { - return m_containers[i]; - } - - bool is_null( size_type i ) const { return !m_containers[i]; } - }; - - ///////////////////////////////////////// - namespace detail2 { // utilities for detected_or_t{,_} usage - template <typename Tr> - using BaseClass_t = typename Tr::BaseClass; - template <typename Tr, typename T> - using OutputHandle_t = typename Tr::template OutputHandle<T>; - template <typename Tr, typename T> - using InputHandle_t = typename Tr::template InputHandle<T>; - } // namespace detail2 - - // check whether Traits::BaseClass is a valid type, - // if so, define BaseClass_t<Traits> as being Traits::BaseClass - // else define as being GaudiAlgorithm - template <typename Tr> - using BaseClass_t = Gaudi::cpp17::detected_or_t<GaudiAlgorithm, detail2::BaseClass_t, Tr>; - - // check whether Traits::{Input,Output}Handle<T> is a valid type, - // if so, define {Input,Output}Handle_t<Traits,T> as being Traits::{Input,Output}Handle<T> - // else define as being DataObject{Read,,Write}Handle<T> - template <typename Tr, typename T> - using OutputHandle_t = Gaudi::cpp17::detected_or_t<DataObjectWriteHandle<T>, detail2::OutputHandle_t, Tr, T>; - template <typename Tr, typename T> - using InputHandle_t = Gaudi::cpp17::detected_or_t<DataObjectReadHandle<T>, detail2::InputHandle_t, Tr, T>; - - ///////// - - template <typename Handles> - Handles make_vector_of_handles( IDataHandleHolder* owner, const std::vector<std::string>& init ) { - Handles handles; - handles.reserve( init.size() ); - std::transform( init.begin(), init.end(), - std::back_inserter( handles ), [&]( const std::string& loc ) -> typename Handles::value_type { - return {loc, owner}; - } ); - return handles; + iterator& operator--() { + --m_i; + return *this; } - - /////////////////////// - // given a pack, return a corresponding tuple - template <typename... In> - struct filter_evtcontext_t { - using type = std::tuple<In...>; - - static_assert( !details::disjunction<std::is_same<EventContext, In>...>::value, - "EventContext can only appear as first argument" ); - - template <typename Algorithm, typename Handles> - static auto apply( const Algorithm& algo, Handles& handles ) { - return std::apply( [&]( const auto&... handle ) { return algo( details::deref( handle.get() )... ); }, - handles ); - } - }; - - // except when it starts with EventContext, then drop it - template <typename... In> - struct filter_evtcontext_t<EventContext, In...> { - using type = std::tuple<In...>; - - static_assert( !details::disjunction<std::is_same<EventContext, In>...>::value, - "EventContext can only appear as first argument" ); - - template <typename Algorithm, typename Handles> - static auto apply( const Algorithm& algo, Handles& handles ) { - return std::apply( - [&]( const auto&... handle ) { - return algo( Gaudi::Hive::currentContext(), details::deref( handle.get() )... ); - }, - handles ); - } - }; - - template <typename... In> - using filter_evtcontext = typename filter_evtcontext_t<In...>::type; - - template <typename OutputSpec, typename InputSpec, typename Traits_> - class DataHandleMixin; - - template <typename... Out, typename... In, typename Traits_> - class DataHandleMixin<std::tuple<Out...>, std::tuple<In...>, Traits_> : public BaseClass_t<Traits_> { - static_assert( std::is_base_of<Algorithm, BaseClass_t<Traits_>>::value, - "BaseClass must inherit from Algorithm" ); - - template <typename IArgs, typename OArgs, std::size_t... I, std::size_t... J> - DataHandleMixin( const std::string& name, ISvcLocator* pSvcLocator, const IArgs& inputs, - std::index_sequence<I...>, const OArgs& outputs, std::index_sequence<J...> ) - : BaseClass_t<Traits_>( name, pSvcLocator ) - , m_inputs( std::tuple_cat( std::forward_as_tuple( this ), std::get<I>( inputs ) )... ) - , m_outputs( std::tuple_cat( std::forward_as_tuple( this ), std::get<J>( outputs ) )... ) { - // make sure this algorithm is seen as reentrant by Gaudi - this->setProperty( "Cardinality", 0 ); - } - - public: - using KeyValue = std::pair<std::string, std::string>; - constexpr static std::size_t N_in = sizeof...( In ); - constexpr static std::size_t N_out = sizeof...( Out ); - - // generic constructor: N -> M - DataHandleMixin( const std::string& name, ISvcLocator* pSvcLocator, const std::array<KeyValue, N_in>& inputs, - const std::array<KeyValue, N_out>& outputs ) - : DataHandleMixin( name, pSvcLocator, inputs, std::index_sequence_for<In...>{}, outputs, - std::index_sequence_for<Out...>{} ) {} - - // special cases: forward to the generic case... - // 1 -> 1 - DataHandleMixin( const std::string& name, ISvcLocator* locator, const KeyValue& input, const KeyValue& output ) - : DataHandleMixin( name, locator, std::array<KeyValue, 1>{input}, std::array<KeyValue, 1>{output} ) {} - // 1 -> N - DataHandleMixin( const std::string& name, ISvcLocator* locator, const KeyValue& input, - const std::array<KeyValue, N_out>& outputs ) - : DataHandleMixin( name, locator, std::array<KeyValue, 1>{input}, outputs ) {} - // N -> 1 - DataHandleMixin( const std::string& name, ISvcLocator* locator, const std::array<KeyValue, N_in>& inputs, - const KeyValue& output ) - : DataHandleMixin( name, locator, inputs, std::array<KeyValue, 1>{output} ) {} - - template <std::size_t N = 0> - const std::string& inputLocation() const { - return std::get<N>( m_inputs ).objKey(); - } - constexpr unsigned int inputLocationSize() const { return N_in; } - - template <std::size_t N = 0> - const std::string& outputLocation() const { - return std::get<N>( m_outputs ).objKey(); - } - constexpr unsigned int outputLocationSize() const { return N_out; } - - protected: - bool isReEntrant() const override { return true; } - - std::tuple<details::InputHandle_t<Traits_, In>...> m_inputs; - std::tuple<details::OutputHandle_t<Traits_, Out>...> m_outputs; - }; - - template <typename Traits_> - class DataHandleMixin<void, std::tuple<>, Traits_> : public BaseClass_t<Traits_> { - static_assert( std::is_base_of<Algorithm, BaseClass_t<Traits_>>::value, - "BaseClass must inherit from Algorithm" ); - - public: - DataHandleMixin( const std::string& name, ISvcLocator* pSvcLocator ) - : BaseClass_t<Traits_>( name, pSvcLocator ) { - // make sure this algorithm is seen as reentrant by Gaudi - this->setProperty( "Cardinality", 0 ); - } - - protected: - bool isReEntrant() const override { return true; } - - std::tuple<> m_inputs; - }; - - template <typename... In, typename Traits_> - class DataHandleMixin<void, std::tuple<In...>, Traits_> : public BaseClass_t<Traits_> { - static_assert( std::is_base_of<Algorithm, BaseClass_t<Traits_>>::value, - "BaseClass must inherit from Algorithm" ); - - template <typename IArgs, std::size_t... I> - DataHandleMixin( const std::string& name, ISvcLocator* pSvcLocator, const IArgs& inputs, - std::index_sequence<I...> ) - : BaseClass_t<Traits_>( name, pSvcLocator ) - , m_inputs( std::tuple_cat( std::forward_as_tuple( this ), std::get<I>( inputs ) )... ) { - // make sure this algorithm is seen as reentrant by Gaudi - this->setProperty( "Cardinality", 0 ); - } - - public: - using KeyValue = std::pair<std::string, std::string>; - constexpr static std::size_t N_in = sizeof...( In ); - - // generic constructor: N -> 0 - DataHandleMixin( const std::string& name, ISvcLocator* pSvcLocator, const std::array<KeyValue, N_in>& inputs ) - : DataHandleMixin( name, pSvcLocator, inputs, std::index_sequence_for<In...>{} ) {} - - // special cases: forward to the generic case... - // 1 -> 0 - DataHandleMixin( const std::string& name, ISvcLocator* locator, const KeyValue& input ) - : DataHandleMixin( name, locator, std::array<KeyValue, 1>{input} ) {} - - template <std::size_t N = 0> - const std::string& inputLocation() const { - return std::get<N>( m_inputs ).objKey(); - } - constexpr unsigned int inputLocationSize() const { return N_in; } - - protected: - bool isReEntrant() const override { return true; } - - std::tuple<details::InputHandle_t<Traits_, In>...> m_inputs; - }; - - template <typename... Out, typename Traits_> - class DataHandleMixin<std::tuple<Out...>, void, Traits_> : public BaseClass_t<Traits_> { - static_assert( std::is_base_of<Algorithm, BaseClass_t<Traits_>>::value, - "BaseClass must inherit from Algorithm" ); - - template <typename OArgs, std::size_t... J> - DataHandleMixin( const std::string& name, ISvcLocator* pSvcLocator, const OArgs& outputs, - std::index_sequence<J...> ) - : BaseClass_t<Traits_>( name, pSvcLocator ) - , m_outputs( std::tuple_cat( std::forward_as_tuple( this ), std::get<J>( outputs ) )... ) { - // make sure this algorithm is seen as reentrant by Gaudi - this->setProperty( "Cardinality", 0 ); - } - - public: - using KeyValue = std::pair<std::string, std::string>; - constexpr static std::size_t N_out = sizeof...( Out ); - - // generic constructor: 0 -> N - DataHandleMixin( const std::string& name, ISvcLocator* pSvcLocator, const std::array<KeyValue, N_out>& outputs ) - : DataHandleMixin( name, pSvcLocator, outputs, std::index_sequence_for<Out...>{} ) {} - - // 0 -> 1 - DataHandleMixin( const std::string& name, ISvcLocator* locator, const KeyValue& output ) - : DataHandleMixin( name, locator, std::array<KeyValue, 1>{output} ) {} - - template <std::size_t N = 0> - const std::string& outputLocation() const { - return std::get<N>( m_outputs ).objKey(); - } - constexpr unsigned int outputLocationSize() const { return N_out; } - - protected: - bool isReEntrant() const override { return true; } - - std::tuple<details::OutputHandle_t<Traits_, Out>...> m_outputs; - }; - - ///////////////// - template <typename Fun, typename Container, typename... Args> - constexpr void applyPostProcessing( const Fun&, Container&, Args... ) { - static_assert( sizeof...( Args ) == 0, "Args should not be used!" ); - } - - template <typename Fun, typename Container> - auto applyPostProcessing( const Fun& fun, Container& c ) -> decltype( fun.postprocess( c ), void() ) { - fun.postprocess( c ); - } - - ///////////////// - } // namespace details - } // namespace Functional -} // namespace Gaudi + bool is_null() const { return !*m_i; } + explicit operator bool() const { return !is_null(); } + }; + vector_of_const_() = default; + void reserve( size_type size ) { m_containers.reserve( size ); } + template <typename T> // , typename = std::is_convertible<T,std::conditional_t<is_pointer,ptr_t,val_t>> + void push_back( T&& container ) { + details2::push_back( m_containers, std::forward<T>( container ), std::integral_constant<bool, is_pointer>{} ); + } // note: does not copy its argument, so we're not really a container... + iterator begin() const { return m_containers.begin(); } + iterator end() const { return m_containers.end(); } + size_type size() const { return m_containers.size(); } + + template <typename X = Container> + std::enable_if_t<!std::is_pointer<X>::value, ref_t> operator[]( size_type i ) const { + return *m_containers[i]; + } + + template <typename X = Container> + std::enable_if_t<std::is_pointer<X>::value, ptr_t> operator[]( size_type i ) const { + return m_containers[i]; + } + + template <typename X = Container> + std::enable_if_t<!std::is_pointer<X>::value, ref_t> at( size_type i ) const { + return *m_containers[i]; + } + + template <typename X = Container> + std::enable_if_t<std::is_pointer<X>::value, ptr_t> at( size_type i ) const { + return m_containers[i]; + } + + bool is_null( size_type i ) const { return !m_containers[i]; } + }; + + ///////////////////////////////////////// + namespace detail2 { // utilities for detected_or_t{,_} usage + template <typename Tr> + using BaseClass_t = typename Tr::BaseClass; + template <typename Tr, typename T> + using OutputHandle_t = typename Tr::template OutputHandle<T>; + template <typename Tr, typename T> + using InputHandle_t = typename Tr::template InputHandle<T>; + } // namespace detail2 + + // check whether Traits::BaseClass is a valid type, + // if so, define BaseClass_t<Traits> as being Traits::BaseClass + // else define as being GaudiAlgorithm + template <typename Tr> + using BaseClass_t = Gaudi::cpp17::detected_or_t<GaudiAlgorithm, detail2::BaseClass_t, Tr>; + + // check whether Traits::{Input,Output}Handle<T> is a valid type, + // if so, define {Input,Output}Handle_t<Traits,T> as being Traits::{Input,Output}Handle<T> + // else define as being DataObject{Read,,Write}Handle<T> + template <typename Tr, typename T> + using OutputHandle_t = Gaudi::cpp17::detected_or_t<DataObjectWriteHandle<T>, detail2::OutputHandle_t, Tr, T>; + template <typename Tr, typename T> + using InputHandle_t = Gaudi::cpp17::detected_or_t<DataObjectReadHandle<T>, detail2::InputHandle_t, Tr, T>; + + template <typename Traits> + inline constexpr bool isLegacy = + std::is_base_of_v<Gaudi::details::LegacyAlgorithmAdapter, details::BaseClass_t<Traits>>; + + ///////// + + template <typename Handles> + Handles make_vector_of_handles( IDataHandleHolder* owner, const std::vector<std::string>& init ) { + Handles handles; + handles.reserve( init.size() ); + std::transform( init.begin(), init.end(), + std::back_inserter( handles ), [&]( const std::string& loc ) -> typename Handles::value_type { + return {loc, owner}; + } ); + return handles; + } + + /////////////////////// + // given a pack, return a corresponding tuple + template <typename... In> + struct filter_evtcontext_t { + using type = std::tuple<In...>; + + static_assert( !std::disjunction<std::is_same<EventContext, In>...>::value, + "EventContext can only appear as first argument" ); + + template <typename Algorithm, typename Handles> + static auto apply( const Algorithm& algo, Handles& handles ) { + return std::apply( [&]( const auto&... handle ) { return algo( details::deref( handle.get() )... ); }, handles ); + } + template <typename Algorithm, typename Handles> + static auto apply( const Algorithm& algo, const EventContext&, Handles& handles ) { + return std::apply( [&]( const auto&... handle ) { return algo( details::deref( handle.get() )... ); }, handles ); + } + }; + + // except when it starts with EventContext, then drop it + template <typename... In> + struct filter_evtcontext_t<EventContext, In...> { + using type = std::tuple<In...>; + + static_assert( !std::disjunction<std::is_same<EventContext, In>...>::value, + "EventContext can only appear as first argument" ); + + template <typename Algorithm, typename Handles> + static auto apply( const Algorithm& algo, Handles& handles ) { + return std::apply( + [&]( const auto&... handle ) { + return algo( Gaudi::Hive::currentContext(), details::deref( handle.get() )... ); + }, + handles ); + } + + template <typename Algorithm, typename Handles> + static auto apply( const Algorithm& algo, const EventContext& ctx, Handles& handles ) { + return std::apply( [&]( const auto&... handle ) { return algo( ctx, details::deref( handle.get() )... ); }, + handles ); + } + }; + + template <typename... In> + using filter_evtcontext = typename filter_evtcontext_t<In...>::type; + + template <typename OutputSpec, typename InputSpec, typename Traits_> + class DataHandleMixin; + + template <typename Out, typename In, typename Tr> + void updateHandleLocation( DataHandleMixin<Out, In, Tr>& parent, const std::string& prop, + const std::string& newLoc ) { + auto sc = parent.setProperty( prop, newLoc ); + if ( sc.isFailure() ) throw GaudiException( "Could not set Property", prop + " -> " + newLoc, sc ); + } + + template <typename Out, typename In, typename Tr> + void updateHandleLocations( DataHandleMixin<Out, In, Tr>& parent, const std::string& prop, + const std::vector<std::string>& newLocs ) { + std::ostringstream ss; + GaudiUtils::details::ostream_joiner( ss << '[', newLocs, ", ", []( std::ostream & os, const auto& i ) -> auto& { + return os << "'" << i << "'"; + } ) << ']'; + auto sc = parent.setProperty( prop, ss.str() ); + if ( sc.isFailure() ) throw GaudiException( "Could not set Property", prop + " -> " + ss.str(), sc ); + } + + template <typename... Out, typename... In, typename Traits_> + class DataHandleMixin<std::tuple<Out...>, std::tuple<In...>, Traits_> : public BaseClass_t<Traits_> { + static_assert( std::is_base_of<Algorithm, BaseClass_t<Traits_>>::value, "BaseClass must inherit from Algorithm" ); + + template <typename IArgs, typename OArgs, std::size_t... I, std::size_t... J> + DataHandleMixin( const std::string& name, ISvcLocator* pSvcLocator, const IArgs& inputs, std::index_sequence<I...>, + const OArgs& outputs, std::index_sequence<J...> ) + : BaseClass_t<Traits_>( name, pSvcLocator ) + , m_inputs( std::tuple_cat( std::forward_as_tuple( this ), std::get<I>( inputs ) )... ) + , m_outputs( std::tuple_cat( std::forward_as_tuple( this ), std::get<J>( outputs ) )... ) { + // make sure this algorithm is seen as reentrant by Gaudi + this->setProperty( "Cardinality", 0 ); + } + + public: + constexpr static std::size_t N_in = sizeof...( In ); + constexpr static std::size_t N_out = sizeof...( Out ); + + using KeyValue = std::pair<std::string, std::string>; + using KeyValues = std::pair<std::string, std::vector<std::string>>; + + // generic constructor: N -> M + DataHandleMixin( const std::string& name, ISvcLocator* pSvcLocator, const std::array<KeyValue, N_in>& inputs, + const std::array<KeyValue, N_out>& outputs ) + : DataHandleMixin( name, pSvcLocator, inputs, std::index_sequence_for<In...>{}, outputs, + std::index_sequence_for<Out...>{} ) {} + + // special cases: forward to the generic case... + // 1 -> 1 + DataHandleMixin( const std::string& name, ISvcLocator* locator, const KeyValue& input, const KeyValue& output ) + : DataHandleMixin( name, locator, std::array<KeyValue, 1>{input}, std::array<KeyValue, 1>{output} ) {} + // 1 -> N + DataHandleMixin( const std::string& name, ISvcLocator* locator, const KeyValue& input, + const std::array<KeyValue, N_out>& outputs ) + : DataHandleMixin( name, locator, std::array<KeyValue, 1>{input}, outputs ) {} + // N -> 1 + DataHandleMixin( const std::string& name, ISvcLocator* locator, const std::array<KeyValue, N_in>& inputs, + const KeyValue& output ) + : DataHandleMixin( name, locator, inputs, std::array<KeyValue, 1>{output} ) {} + + template <std::size_t N = 0> + const std::string& inputLocation() const { + return std::get<N>( m_inputs ).objKey(); + } + constexpr unsigned int inputLocationSize() const { return N_in; } + + template <std::size_t N = 0> + const std::string& outputLocation() const { + return std::get<N>( m_outputs ).objKey(); + } + constexpr unsigned int outputLocationSize() const { return N_out; } + + protected: + bool isReEntrant() const override { return true; } + + std::tuple<details::InputHandle_t<Traits_, In>...> m_inputs; + std::tuple<details::OutputHandle_t<Traits_, Out>...> m_outputs; + }; + + template <typename Traits_> + class DataHandleMixin<void, std::tuple<>, Traits_> : public BaseClass_t<Traits_> { + static_assert( std::is_base_of<Algorithm, BaseClass_t<Traits_>>::value, "BaseClass must inherit from Algorithm" ); + + public: + DataHandleMixin( const std::string& name, ISvcLocator* pSvcLocator ) : BaseClass_t<Traits_>( name, pSvcLocator ) { + // make sure this algorithm is seen as reentrant by Gaudi + this->setProperty( "Cardinality", 0 ); + } + + protected: + bool isReEntrant() const override { return true; } + + std::tuple<> m_inputs; + }; + + template <typename... In, typename Traits_> + class DataHandleMixin<void, std::tuple<In...>, Traits_> : public BaseClass_t<Traits_> { + static_assert( std::is_base_of<Algorithm, BaseClass_t<Traits_>>::value, "BaseClass must inherit from Algorithm" ); + + template <typename IArgs, std::size_t... I> + DataHandleMixin( const std::string& name, ISvcLocator* pSvcLocator, const IArgs& inputs, std::index_sequence<I...> ) + : BaseClass_t<Traits_>( name, pSvcLocator ) + , m_inputs( std::tuple_cat( std::forward_as_tuple( this ), std::get<I>( inputs ) )... ) { + // make sure this algorithm is seen as reentrant by Gaudi + this->setProperty( "Cardinality", 0 ); + } + + public: + using KeyValue = std::pair<std::string, std::string>; + using KeyValues = std::pair<std::string, std::vector<std::string>>; + constexpr static std::size_t N_in = sizeof...( In ); + + // generic constructor: N -> 0 + DataHandleMixin( const std::string& name, ISvcLocator* pSvcLocator, const std::array<KeyValue, N_in>& inputs ) + : DataHandleMixin( name, pSvcLocator, inputs, std::index_sequence_for<In...>{} ) {} + + // special cases: forward to the generic case... + // 1 -> 0 + DataHandleMixin( const std::string& name, ISvcLocator* locator, const KeyValue& input ) + : DataHandleMixin( name, locator, std::array<KeyValue, 1>{input} ) {} + + template <std::size_t N = 0> + const std::string& inputLocation() const { + return std::get<N>( m_inputs ).objKey(); + } + constexpr unsigned int inputLocationSize() const { return N_in; } + + protected: + bool isReEntrant() const override { return true; } + + std::tuple<details::InputHandle_t<Traits_, In>...> m_inputs; + }; + + template <typename... Out, typename Traits_> + class DataHandleMixin<std::tuple<Out...>, void, Traits_> : public BaseClass_t<Traits_> { + static_assert( std::is_base_of<Algorithm, BaseClass_t<Traits_>>::value, "BaseClass must inherit from Algorithm" ); + + template <typename OArgs, std::size_t... J> + DataHandleMixin( const std::string& name, ISvcLocator* pSvcLocator, const OArgs& outputs, + std::index_sequence<J...> ) + : BaseClass_t<Traits_>( name, pSvcLocator ) + , m_outputs( std::tuple_cat( std::forward_as_tuple( this ), std::get<J>( outputs ) )... ) { + // make sure this algorithm is seen as reentrant by Gaudi + this->setProperty( "Cardinality", 0 ); + } + + public: + constexpr static std::size_t N_out = sizeof...( Out ); + using KeyValue = std::pair<std::string, std::string>; + using KeyValues = std::pair<std::string, std::vector<std::string>>; + + // generic constructor: 0 -> N + DataHandleMixin( const std::string& name, ISvcLocator* pSvcLocator, const std::array<KeyValue, N_out>& outputs ) + : DataHandleMixin( name, pSvcLocator, outputs, std::index_sequence_for<Out...>{} ) {} + + // 0 -> 1 + DataHandleMixin( const std::string& name, ISvcLocator* locator, const KeyValue& output ) + : DataHandleMixin( name, locator, std::array<KeyValue, 1>{output} ) {} + + template <std::size_t N = 0> + const std::string& outputLocation() const { + return std::get<N>( m_outputs ).objKey(); + } + constexpr unsigned int outputLocationSize() const { return N_out; } + + protected: + bool isReEntrant() const override { return true; } + + std::tuple<details::OutputHandle_t<Traits_, Out>...> m_outputs; + }; + + ///////////////// + template <typename Fun, typename Container, typename... Args> + constexpr void applyPostProcessing( const Fun&, Container&, Args... ) { + static_assert( sizeof...( Args ) == 0, "Args should not be used!" ); + } + + template <typename Fun, typename Container> + auto applyPostProcessing( const Fun& fun, Container& c ) -> decltype( fun.postprocess( c ), void() ) { + fun.postprocess( c ); + } + +} // namespace Gaudi::Functional::details #endif diff --git a/GaudiAlg/GaudiAlg/FunctionalUtilities.h b/GaudiAlg/GaudiAlg/FunctionalUtilities.h index 33a9ff5c12d5df7a249da20e08d277950ae0b748..f02dba5d755f5b5e3ed7d499a4ac68e87519414b 100644 --- a/GaudiAlg/GaudiAlg/FunctionalUtilities.h +++ b/GaudiAlg/GaudiAlg/FunctionalUtilities.h @@ -11,82 +11,77 @@ #include "GaudiKernel/DataObjectHandle.h" #include "GaudiKernel/SerializeSTL.h" -namespace Gaudi { - namespace Functional { - - // This utility is needed when the inputs of a functional algorithm may be stored in several locations - inline std::string concat_alternatives( std::initializer_list<std::string> c ) { - return boost::algorithm::join( c, ":" ); - } - - template <typename... Strings> - std::string concat_alternatives( const Strings&... s ) { - return concat_alternatives( std::initializer_list<std::string>{s...} ); - } - - inline void updateHandleLocation( IProperty& parent, const std::string& prop, const std::string& newLoc ) { - auto sc = parent.setProperty( prop, newLoc ); - if ( sc.isFailure() ) throw GaudiException( "Could not set Property", prop + " -> " + newLoc, sc ); - } - - inline void updateHandleLocations( IProperty& parent, const std::string& prop, - const std::vector<std::string>& newLocs ) { - std::ostringstream ss; - GaudiUtils::details::ostream_joiner( ss << '[', newLocs, ", ", []( std::ostream & os, const auto& i ) -> auto& { - return os << "'" << i << "'"; - } ) << ']'; - auto sc = parent.setProperty( prop, ss.str() ); - if ( sc.isFailure() ) throw GaudiException( "Could not set Property", prop + " -> " + ss.str(), sc ); - } - - [[deprecated( "please use updateHandleLocation instead" )]] inline void - updateReadHandleLocation( IProperty& parent, const std::string& prop, const std::string& newLoc ) { - return updateHandleLocation( parent, prop, newLoc ); - } - - namespace Traits { - - // traits classes used to customize Transformer and FilterPredicate - // Define the types to to be used as baseclass, and as in- resp. output hanldes. - // In case a type is not specified in the traits struct, a default is used. - // - // The defaults are: - // - // using BaseClass = GaudiAlgorithm - // template <typename T> using InputHandle = DataObjectHandle<T>; - // template <typename T> using OutputHandle = DataObjectHandle<T>; - // - - // the best way to 'compose' traits is by inheriting them one-by-one... - template <typename... Base> - struct use_ : Base... {}; - - // helper classes one can inherit from to specify a specific trait - template <typename Base> - struct BaseClass_t { - using BaseClass = Base; - }; - - template <template <typename> class Handle> - struct InputHandle_t { - template <typename T> - using InputHandle = Handle<T>; - }; - - template <template <typename> class Handle> - struct OutputHandle_t { - template <typename T> - using OutputHandle = Handle<T>; - }; - - // this uses the defaults -- and it itself is the default ;-) - using useDefaults = use_<>; - - // this example uses GaudiHistoAlg as baseclass, and the default handle types for - // input and output - using useGaudiHistoAlg = use_<BaseClass_t<GaudiHistoAlg>>; - } // namespace Traits - } // namespace Functional -} // namespace Gaudi +namespace Gaudi::Functional { + + // This utility is needed when the inputs of a functional algorithm may be stored in several locations + inline std::string concat_alternatives( std::initializer_list<std::string> c ) { + return boost::algorithm::join( c, ":" ); + } + + template <typename... Strings> + std::string concat_alternatives( const Strings&... s ) { + return concat_alternatives( std::initializer_list<std::string>{s...} ); + } + + [[deprecated( "please use `updateHandleLocation` instead of `Gaudi::Functional::updateHandleLocation`" )]] inline void + updateHandleLocation( IProperty& parent, const std::string& prop, const std::string& newLoc ) { + auto sc = parent.setProperty( prop, newLoc ); + if ( sc.isFailure() ) throw GaudiException( "Could not set Property", prop + " -> " + newLoc, sc ); + } + + [[deprecated( + "please use `updateHandleLocations` instead of `Gaudi::Functional::updateHandleLocations`" )]] inline void + updateHandleLocations( IProperty& parent, const std::string& prop, const std::vector<std::string>& newLocs ) { + std::ostringstream ss; + GaudiUtils::details::ostream_joiner( ss << '[', newLocs, ", ", []( std::ostream & os, const auto& i ) -> auto& { + return os << "'" << i << "'"; + } ) << ']'; + auto sc = parent.setProperty( prop, ss.str() ); + if ( sc.isFailure() ) throw GaudiException( "Could not set Property", prop + " -> " + ss.str(), sc ); + } + + namespace Traits { + + // traits classes used to customize Transformer and FilterPredicate + // Define the types to to be used as baseclass, and as in- resp. output hanldes. + // In case a type is not specified in the traits struct, a default is used. + // + // The defaults are: + // + // using BaseClass = GaudiAlgorithm + // template <typename T> using InputHandle = DataObjectHandle<T>; + // template <typename T> using OutputHandle = DataObjectHandle<T>; + // + + // the best way to 'compose' traits is by inheriting them one-by-one... + template <typename... Base> + struct use_ : Base... {}; + + // helper classes one can inherit from to specify a specific trait + template <typename Base> + struct BaseClass_t { + using BaseClass = Base; + }; + + template <template <typename> class Handle> + struct InputHandle_t { + template <typename T> + using InputHandle = Handle<T>; + }; + + template <template <typename> class Handle> + struct OutputHandle_t { + template <typename T> + using OutputHandle = Handle<T>; + }; + + // this uses the defaults -- and it itself is the default ;-) + using useDefaults = use_<>; + + // this example uses GaudiHistoAlg as baseclass, and the default handle types for + // input and output + using useGaudiHistoAlg = use_<BaseClass_t<GaudiHistoAlg>>; + } // namespace Traits +} // namespace Gaudi::Functional #endif diff --git a/GaudiAlg/GaudiAlg/MergingTransformer.h b/GaudiAlg/GaudiAlg/MergingTransformer.h index 49b5baea905c4ffce82b592c60c349698792cb04..af1bc9f891c36b2dd12fcf10c6fcfbd1b184048b 100644 --- a/GaudiAlg/GaudiAlg/MergingTransformer.h +++ b/GaudiAlg/GaudiAlg/MergingTransformer.h @@ -8,26 +8,40 @@ #include "GaudiAlg/FunctionalDetails.h" #include "GaudiAlg/FunctionalUtilities.h" -namespace Gaudi { - namespace Functional { +namespace Gaudi::Functional { - template <typename Signature, typename Traits_ = Traits::useDefaults> - class MergingTransformer; + using details::vector_of_const_; + + namespace details { - using details::vector_of_const_; + template <typename Signature, typename Traits_, bool isLegacy> + class MergingTransformer; ////// Many of the same -> 1 template <typename Out, typename In, typename Traits_> - class MergingTransformer<Out( const vector_of_const_<In>& ), Traits_> + class MergingTransformer<Out( const vector_of_const_<In>& ), Traits_, true> : public details::DataHandleMixin<std::tuple<Out>, void, Traits_> { using base_class = details::DataHandleMixin<std::tuple<Out>, void, Traits_>; public: - using KeyValue = std::pair<std::string, std::string>; - using KeyValues = std::pair<std::string, std::vector<std::string>>; + using KeyValue = typename base_class::KeyValue; + using KeyValues = typename base_class::KeyValues; MergingTransformer( const std::string& name, ISvcLocator* locator, const KeyValues& inputs, - const KeyValue& output ); + const KeyValue& output ) + : base_class( name, locator, output ) + , m_inputLocations{this, inputs.first, inputs.second, + [=]( Gaudi::Details::PropertyBase& ) { + this->m_inputs = details::make_vector_of_handles<decltype( this->m_inputs )>( + this, m_inputLocations ); + if ( std::is_pointer<In>::value ) { // handle constructor does not (yet) allow to set + // optional flag... so do it + // explicitly here... + std::for_each( this->m_inputs.begin(), this->m_inputs.end(), + []( auto& h ) { h.setOptional( true ); } ); + } + }, + Gaudi::Details::Property::ImmediatelyInvokeHandler{true}} {} // accessor to input Locations const std::string& inputLocation( unsigned int n ) const { return m_inputLocations.value()[n]; } @@ -40,13 +54,12 @@ namespace Gaudi { std::transform( m_inputs.begin(), m_inputs.end(), std::back_inserter( ins ), details::details2::get_from_handle<In>{} ); try { - using details::as_const; - details::put( std::get<0>( this->m_outputs ), as_const( *this )( as_const( ins ) ) ); + details::put( std::get<0>( this->m_outputs ), std::as_const( *this )( std::as_const( ins ) ) ); + return StatusCode::SUCCESS; } catch ( GaudiException& e ) { ( e.code() ? this->warning() : this->error() ) << e.message() << endmsg; return e.code(); } - return StatusCode::SUCCESS; } virtual Out operator()( const vector_of_const_<In>& inputs ) const = 0; @@ -62,24 +75,66 @@ namespace Gaudi { }; template <typename Out, typename In, typename Traits_> - MergingTransformer<Out( const vector_of_const_<In>& ), Traits_>::MergingTransformer( const std::string& name, - ISvcLocator* pSvcLocator, - const KeyValues& inputs, - const KeyValue& output ) - : base_class( name, pSvcLocator, output ) - , m_inputLocations{this, inputs.first, inputs.second, - [=]( Gaudi::Details::PropertyBase& ) { - this->m_inputs = - details::make_vector_of_handles<decltype( this->m_inputs )>( this, m_inputLocations ); - if ( std::is_pointer<In>::value ) { // handle constructor does not (yet) allow to set - // optional flag... so do it - // explicitly here... - std::for_each( this->m_inputs.begin(), this->m_inputs.end(), - []( auto& h ) { h.setOptional( true ); } ); - } - }, - Gaudi::Details::Property::ImmediatelyInvokeHandler{true}} {} - } // namespace Functional -} // namespace Gaudi + class MergingTransformer<Out( const vector_of_const_<In>& ), Traits_, false> + : public details::DataHandleMixin<std::tuple<Out>, void, Traits_> { + using base_class = details::DataHandleMixin<std::tuple<Out>, void, Traits_>; + + public: + using KeyValue = typename base_class::KeyValue; + using KeyValues = typename base_class::KeyValues; + + MergingTransformer( const std::string& name, ISvcLocator* locator, const KeyValues& inputs, + const KeyValue& output ) + : base_class( name, locator, output ) + , m_inputLocations{this, inputs.first, inputs.second, + [=]( Gaudi::Details::PropertyBase& ) { + this->m_inputs = details::make_vector_of_handles<decltype( this->m_inputs )>( + this, m_inputLocations ); + if ( std::is_pointer<In>::value ) { // handle constructor does not (yet) allow to set + // optional flag... so do it + // explicitly here... + std::for_each( this->m_inputs.begin(), this->m_inputs.end(), + []( auto& h ) { h.setOptional( true ); } ); + } + }, + Gaudi::Details::Property::ImmediatelyInvokeHandler{true}} {} + + // accessor to input Locations + const std::string& inputLocation( unsigned int n ) const { return m_inputLocations.value()[n]; } + unsigned int inputLocationSize() const { return m_inputLocations.value().size(); } + + // derived classes can NOT implement execute + StatusCode execute( const EventContext& ) const override final { + vector_of_const_<In> ins; + ins.reserve( m_inputs.size() ); + std::transform( m_inputs.begin(), m_inputs.end(), std::back_inserter( ins ), + details::details2::get_from_handle<In>{} ); + try { + details::put( std::get<0>( this->m_outputs ), std::as_const( *this )( std::as_const( ins ) ) ); + return StatusCode::SUCCESS; + } catch ( GaudiException& e ) { + ( e.code() ? this->warning() : this->error() ) << e.message() << endmsg; + return e.code(); + } + } + + virtual Out operator()( const vector_of_const_<In>& inputs ) const = 0; + + private: + // if In is a pointer, it signals optional (as opposed to mandatory) input + template <typename T> + using InputHandle_t = details::InputHandle_t<Traits_, typename std::remove_pointer<T>::type>; + std::vector<InputHandle_t<In>> m_inputs; // and make the handles properties instead... + Gaudi::Property<std::vector<std::string>> m_inputLocations; // TODO/FIXME: remove this duplication... + // TODO/FIXME: replace vector of string property + call-back with a + // vector<handle> property ... as soon as declareProperty can deal with that. + }; + + } // namespace details + + template <typename Signature, typename Traits_ = Traits::useDefaults> + using MergingTransformer = details::MergingTransformer<Signature, Traits_, details::isLegacy<Traits_>>; + +} // namespace Gaudi::Functional #endif diff --git a/GaudiAlg/GaudiAlg/Producer.h b/GaudiAlg/GaudiAlg/Producer.h index f60b41a22e27c97f9bad1ffeec34e39675c00c22..734ad77818a173c2d705ba3e3ce5c759239bb7bc 100644 --- a/GaudiAlg/GaudiAlg/Producer.h +++ b/GaudiAlg/GaudiAlg/Producer.h @@ -5,14 +5,16 @@ #include "GaudiAlg/FunctionalUtilities.h" #include <utility> -namespace Gaudi { - namespace Functional { +namespace Gaudi::Functional { - template <typename Signature, typename Traits_ = Traits::useDefaults> + namespace details { + + template <typename Signature, typename Traits_, bool isLegacy> class Producer; template <typename... Out, typename Traits_> - class Producer<std::tuple<Out...>(), Traits_> : public details::DataHandleMixin<std::tuple<Out...>, void, Traits_> { + class Producer<std::tuple<Out...>(), Traits_, true> + : public details::DataHandleMixin<std::tuple<Out...>, void, Traits_> { public: using details::DataHandleMixin<std::tuple<Out...>, void, Traits_>::DataHandleMixin; @@ -25,14 +27,43 @@ namespace Gaudi { [&ohandle...]( auto&&... data ) { ( details::put( ohandle, std::forward<decltype( data )>( data ) ), ... ); }, - details::as_const( *this )() ); + std::as_const( *this )() ); + }, + this->m_outputs ); + return StatusCode::SUCCESS; + } catch ( GaudiException& e ) { + ( e.code() ? this->warning() : this->error() ) << e.message() << endmsg; + return e.code(); + } + } + + // ... instead, they must implement the following operator + virtual std::tuple<Out...> operator()() const = 0; + }; + + template <typename... Out, typename Traits_> + class Producer<std::tuple<Out...>(), Traits_, false> + : public details::DataHandleMixin<std::tuple<Out...>, void, Traits_> { + public: + using details::DataHandleMixin<std::tuple<Out...>, void, Traits_>::DataHandleMixin; + + // derived classes are NOT allowed to implement execute ... + StatusCode execute( const EventContext& ) const override final { + try { + std::apply( + [&]( auto&... ohandle ) { + std::apply( + [&ohandle...]( auto&&... data ) { + ( details::put( ohandle, std::forward<decltype( data )>( data ) ), ... ); + }, + std::as_const( *this )() ); }, this->m_outputs ); + return StatusCode::SUCCESS; } catch ( GaudiException& e ) { ( e.code() ? this->warning() : this->error() ) << e.message() << endmsg; return e.code(); } - return StatusCode::SUCCESS; } // ... instead, they must implement the following operator @@ -40,24 +71,47 @@ namespace Gaudi { }; template <typename Out, typename Traits_> - class Producer<Out(), Traits_> : public details::DataHandleMixin<std::tuple<Out>, void, Traits_> { + class Producer<Out(), Traits_, true> : public details::DataHandleMixin<std::tuple<Out>, void, Traits_> { public: using details::DataHandleMixin<std::tuple<Out>, void, Traits_>::DataHandleMixin; // derived classes are NOT allowed to implement execute ... StatusCode execute() override final { try { - details::put( std::get<0>( this->m_outputs ), details::as_const( *this )() ); + details::put( std::get<0>( this->m_outputs ), std::as_const( *this )() ); + return StatusCode::SUCCESS; } catch ( GaudiException& e ) { ( e.code() ? this->warning() : this->error() ) << e.message() << endmsg; return e.code(); } - return StatusCode::SUCCESS; } // ... instead, they must implement the following operator virtual Out operator()() const = 0; }; - } // namespace Functional -} // namespace Gaudi + + template <typename Out, typename Traits_> + class Producer<Out(), Traits_, false> : public details::DataHandleMixin<std::tuple<Out>, void, Traits_> { + public: + using details::DataHandleMixin<std::tuple<Out>, void, Traits_>::DataHandleMixin; + // derived classes are NOT allowed to implement execute ... + StatusCode execute( const EventContext& ) const override final { + try { + details::put( std::get<0>( this->m_outputs ), std::as_const( *this )() ); + return StatusCode::SUCCESS; + } catch ( GaudiException& e ) { + ( e.code() ? this->warning() : this->error() ) << e.message() << endmsg; + return e.code(); + } + } + + // ... instead, they must implement the following operator + virtual Out operator()() const = 0; + }; + } // namespace details + + template <typename Signature, typename Traits_ = Traits::useDefaults> + using Producer = details::Producer<Signature, Traits_, details::isLegacy<Traits_>>; + +} // namespace Gaudi::Functional #endif diff --git a/GaudiAlg/GaudiAlg/SplittingTransformer.h b/GaudiAlg/GaudiAlg/SplittingTransformer.h index 1c0d363651d974a692a6dafdb596b93479b17a9d..fdb0a434c723af8af15ed3f96c768e8542df6b88 100644 --- a/GaudiAlg/GaudiAlg/SplittingTransformer.h +++ b/GaudiAlg/GaudiAlg/SplittingTransformer.h @@ -8,30 +8,44 @@ #include "GaudiAlg/FunctionalDetails.h" #include "GaudiAlg/FunctionalUtilities.h" -namespace Gaudi { - namespace Functional { +namespace Gaudi::Functional { - template <typename Signature, typename Traits_ = Traits::useDefaults> - class SplittingTransformer; + template <typename Container> + using vector_of_ = std::vector<Container>; + template <typename Container> + using vector_of_optional_ = std::vector<boost::optional<Container>>; + + namespace details { - template <typename Container> - using vector_of_ = std::vector<Container>; - template <typename Container> - using vector_of_optional_ = std::vector<boost::optional<Container>>; + template <typename Signature, typename Traits_, bool isLegacy> + class SplittingTransformer; ////// N -> Many of the same one (value of Many not known at compile time, but known at configuration time) template <typename Out, typename... In, typename Traits_> - class SplittingTransformer<vector_of_<Out>( const In&... ), Traits_> + class SplittingTransformer<vector_of_<Out>( const In&... ), Traits_, true> : public details::DataHandleMixin<void, std::tuple<In...>, Traits_> { using base_class = details::DataHandleMixin<void, std::tuple<In...>, Traits_>; public: - constexpr static std::size_t N = sizeof...( In ); - using KeyValue = std::pair<std::string, std::string>; - using KeyValues = std::pair<std::string, std::vector<std::string>>; + constexpr static std::size_t N = base_class::N_in; + using KeyValue = typename base_class::KeyValue; + using KeyValues = typename base_class::KeyValues; SplittingTransformer( const std::string& name, ISvcLocator* locator, const std::array<KeyValue, N>& inputs, - const KeyValues& output ); + const KeyValues& outputs ) + : base_class( name, locator, inputs ) + , m_outputLocations( this, outputs.first, outputs.second, + [=]( Gaudi::Details::PropertyBase& ) { + this->m_outputs = details::make_vector_of_handles<decltype( this->m_outputs )>( + this, m_outputLocations ); + if ( details::is_optional<Out>::value ) { // handle constructor does not (yet) allow to + // set optional flag... so + // do it explicitly here... + std::for_each( this->m_outputs.begin(), this->m_outputs.end(), + []( auto& h ) { h.setOptional( true ); } ); + } + }, + Gaudi::Details::Property::ImmediatelyInvokeHandler{true} ) {} SplittingTransformer( const std::string& name, ISvcLocator* locator, const KeyValue& input, const KeyValues& output ) @@ -47,7 +61,6 @@ namespace Gaudi { StatusCode execute() override final { try { // TODO:FIXME: how does operator() know the number and order of expected outputs? - using details::as_const; auto out = details::filter_evtcontext_t<In...>::apply( *this, this->m_inputs ); if ( out.size() != m_outputs.size() ) { throw GaudiException( "Error during transform: expected " + std::to_string( m_outputs.size() ) + @@ -74,25 +87,78 @@ namespace Gaudi { Gaudi::Property<std::vector<std::string>> m_outputLocations; // TODO/FIXME for now: use a call-back to update the // actual handles! }; - template <typename Out, typename... In, typename Traits_> - SplittingTransformer<vector_of_<Out>( const In&... ), Traits_>::SplittingTransformer( - const std::string& name, ISvcLocator* pSvcLocator, const std::array<KeyValue, N>& inputs, - const KeyValues& outputs ) - : base_class( name, pSvcLocator, inputs ) - , m_outputLocations( this, outputs.first, outputs.second, - [=]( Gaudi::Details::PropertyBase& ) { - this->m_outputs = details::make_vector_of_handles<decltype( this->m_outputs )>( - this, m_outputLocations ); - if ( details::is_optional<Out>::value ) { // handle constructor does not (yet) allow to - // set optional flag... so - // do it explicitly here... - std::for_each( this->m_outputs.begin(), this->m_outputs.end(), - []( auto& h ) { h.setOptional( true ); } ); - } - }, - Gaudi::Details::Property::ImmediatelyInvokeHandler{true} ) {} - } // namespace Functional -} // namespace Gaudi + class SplittingTransformer<vector_of_<Out>( const In&... ), Traits_, false> + : public details::DataHandleMixin<void, std::tuple<In...>, Traits_> { + using base_class = details::DataHandleMixin<void, std::tuple<In...>, Traits_>; + + public: + constexpr static std::size_t N = base_class::N_in; + using KeyValue = typename base_class::KeyValue; + using KeyValues = typename base_class::KeyValues; + + SplittingTransformer( const std::string& name, ISvcLocator* locator, const std::array<KeyValue, N>& inputs, + const KeyValues& outputs ) + : base_class( name, locator, inputs ) + , m_outputLocations( this, outputs.first, outputs.second, + [=]( Gaudi::Details::PropertyBase& ) { + this->m_outputs = details::make_vector_of_handles<decltype( this->m_outputs )>( + this, m_outputLocations ); + if ( details::is_optional<Out>::value ) { // handle constructor does not (yet) allow to + // set optional flag... so + // do it explicitly here... + std::for_each( this->m_outputs.begin(), this->m_outputs.end(), + []( auto& h ) { h.setOptional( true ); } ); + } + }, + Gaudi::Details::Property::ImmediatelyInvokeHandler{true} ) {} + + SplittingTransformer( const std::string& name, ISvcLocator* locator, const KeyValue& input, + const KeyValues& output ) + : SplittingTransformer( name, locator, std::array<KeyValue, 1>{input}, output ) { + static_assert( N == 1, "single input argument requires single input signature" ); + } + + // accessor to output Locations + const std::string& outputLocation( unsigned int n ) const { return m_outputLocations.value()[n]; } + unsigned int outputLocationSize() const { return m_outputLocations.value().size(); } + + // derived classes can NOT implement execute + StatusCode execute( const EventContext& ctx ) const override final { + try { + // TODO:FIXME: how does operator() know the number and order of expected outputs? + auto out = details::filter_evtcontext_t<In...>::apply( *this, ctx, this->m_inputs ); + if ( out.size() != m_outputs.size() ) { + throw GaudiException( "Error during transform: expected " + std::to_string( m_outputs.size() ) + + " containers, got " + std::to_string( out.size() ) + " instead", + this->name(), StatusCode::FAILURE ); + } + for ( unsigned i = 0; i != out.size(); ++i ) details::put( m_outputs[i], std::move( out[i] ) ); + return StatusCode::SUCCESS; + } catch ( GaudiException& e ) { + ( e.code() ? this->warning() : this->error() ) << e.message() << endmsg; + return e.code(); + } + } + + // TODO/FIXME: how does the callee know in which order to produce the outputs? + // (note: 'missing' items can be specified by making Out an boost::optional<Out>, + // and only those entries which contain an Out are stored) + virtual vector_of_<Out> operator()( const In&... ) const = 0; + + private: + template <typename T> + using OutputHandle = details::OutputHandle_t<Traits_, details::remove_optional_t<T>>; + std::vector<OutputHandle<Out>> m_outputs; + Gaudi::Property<std::vector<std::string>> m_outputLocations; // TODO/FIXME for now: use a call-back to update the + // actual handles! + }; + + } // namespace details + + template <typename Signature, typename Traits_ = Traits::useDefaults> + using SplittingTransformer = details::SplittingTransformer<Signature, Traits_, details::isLegacy<Traits_>>; + +} // namespace Gaudi::Functional #endif diff --git a/GaudiAlg/GaudiAlg/Transformer.h b/GaudiAlg/GaudiAlg/Transformer.h index 796b810f063147c931270ac8b2c91d91f3672358..e1585f155f74ed6f61fad59e739feda9fa86e449 100644 --- a/GaudiAlg/GaudiAlg/Transformer.h +++ b/GaudiAlg/GaudiAlg/Transformer.h @@ -13,15 +13,16 @@ // b) are encouraged not to have state which depends on the events // (eg. histograms, counters will have to be mutable) -namespace Gaudi { - namespace Functional { +namespace Gaudi ::Functional { - template <typename Signature, typename Traits_ = Traits::useDefaults> + namespace details { + + template <typename Signature, typename Traits_, bool isLegacy> class Transformer; // general N -> 1 algorithms template <typename Out, typename... In, typename Traits_> - class Transformer<Out( const In&... ), Traits_> + class Transformer<Out( const In&... ), Traits_, true> : public details::DataHandleMixin<std::tuple<Out>, details::filter_evtcontext<In...>, Traits_> { public: using details::DataHandleMixin<std::tuple<Out>, details::filter_evtcontext<In...>, Traits_>::DataHandleMixin; @@ -42,14 +43,36 @@ namespace Gaudi { virtual Out operator()( const In&... ) const = 0; }; + template <typename Out, typename... In, typename Traits_> + class Transformer<Out( const In&... ), Traits_, false> + : public details::DataHandleMixin<std::tuple<Out>, details::filter_evtcontext<In...>, Traits_> { + public: + using details::DataHandleMixin<std::tuple<Out>, details::filter_evtcontext<In...>, Traits_>::DataHandleMixin; + + // derived classes can NOT implement execute + StatusCode execute( const EventContext& ctx ) const override final { + try { + details::put( std::get<0>( this->m_outputs ), + details::filter_evtcontext_t<In...>::apply( *this, ctx, this->m_inputs ) ); + return StatusCode::SUCCESS; + } catch ( GaudiException& e ) { + ( e.code() ? this->warning() : this->error() ) << e.message() << endmsg; + return e.code(); + } + } + + // instead they MUST implement this operator + virtual Out operator()( const In&... ) const = 0; + }; + // // general N -> M algorithms // - template <typename Signature, typename Traits_ = Traits::useDefaults> + template <typename Signature, typename Traits_, bool isLegacy> class MultiTransformer; template <typename... Out, typename... In, typename Traits_> - class MultiTransformer<std::tuple<Out...>( const In&... ), Traits_> + class MultiTransformer<std::tuple<Out...>( const In&... ), Traits_, true> : public details::DataHandleMixin<std::tuple<Out...>, details::filter_evtcontext<In...>, Traits_> { public: using details::DataHandleMixin<std::tuple<Out...>, details::filter_evtcontext<In...>, Traits_>::DataHandleMixin; @@ -71,6 +94,45 @@ namespace Gaudi { }, details::filter_evtcontext_t<In...>::apply( *this, this->m_inputs ) ); +#if defined( __clang__ ) && ( __clang_major__ < 6 ) +# pragma clang diagnostic pop +#endif + }, + this->m_outputs ); + return StatusCode::SUCCESS; + } catch ( GaudiException& e ) { + ( e.code() ? this->warning() : this->error() ) << e.message() << endmsg; + return e.code(); + } + } + + // instead they MUST implement this operator + virtual std::tuple<Out...> operator()( const In&... ) const = 0; + }; + + template <typename... Out, typename... In, typename Traits_> + class MultiTransformer<std::tuple<Out...>( const In&... ), Traits_, false> + : public details::DataHandleMixin<std::tuple<Out...>, details::filter_evtcontext<In...>, Traits_> { + public: + using details::DataHandleMixin<std::tuple<Out...>, details::filter_evtcontext<In...>, Traits_>::DataHandleMixin; + + // derived classes can NOT implement execute + StatusCode execute( const EventContext& ctx ) const override final { + try { + std::apply( + [this, &ctx]( auto&... ohandle ) { + +#if defined( __clang__ ) && ( __clang_major__ < 6 ) +// clang-5 gives a spurious warning about not using the captured `ohandle` +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-lambda-capture" +#endif + std::apply( + [&ohandle...]( auto&&... data ) { + ( details::put( ohandle, std::forward<decltype( data )>( data ) ), ... ); + }, + details::filter_evtcontext_t<In...>::apply( *this, ctx, this->m_inputs ) ); + #if defined( __clang__ ) && ( __clang_major__ < 6 ) # pragma clang diagnostic pop #endif @@ -90,11 +152,11 @@ namespace Gaudi { // // general N -> M algorithms with filter functionality // - template <typename Signature, typename Traits_ = Traits::useDefaults> + template <typename Signature, typename Traits_, bool isLegacy> class MultiTransformerFilter; template <typename... Out, typename... In, typename Traits_> - class MultiTransformerFilter<std::tuple<Out...>( const In&... ), Traits_> + class MultiTransformerFilter<std::tuple<Out...>( const In&... ), Traits_, true> : public details::DataHandleMixin<std::tuple<Out...>, std::tuple<In...>, Traits_> { public: using details::DataHandleMixin<std::tuple<Out...>, std::tuple<In...>, Traits_>::DataHandleMixin; @@ -122,7 +184,47 @@ namespace Gaudi { // instead they MUST implement this operator virtual std::tuple<bool, Out...> operator()( const In&... ) const = 0; }; - } // namespace Functional -} // namespace Gaudi + + template <typename... Out, typename... In, typename Traits_> + class MultiTransformerFilter<std::tuple<Out...>( const In&... ), Traits_, false> + : public details::DataHandleMixin<std::tuple<Out...>, std::tuple<In...>, Traits_> { + public: + using details::DataHandleMixin<std::tuple<Out...>, std::tuple<In...>, Traits_>::DataHandleMixin; + + // derived classes can NOT implement execute + StatusCode execute( const EventContext& ctx ) const override final { + try { + std::apply( + [&]( auto&... ohandle ) { + std::apply( + [&ohandle..., this]( bool passed, auto&&... data ) { + this->setFilterPassed( passed ); + ( details::put( ohandle, std::forward<decltype( data )>( data ) ), ... ); + }, + details::filter_evtcontext_t<In...>::apply( *this, ctx, this->m_inputs ) ); + }, + this->m_outputs ); + return StatusCode::SUCCESS; + } catch ( GaudiException& e ) { + ( e.code() ? this->warning() : this->error() ) << e.message() << endmsg; + return e.code(); + } + } + + // instead they MUST implement this operator + virtual std::tuple<bool, Out...> operator()( const In&... ) const = 0; + }; + } // namespace details + + template <typename Signature, typename Traits_ = Traits::useDefaults> + using Transformer = details::Transformer<Signature, Traits_, details::isLegacy<Traits_>>; + + template <typename Signature, typename Traits_ = Traits::useDefaults> + using MultiTransformer = details::MultiTransformer<Signature, Traits_, details::isLegacy<Traits_>>; + + template <typename Signature, typename Traits_ = Traits::useDefaults> + using MultiTransformerFilter = details::MultiTransformerFilter<Signature, Traits_, details::isLegacy<Traits_>>; + +} // namespace Gaudi::Functional #endif diff --git a/GaudiExamples/src/FunctionalAlgorithms/MakeAndConsume.cpp b/GaudiExamples/src/FunctionalAlgorithms/MakeAndConsume.cpp index a75992f1190968bbfe9d6b263eff2244d37bad19..fef37ccff98b5e1e45dce3fd117272c2b5963d53 100644 --- a/GaudiExamples/src/FunctionalAlgorithms/MakeAndConsume.cpp +++ b/GaudiExamples/src/FunctionalAlgorithms/MakeAndConsume.cpp @@ -5,247 +5,246 @@ #include "GaudiKernel/KeyedContainer.h" #include <cmath> -namespace Gaudi { - namespace Examples { +namespace Gaudi::Examples { - using BaseClass_t = Gaudi::Functional::Traits::BaseClass_t<::Algorithm>; + using LegacyBaseClass_t = Gaudi::Functional::Traits::BaseClass_t<::Algorithm>; + using BaseClass_t = Gaudi::Functional::Traits::BaseClass_t<Gaudi::Algorithm>; - struct IntDataProducer final : Gaudi::Functional::Producer<int(), BaseClass_t> { + struct IntDataProducer final : Gaudi::Functional::Producer<int(), BaseClass_t> { - IntDataProducer( const std::string& name, ISvcLocator* svcLoc ) - : Producer( name, svcLoc, KeyValue( "OutputLocation", "/Event/MyInt" ) ) {} + IntDataProducer( const std::string& name, ISvcLocator* svcLoc ) + : Producer( name, svcLoc, KeyValue( "OutputLocation", "/Event/MyInt" ) ) {} - int operator()() const override { - info() << "executing IntDataProducer, storing 7 into " << outputLocation() << endmsg; - return 7; - } - }; + int operator()() const override { + info() << "executing IntDataProducer, storing 7 into " << outputLocation() << endmsg; + return 7; + } + }; - DECLARE_COMPONENT( IntDataProducer ) + DECLARE_COMPONENT( IntDataProducer ) - struct VectorDataProducer final : Gaudi::Functional::Producer<std::vector<int>(), BaseClass_t> { + struct VectorDataProducer final : Gaudi::Functional::Producer<std::vector<int>(), BaseClass_t> { - VectorDataProducer( const std::string& name, ISvcLocator* svcLoc ) - : Producer( name, svcLoc, KeyValue( "OutputLocation", "/Event/MyVector" ) ) {} + VectorDataProducer( const std::string& name, ISvcLocator* svcLoc ) + : Producer( name, svcLoc, KeyValue( "OutputLocation", "/Event/MyVector" ) ) {} - std::vector<int> operator()() const override { - info() << "executing VectorDataProducer, storing [3,3,3,3] into " << outputLocation() << endmsg; - return {3, 3, 3, 3}; - } - }; + std::vector<int> operator()() const override { + info() << "executing VectorDataProducer, storing [3,3,3,3] into " << outputLocation() << endmsg; + return {3, 3, 3, 3}; + } + }; - DECLARE_COMPONENT( VectorDataProducer ) + DECLARE_COMPONENT( VectorDataProducer ) - using int_container = KeyedContainer<KeyedObject<int>, Containers::HashMap>; - struct KeyedDataProducer final : Gaudi::Functional::Producer<int_container(), BaseClass_t> { + using int_container = KeyedContainer<KeyedObject<int>, Containers::HashMap>; + struct KeyedDataProducer final : Gaudi::Functional::Producer<int_container(), BaseClass_t> { - KeyedDataProducer( const std::string& name, ISvcLocator* svcLoc ) - : Producer( name, svcLoc, KeyValue( "OutputLocation", "/Event/MyKeyed" ) ) {} + KeyedDataProducer( const std::string& name, ISvcLocator* svcLoc ) + : Producer( name, svcLoc, KeyValue( "OutputLocation", "/Event/MyKeyed" ) ) {} - int_container operator()() const override { - int_container container; - auto a = new KeyedObject<int>{4}; - container.add( a ); - auto b = new KeyedObject<int>{5}; - container.add( b ); - info() << "executing KeyedDataProducer, storing [4,5] into " << outputLocation() << endmsg; - return container; - } - }; + int_container operator()() const override { + int_container container; + auto a = new KeyedObject<int>{4}; + container.add( a ); + auto b = new KeyedObject<int>{5}; + container.add( b ); + info() << "executing KeyedDataProducer, storing [4,5] into " << outputLocation() << endmsg; + return container; + } + }; - DECLARE_COMPONENT( KeyedDataProducer ) + DECLARE_COMPONENT( KeyedDataProducer ) - struct IntDataConsumer final : Gaudi::Functional::Consumer<void( const int& ), BaseClass_t> { + struct IntDataConsumer final : Gaudi::Functional::Consumer<void( const int& ), BaseClass_t> { - IntDataConsumer( const std::string& name, ISvcLocator* svcLoc ) - : Consumer( name, svcLoc, KeyValue( "InputLocation", "/Event/MyInt" ) ) {} + IntDataConsumer( const std::string& name, ISvcLocator* svcLoc ) + : Consumer( name, svcLoc, KeyValue( "InputLocation", "/Event/MyInt" ) ) {} - void operator()( const int& input ) const override { - info() << "executing IntDataConsumer, consuming " << input << " from " << inputLocation() << endmsg; - } - }; + void operator()( const int& input ) const override { + info() << "executing IntDataConsumer, consuming " << input << " from " << inputLocation() << endmsg; + } + }; - DECLARE_COMPONENT( IntDataConsumer ) + DECLARE_COMPONENT( IntDataConsumer ) - struct IntToFloatData final : Gaudi::Functional::Transformer<float( const int& ), BaseClass_t> { + struct IntToFloatData final : Gaudi::Functional::Transformer<float( const int& ), LegacyBaseClass_t> { - IntToFloatData( const std::string& name, ISvcLocator* svcLoc ) - : Transformer( name, svcLoc, KeyValue( "InputLocation", "/Event/MyInt" ), - KeyValue( "OutputLocation", "/Event/MyFloat" ) ) {} + IntToFloatData( const std::string& name, ISvcLocator* svcLoc ) + : Transformer( name, svcLoc, KeyValue( "InputLocation", "/Event/MyInt" ), + KeyValue( "OutputLocation", "/Event/MyFloat" ) ) {} - float operator()( const int& input ) const override { - info() << "Converting: " << input << " from " << inputLocation() << " and storing it into " << outputLocation() - << endmsg; - return input; - } - }; - - DECLARE_COMPONENT( IntToFloatData ) - - struct IntIntToFloatFloatData final - : Gaudi::Functional::MultiTransformer<std::tuple<float, float>( const int&, const int& ), BaseClass_t> { - IntIntToFloatFloatData( const std::string& name, ISvcLocator* svcLoc ) - : MultiTransformer( - name, svcLoc, - {KeyValue( "InputLocation1", {"/Event/MyInt"} ), KeyValue( "InputLocation2", {"/Event/MyOtherInt"} )}, - {KeyValue( "OutputLocation1", {"/Event/MyMultiFloat1"} ), - KeyValue( "OutputLocation2", {"/Event/MyMultiFloat2"} )} ) {} - - std::tuple<float, float> operator()( const int& input1, const int& input2 ) const override { - info() << "Number of inputs : " << inputLocationSize() << ", number of outputs : " << outputLocationSize() - << endmsg; - info() << "Converting " << input1 << " from " << inputLocation<0>() << " and " << input2 << " from " - << inputLocation<1>() << endmsg; - info() << "Storing results into " << outputLocation<0>() << " and " << outputLocation<1>() << endmsg; - return std::tuple<float, float>{input1, input2}; - } - }; + float operator()( const int& input ) const override { + info() << "Converting: " << input << " from " << inputLocation() << " and storing it into " << outputLocation() + << endmsg; + return input; + } + }; - DECLARE_COMPONENT( IntIntToFloatFloatData ) + DECLARE_COMPONENT( IntToFloatData ) - struct FloatDataConsumer final : Gaudi::Functional::Consumer<void( const float& ), BaseClass_t> { + struct IntIntToFloatFloatData final + : Gaudi::Functional::MultiTransformer<std::tuple<float, float>( const int&, const int& ), LegacyBaseClass_t> { + IntIntToFloatFloatData( const std::string& name, ISvcLocator* svcLoc ) + : MultiTransformer( + name, svcLoc, + {KeyValue( "InputLocation1", {"/Event/MyInt"} ), KeyValue( "InputLocation2", {"/Event/MyOtherInt"} )}, + {KeyValue( "OutputLocation1", {"/Event/MyMultiFloat1"} ), + KeyValue( "OutputLocation2", {"/Event/MyMultiFloat2"} )} ) {} - FloatDataConsumer( const std::string& name, ISvcLocator* svcLoc ) - : Consumer( name, svcLoc, KeyValue( "InputLocation", "/Event/MyFloat" ) ) {} + std::tuple<float, float> operator()( const int& input1, const int& input2 ) const override { + info() << "Number of inputs : " << inputLocationSize() << ", number of outputs : " << outputLocationSize() + << endmsg; + info() << "Converting " << input1 << " from " << inputLocation<0>() << " and " << input2 << " from " + << inputLocation<1>() << endmsg; + info() << "Storing results into " << outputLocation<0>() << " and " << outputLocation<1>() << endmsg; + return std::tuple<float, float>{input1, input2}; + } + }; - void operator()( const float& input ) const override { - info() << "executing FloatDataConsumer: " << input << endmsg; - } - }; + DECLARE_COMPONENT( IntIntToFloatFloatData ) - DECLARE_COMPONENT( FloatDataConsumer ) + struct FloatDataConsumer final : Gaudi::Functional::Consumer<void( const float& ), BaseClass_t> { - struct ContextConsumer final : Gaudi::Functional::Consumer<void( const EventContext& )> { + FloatDataConsumer( const std::string& name, ISvcLocator* svcLoc ) + : Consumer( name, svcLoc, KeyValue( "InputLocation", "/Event/MyFloat" ) ) {} - using Gaudi::Functional::Consumer<void( const EventContext& )>::Consumer; + void operator()( const float& input ) const override { + info() << "executing FloatDataConsumer: " << input << endmsg; + } + }; - void operator()( const EventContext& ctx ) const override { - info() << "executing ContextConsumer, got " << ctx << endmsg; - } - }; + DECLARE_COMPONENT( FloatDataConsumer ) - DECLARE_COMPONENT( ContextConsumer ) + struct ContextConsumer final : Gaudi::Functional::Consumer<void( const EventContext& ), BaseClass_t> { - struct ContextIntConsumer final : Gaudi::Functional::Consumer<void( const EventContext&, const int& )> { + using Gaudi::Functional::Consumer<void( const EventContext& ), BaseClass_t>::Consumer; - ContextIntConsumer( const std::string& name, ISvcLocator* svcLoc ) - : Consumer( name, svcLoc, KeyValue( "InputLocation", "/Event/MyInt" ) ) {} + void operator()( const EventContext& ctx ) const override { + info() << "executing ContextConsumer, got " << ctx << endmsg; + } + }; - void operator()( const EventContext& ctx, const int& i ) const override { - info() << "executing ContextIntConsumer, got context = " << ctx << ", int = " << i << endmsg; - } - }; + DECLARE_COMPONENT( ContextConsumer ) + + struct ContextIntConsumer final : Gaudi::Functional::Consumer<void( const EventContext&, const int& ), BaseClass_t> { - DECLARE_COMPONENT( ContextIntConsumer ) + ContextIntConsumer( const std::string& name, ISvcLocator* svcLoc ) + : Consumer( name, svcLoc, KeyValue( "InputLocation", "/Event/MyInt" ) ) {} + + void operator()( const EventContext& ctx, const int& i ) const override { + info() << "executing ContextIntConsumer, got context = " << ctx << ", int = " << i << endmsg; + } + }; - struct VectorDoubleProducer final : Gaudi::Functional::Producer<std::vector<double>(), BaseClass_t> { + DECLARE_COMPONENT( ContextIntConsumer ) + + struct VectorDoubleProducer final : Gaudi::Functional::Producer<std::vector<double>(), BaseClass_t> { + + VectorDoubleProducer( const std::string& name, ISvcLocator* svcLoc ) + : Producer( name, svcLoc, KeyValue( "OutputLocation", "/Event/MyVectorOfDoubles" ) ) {} + + std::vector<double> operator()() const override { + info() << "storing vector<double> into " << outputLocation() << endmsg; + return {12.34, 56.78, 90.12, 34.56, 78.90}; + } + }; - VectorDoubleProducer( const std::string& name, ISvcLocator* svcLoc ) - : Producer( name, svcLoc, KeyValue( "OutputLocation", "/Event/MyVectorOfDoubles" ) ) {} + DECLARE_COMPONENT( VectorDoubleProducer ) - std::vector<double> operator()() const override { - info() << "storing vector<double> into " << outputLocation() << endmsg; - return {12.34, 56.78, 90.12, 34.56, 78.90}; - } - }; - - DECLARE_COMPONENT( VectorDoubleProducer ) - - struct FrExpTransformer final - : Gaudi::Functional::MultiScalarTransformer< - FrExpTransformer, std::tuple<std::vector<double>, std::vector<int>>( const std::vector<double>& ), - BaseClass_t> { - FrExpTransformer( const std::string& name, ISvcLocator* svcLoc ) - : MultiScalarTransformer( name, svcLoc, KeyValue{"InputDoubles", {"/Event/MyVectorOfDoubles"}}, - {KeyValue{"OutputFractions", {"/Event/MyVectorOfFractions"}}, - KeyValue{"OutputIntegers", {"/Event/MyVectorOfIntegers"}}} ) {} - - using MultiScalarTransformer::operator(); - - std::tuple<double, int> operator()( const double& d ) const { - int i; - double frac = std::frexp( d, &i ); - info() << "Converting " << d << " -> " << frac << ", " << i << endmsg; - return {frac, i}; - } - }; - DECLARE_COMPONENT( FrExpTransformer ) - - struct OptFrExpTransformer final - : Gaudi::Functional::MultiScalarTransformer< - OptFrExpTransformer, std::tuple<std::vector<double>, std::vector<int>>( const std::vector<double>& ), - BaseClass_t> { - OptFrExpTransformer( const std::string& name, ISvcLocator* svcLoc ) - : MultiScalarTransformer( name, svcLoc, KeyValue{"InputDoubles", {"/Event/MyVectorOfDoubles"}}, - {KeyValue{"OutputFractions", {"/Event/OptMyVectorOfFractions"}}, - KeyValue{"OutputIntegers", {"/Event/OptMyVectorOfIntegers"}}} ) {} - - using MultiScalarTransformer::operator(); - - boost::optional<std::tuple<double, int>> operator()( const double& d ) const { - if ( d < 30. ) { - info() << "Skipping " << d << endmsg; - return {}; - } - int i; - double frac = std::frexp( d, &i ); - info() << "Converting " << d << " -> " << frac << ", " << i << endmsg; - return std::make_tuple( frac, i ); - } - }; - DECLARE_COMPONENT( OptFrExpTransformer ) - - struct LdExpTransformer final - : Gaudi::Functional::ScalarTransformer< - LdExpTransformer, std::vector<double>( const std::vector<double>&, const std::vector<int>& ), - BaseClass_t> { - LdExpTransformer( const std::string& name, ISvcLocator* svcLoc ) - : ScalarTransformer( name, svcLoc, - {KeyValue{"InputFractions", {"/Event/MyVectorOfFractions"}}, - KeyValue{"InputIntegers", {"/Event/MyVectorOfIntegers"}}}, - {KeyValue{"OutputDoubles", {"/Event/MyNewVectorOfDoubles"}}} ) {} - - using ScalarTransformer::operator(); - - double operator()( double frac, int i ) const { - double d = std::ldexp( frac, i ); - info() << "Converting " << i << ", " << frac << " -> " << d << endmsg; - return d; + struct FrExpTransformer final + : Gaudi::Functional::MultiScalarTransformer< + FrExpTransformer, std::tuple<std::vector<double>, std::vector<int>>( const std::vector<double>& ), + LegacyBaseClass_t> { + FrExpTransformer( const std::string& name, ISvcLocator* svcLoc ) + : MultiScalarTransformer( name, svcLoc, KeyValue{"InputDoubles", {"/Event/MyVectorOfDoubles"}}, + {KeyValue{"OutputFractions", {"/Event/MyVectorOfFractions"}}, + KeyValue{"OutputIntegers", {"/Event/MyVectorOfIntegers"}}} ) {} + + using MultiScalarTransformer::operator(); + + std::tuple<double, int> operator()( const double& d ) const { + int i; + double frac = std::frexp( d, &i ); + info() << "Converting " << d << " -> " << frac << ", " << i << endmsg; + return {frac, i}; + } + }; + DECLARE_COMPONENT( FrExpTransformer ) + + struct OptFrExpTransformer final + : Gaudi::Functional::MultiScalarTransformer< + OptFrExpTransformer, std::tuple<std::vector<double>, std::vector<int>>( const std::vector<double>& ), + LegacyBaseClass_t> { + OptFrExpTransformer( const std::string& name, ISvcLocator* svcLoc ) + : MultiScalarTransformer( name, svcLoc, KeyValue{"InputDoubles", {"/Event/MyVectorOfDoubles"}}, + {KeyValue{"OutputFractions", {"/Event/OptMyVectorOfFractions"}}, + KeyValue{"OutputIntegers", {"/Event/OptMyVectorOfIntegers"}}} ) {} + + using MultiScalarTransformer::operator(); + + boost::optional<std::tuple<double, int>> operator()( const double& d ) const { + if ( d < 30. ) { + info() << "Skipping " << d << endmsg; + return {}; } - }; - DECLARE_COMPONENT( LdExpTransformer ) - - struct OptLdExpTransformer final - : Gaudi::Functional::ScalarTransformer< - OptLdExpTransformer, std::vector<double>( const std::vector<double>&, const std::vector<int>& ), - BaseClass_t> { - OptLdExpTransformer( const std::string& name, ISvcLocator* svcLoc ) - : ScalarTransformer( name, svcLoc, - {KeyValue{"InputFractions", {"/Event/MyVectorOfFractions"}}, - KeyValue{"InputIntegers", {"/Event/MyVectorOfIntegers"}}}, - {KeyValue{"OutputDoubles", {"/Event/MyOptVectorOfDoubles"}}} ) {} - - using ScalarTransformer::operator(); - - boost::optional<double> operator()( const double& frac, const int& i ) const { - double d = std::ldexp( frac, i ); - if ( i > 6 ) { - info() << "Skipping " << d << endmsg; - return {}; - } - info() << "Converting " << i << ", " << frac << " -> " << d << endmsg; - return d; + int i; + double frac = std::frexp( d, &i ); + info() << "Converting " << d << " -> " << frac << ", " << i << endmsg; + return std::make_tuple( frac, i ); + } + }; + DECLARE_COMPONENT( OptFrExpTransformer ) + + struct LdExpTransformer final + : Gaudi::Functional::ScalarTransformer<LdExpTransformer, + std::vector<double>( const std::vector<double>&, const std::vector<int>& ), + LegacyBaseClass_t> { + LdExpTransformer( const std::string& name, ISvcLocator* svcLoc ) + : ScalarTransformer( name, svcLoc, + {KeyValue{"InputFractions", {"/Event/MyVectorOfFractions"}}, + KeyValue{"InputIntegers", {"/Event/MyVectorOfIntegers"}}}, + {KeyValue{"OutputDoubles", {"/Event/MyNewVectorOfDoubles"}}} ) {} + + using ScalarTransformer::operator(); + + double operator()( double frac, int i ) const { + double d = std::ldexp( frac, i ); + info() << "Converting " << i << ", " << frac << " -> " << d << endmsg; + return d; + } + }; + DECLARE_COMPONENT( LdExpTransformer ) + + struct OptLdExpTransformer final + : Gaudi::Functional::ScalarTransformer<OptLdExpTransformer, + std::vector<double>( const std::vector<double>&, const std::vector<int>& ), + LegacyBaseClass_t> { + OptLdExpTransformer( const std::string& name, ISvcLocator* svcLoc ) + : ScalarTransformer( name, svcLoc, + {KeyValue{"InputFractions", {"/Event/MyVectorOfFractions"}}, + KeyValue{"InputIntegers", {"/Event/MyVectorOfIntegers"}}}, + {KeyValue{"OutputDoubles", {"/Event/MyOptVectorOfDoubles"}}} ) {} + + using ScalarTransformer::operator(); + + boost::optional<double> operator()( const double& frac, const int& i ) const { + double d = std::ldexp( frac, i ); + if ( i > 6 ) { + info() << "Skipping " << d << endmsg; + return {}; } - }; - DECLARE_COMPONENT( OptLdExpTransformer ) + info() << "Converting " << i << ", " << frac << " -> " << d << endmsg; + return d; + } + }; + DECLARE_COMPONENT( OptLdExpTransformer ) - struct VoidConsumer final : Gaudi::Functional::Consumer<void()> { + struct VoidConsumer final : Gaudi::Functional::Consumer<void(), BaseClass_t> { - using Consumer::Consumer; + using Consumer::Consumer; - void operator()() const override { info() << "executing VoidConsumer" << endmsg; } - }; + void operator()() const override { info() << "executing VoidConsumer" << endmsg; } + }; - DECLARE_COMPONENT( VoidConsumer ) - } // namespace Examples -} // namespace Gaudi + DECLARE_COMPONENT( VoidConsumer ) +} // namespace Gaudi::Examples