Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-06-30 08:36:13

0001 /***********************************************************************************\
0002 * (c) Copyright 1998-2023 CERN for the benefit of the LHCb and ATLAS collaborations *
0003 *                                                                                   *
0004 * This software is distributed under the terms of the Apache version 2 licence,     *
0005 * copied verbatim in the file "LICENSE".                                            *
0006 *                                                                                   *
0007 * In applying this licence, CERN does not waive the privileges and immunities       *
0008 * granted to it by virtue of its status as an Intergovernmental Organization        *
0009 * or submit itself to any jurisdiction.                                             *
0010 \***********************************************************************************/
0011 #pragma once
0012 
0013 #include <Gaudi/Algorithm.h>
0014 #include <GaudiKernel/Algorithm.h>
0015 #include <GaudiKernel/DataObjectHandle.h>
0016 #include <GaudiKernel/GaudiException.h>
0017 #include <GaudiKernel/IBinder.h>
0018 #include <GaudiKernel/ThreadLocalContext.h>
0019 #include <GaudiKernel/detected.h>
0020 #include <cassert>
0021 #include <range/v3/version.hpp>
0022 #include <range/v3/view/const.hpp>
0023 #include <range/v3/view/zip.hpp>
0024 #include <sstream>
0025 #include <stdexcept>
0026 #include <type_traits>
0027 
0028 // upstream has renamed namespace ranges::view ranges::views
0029 #if RANGE_V3_VERSION < 900
0030 namespace ranges::views {
0031   using namespace ranges::view;
0032 }
0033 #endif
0034 
0035 #if defined( __clang__ ) && ( __clang_major__ < 11 ) || defined( __APPLE__ ) && ( __clang_major__ < 12 )
0036 #  define GF_SUPPRESS_SPURIOUS_CLANG_WARNING_BEGIN                                                                     \
0037     _Pragma( "clang diagnostic push" ) _Pragma( "clang diagnostic ignored \"-Wunused-lambda-capture\"" )
0038 #  define GF_SUPPRESS_SPURIOUS_CLANG_WARNING_END _Pragma( "clang diagnostic pop" )
0039 #else
0040 #  define GF_SUPPRESS_SPURIOUS_CLANG_WARNING_BEGIN
0041 #  define GF_SUPPRESS_SPURIOUS_CLANG_WARNING_END
0042 #endif
0043 
0044 // temporary hack to help in transition to updated constructor
0045 // allows to write code which is forward and backwards compatible
0046 #define GAUDI_FUNCTIONAL_CONSTRUCTOR_USES_TUPLE
0047 
0048 namespace Gaudi::Functional::details {
0049 
0050   // CRJ : Stuff for zipping
0051   namespace zip {
0052 
0053     /// Print the parameter
0054     template <typename OS, typename Arg>
0055     void printSizes( OS& out, Arg&& arg ) {
0056       out << "SizeOf'" << System::typeinfoName( typeid( Arg ) ) << "'=" << std::forward<Arg>( arg ).size();
0057     }
0058 
0059     /// Print the parameters
0060     template <typename OS, typename Arg, typename... Args>
0061     void printSizes( OS& out, Arg&& arg, Args&&... args ) {
0062       printSizes( out, arg );
0063       out << ", ";
0064       printSizes( out, args... );
0065     }
0066 
0067     /// Resolve case there is only one container in the range
0068     template <typename A>
0069     inline bool check_sizes( const A& ) noexcept {
0070       return true;
0071     }
0072 
0073     /// Compare sizes of two containers
0074     template <typename A, typename B>
0075     inline bool check_sizes( const A& a, const B& b ) noexcept {
0076       return a.size() == b.size();
0077     }
0078 
0079     /// Compare sizes of 3 or more containers
0080     template <typename A, typename B, typename... C>
0081     inline bool check_sizes( const A& a, const B& b, const C&... c ) noexcept {
0082       return ( check_sizes( a, b ) && check_sizes( b, c... ) );
0083     }
0084 
0085     /// Verify the data container sizes have the same sizes
0086     template <typename... Args>
0087     inline decltype( auto ) verifySizes( Args&... args ) {
0088       if ( !check_sizes( args... ) ) {
0089         std::ostringstream mess;
0090         mess << "Zipped containers have different sizes : ";
0091         printSizes( mess, args... );
0092         throw GaudiException( mess.str(), "Gaudi::Functional::details::zip::verifySizes", StatusCode::FAILURE );
0093       }
0094     }
0095 
0096     /// Zips multiple containers together to form a single range
0097     template <typename... Args>
0098     inline decltype( auto ) range( Args&&... args ) {
0099 #ifndef NDEBUG
0100       verifySizes( args... );
0101 #endif
0102       return ranges::views::zip( std::forward<Args>( args )... );
0103     }
0104 
0105     /// Zips multiple containers together to form a single const range
0106     template <typename... Args>
0107     inline decltype( auto ) const_range( Args&&... args ) {
0108 #ifndef NDEBUG
0109       verifySizes( args... );
0110 #endif
0111       return ranges::views::const_( ranges::views::zip( std::forward<Args>( args )... ) );
0112     }
0113   } // namespace zip
0114 
0115   inline std::vector<DataObjID> to_DataObjID( const std::vector<std::string>& in ) {
0116     std::vector<DataObjID> out;
0117     out.reserve( in.size() );
0118     std::transform( in.begin(), in.end(), std::back_inserter( out ),
0119                     []( const std::string& i ) { return DataObjID{ i }; } );
0120     return out;
0121   }
0122 
0123   /////////////////////////////////////////
0124   namespace details2 {
0125     template <typename T>
0126     using is_optional_ = decltype( std::declval<T>().has_value(), std::declval<T>().value() );
0127 
0128     template <typename T>
0129     using value_type_of_t = typename T::value_type;
0130 
0131   } // namespace details2
0132   template <typename Arg>
0133   constexpr bool is_optional_v = Gaudi::cpp17::is_detected_v<details2::is_optional_, Arg>;
0134 
0135   template <typename Arg>
0136   using require_is_optional = std::enable_if_t<is_optional_v<Arg>>;
0137 
0138   template <typename Arg>
0139   using require_is_not_optional = std::enable_if_t<!is_optional_v<Arg>>;
0140 
0141   template <typename T>
0142   using remove_optional_t =
0143       std::conditional_t<is_optional_v<T>, Gaudi::cpp17::detected_t<details2::value_type_of_t, T>, T>;
0144 
0145   constexpr struct invoke_optionally_t {
0146     template <typename F, typename Arg, typename = require_is_not_optional<Arg>>
0147     decltype( auto ) operator()( F&& f, Arg&& arg ) const {
0148       return std::invoke( std::forward<F>( f ), std::forward<Arg>( arg ) );
0149     }
0150     template <typename F, typename Arg, typename = require_is_optional<Arg>>
0151     void operator()( F&& f, Arg&& arg ) const {
0152       if ( arg ) std::invoke( std::forward<F>( f ), *std::forward<Arg>( arg ) );
0153     }
0154   } invoke_optionally{};
0155   /////////////////////////////////////////
0156 
0157   template <typename Value, std::size_t... I>
0158   auto get_values_helper( std::index_sequence<I...> ) {
0159     return std::make_tuple( ( (void)I, Value{} )... );
0160   }
0161 
0162   template <typename Value, auto N>
0163   using RepeatValues_ = decltype( get_values_helper<Value>( std::make_index_sequence<N>() ) );
0164 
0165   /////////////////////////////////////////
0166 
0167   template <typename Out1, typename Out2,
0168             typename = std::enable_if_t<std::is_constructible_v<Out1, Out2> && std::is_base_of_v<DataObject, Out1>>>
0169   auto put( const DataObjectHandle<Out1>& out_handle, Out2&& out ) {
0170     return out_handle.put( std::make_unique<Out1>( std::forward<Out2>( out ) ) );
0171   }
0172 
0173   template <typename Out1, typename Out2, typename = std::enable_if_t<std::is_constructible_v<Out1, Out2>>>
0174   auto put( const DataObjectHandle<AnyDataWrapper<Out1>>& out_handle, Out2&& out ) {
0175     return out_handle.put( std::forward<Out2>( out ) );
0176   }
0177 
0178   // optional put
0179   template <typename OutHandle, typename OptOut, typename = require_is_optional<OptOut>>
0180   void put( const OutHandle& out_handle, OptOut&& out ) {
0181     if ( out ) put( out_handle, *std::forward<OptOut>( out ) );
0182   }
0183 
0184   /////////////////////////////////////////
0185   // adapt to differences between eg. std::vector (which has push_back) and KeyedContainer (which has insert)
0186   // adapt to getting a T, and a container wanting T* by doing new T{ std::move(out) }
0187   // adapt to getting a optional<T>
0188 
0189   constexpr struct insert_t {
0190     // for Container<T*>, return T
0191     template <typename Container>
0192     using c_remove_ptr_t = std::remove_pointer_t<typename Container::value_type>;
0193 
0194     template <typename Container, typename Value>
0195     auto operator()( Container& c, Value&& v ) const -> decltype( c.push_back( v ) ) {
0196       return c.push_back( std::forward<Value>( v ) );
0197     }
0198 
0199     template <typename Container, typename Value>
0200     auto operator()( Container& c, Value&& v ) const -> decltype( c.insert( v ) ) {
0201       return c.insert( std::forward<Value>( v ) );
0202     }
0203 
0204     // Container<T*> with T&& as argument
0205     template <typename Container, typename Value,
0206               typename = std::enable_if_t<std::is_pointer_v<typename Container::value_type>>,
0207               typename = std::enable_if_t<std::is_convertible_v<Value, c_remove_ptr_t<Container>>>>
0208     auto operator()( Container& c, Value&& v ) const {
0209       return operator()( c, new c_remove_ptr_t<Container>{ std::forward<Value>( v ) } );
0210     }
0211 
0212   } insert{};
0213 
0214   /////////////////////////////////////////
0215 
0216   constexpr struct deref_t {
0217     template <typename In, typename = std::enable_if_t<!std::is_pointer_v<In>>>
0218     const In& operator()( const In& in ) const {
0219       return in;
0220     }
0221 
0222     template <typename In, typename = std::enable_if_t<!std::is_pointer_v<std::decay_t<In>>>>
0223     In operator()( In&& in ) const {
0224       return std::forward<In>( in );
0225     }
0226 
0227     template <typename In>
0228     const In& operator()( const In* in ) const {
0229       assert( in != nullptr );
0230       return *in;
0231     }
0232   } deref{};
0233 
0234   /////////////////////////////////////////
0235   // if Container is a pointer, then we're optional items
0236   namespace details2 {
0237     template <typename T>
0238     struct is_gaudi_range : std::false_type {};
0239 
0240     template <typename T, typename IT>
0241     struct is_gaudi_range<Gaudi::Range_<T, IT>> : std::true_type {};
0242 
0243     template <typename T, typename IT>
0244     struct is_gaudi_range<Gaudi::NamedRange_<T, IT>> : std::true_type {};
0245 
0246     template <typename T, typename IT>
0247     struct is_gaudi_range<std::optional<Gaudi::NamedRange_<T, IT>>> : std::true_type {};
0248 
0249     template <typename T>
0250     constexpr static bool is_gaudi_range_v = is_gaudi_range<T>::value;
0251 
0252     template <typename Container, typename Value>
0253     void push_back( Container& c, const Value& v, std::true_type ) {
0254       c.push_back( v );
0255     }
0256     template <typename Container, typename Value>
0257     void push_back( Container& c, const Value& v, std::false_type ) {
0258       c.push_back( &v );
0259     }
0260 
0261     template <typename In>
0262     struct get_from_handle {
0263       template <template <typename> class Handle, typename I, typename = std::enable_if_t<std::is_convertible_v<I, In>>>
0264       auto operator()( const Handle<I>& h ) -> const In& {
0265         return *h.get();
0266       }
0267       template <template <typename> class Handle, typename I, typename IT>
0268       auto operator()( const Handle<Gaudi::Range_<I, IT>>& h ) -> const In {
0269         return h.get();
0270       }
0271       template <template <typename> class Handle, typename I, typename IT>
0272       auto operator()( const Handle<Gaudi::NamedRange_<I, IT>>& h ) -> const In {
0273         return h.get();
0274       }
0275       template <template <typename> class Handle, typename I, typename IT>
0276       auto operator()( const Handle<std::optional<Gaudi::NamedRange_<I, IT>>>& h ) -> const In {
0277         return h.get();
0278       }
0279       template <template <typename> class Handle, typename I,
0280                 typename = std::enable_if_t<std::is_convertible_v<I*, In>>>
0281       auto operator()( const Handle<I>& h ) -> const In {
0282         return h.getIfExists();
0283       } // In is-a pointer
0284     };
0285 
0286     template <typename T>
0287     T* deref_if( T* const t, std::false_type ) {
0288       return t;
0289     }
0290     template <typename T>
0291     T& deref_if( T* const t, std::true_type ) {
0292       return *t;
0293     }
0294   } // namespace details2
0295 
0296   template <typename Container>
0297   class vector_of_const_ {
0298     static constexpr bool is_pointer = std::is_pointer_v<Container>;
0299     static constexpr bool is_range   = details2::is_gaudi_range_v<Container>;
0300     using val_t                      = std::add_const_t<std::remove_pointer_t<Container>>;
0301     using ptr_t                      = std::add_pointer_t<val_t>;
0302     using ref_t                      = std::add_lvalue_reference_t<val_t>;
0303     using ContainerVector            = std::vector<std::conditional_t<is_range, std::remove_const_t<val_t>, ptr_t>>;
0304     ContainerVector m_containers;
0305 
0306   public:
0307     using value_type = std::conditional_t<is_pointer, ptr_t, val_t>;
0308     using size_type  = typename ContainerVector::size_type;
0309     class iterator {
0310       using it_t = typename ContainerVector::const_iterator;
0311       it_t m_i;
0312       friend class vector_of_const_;
0313       iterator( it_t iter ) : m_i( iter ) {}
0314       using ret_t = std::conditional_t<is_pointer, ptr_t, ref_t>;
0315 
0316     public:
0317       using iterator_category = typename it_t::iterator_category;
0318       using value_type        = typename it_t::iterator_category;
0319       using reference         = typename it_t::reference;
0320       using pointer           = typename it_t::pointer;
0321       using difference_type   = typename it_t::difference_type;
0322 
0323       friend bool operator!=( const iterator& lhs, const iterator& rhs ) { return lhs.m_i != rhs.m_i; }
0324       friend bool operator==( const iterator& lhs, const iterator& rhs ) { return lhs.m_i == rhs.m_i; }
0325       friend auto operator-( const iterator& lhs, const iterator& rhs ) { return lhs.m_i - rhs.m_i; }
0326       ret_t       operator*() const {
0327         if constexpr ( is_range ) {
0328           return *m_i;
0329         } else {
0330           return details2::deref_if( *m_i, std::bool_constant<!is_pointer>{} );
0331         }
0332       }
0333       iterator& operator++() {
0334         ++m_i;
0335         return *this;
0336       }
0337       iterator& operator--() {
0338         --m_i;
0339         return *this;
0340       }
0341       bool is_null() const { return !*m_i; }
0342       explicit operator bool() const { return !is_null(); }
0343     };
0344     vector_of_const_() = default;
0345     void reserve( size_type size ) { m_containers.reserve( size ); }
0346     template <typename T> // , typename = std::is_convertible<T,std::conditional_t<is_pointer,ptr_t,val_t>>
0347     void push_back( T&& container ) {
0348       details2::push_back( m_containers, std::forward<T>( container ),
0349                            std::bool_constant < is_pointer || is_range > {} );
0350     } // note: does not copy its argument, so we're not really a container...
0351     iterator  begin() const { return m_containers.begin(); }
0352     iterator  end() const { return m_containers.end(); }
0353     size_type size() const { return m_containers.size(); }
0354 
0355     template <typename X = Container>
0356     std::enable_if_t<!std::is_pointer_v<X>, ref_t> front() const {
0357       return *m_containers.front();
0358     }
0359     template <typename X = Container>
0360     std::enable_if_t<std::is_pointer_v<X>, ptr_t> front() const {
0361       return m_containers.front();
0362     }
0363 
0364     template <typename X = Container>
0365     std::enable_if_t<!std::is_pointer_v<X>, ref_t> back() const {
0366       return *m_containers.back();
0367     }
0368     template <typename X = Container>
0369     std::enable_if_t<std::is_pointer_v<X>, ptr_t> back() const {
0370       return m_containers.back();
0371     }
0372 
0373     template <typename X = Container>
0374     std::enable_if_t<!std::is_pointer_v<X>, ref_t> operator[]( size_type i ) const {
0375       return *m_containers[i];
0376     }
0377     template <typename X = Container>
0378     std::enable_if_t<std::is_pointer_v<X>, ptr_t> operator[]( size_type i ) const {
0379       return m_containers[i];
0380     }
0381 
0382     template <typename X = Container>
0383     std::enable_if_t<!std::is_pointer_v<X>, ref_t> at( size_type i ) const {
0384       return *m_containers[i];
0385     }
0386     template <typename X = Container>
0387     std::enable_if_t<std::is_pointer_v<X>, ptr_t> at( size_type i ) const {
0388       return m_containers[i];
0389     }
0390 
0391     bool is_null( size_type i ) const { return !m_containers[i]; }
0392   };
0393 
0394   /////////////////////////////////////////
0395   namespace detail2 { // utilities for detected_or_t{,_} usage
0396     template <typename Tr>
0397     using BaseClass_t = typename Tr::BaseClass;
0398     template <typename Tr, typename T>
0399     using OutputHandle_t = typename Tr::template OutputHandle<T>;
0400     template <typename Tr, typename T>
0401     using InputHandle_t = typename Tr::template InputHandle<T>;
0402 
0403     template <typename T>
0404     constexpr auto is_tool_v = std::is_base_of_v<IAlgTool, std::decay_t<T>>;
0405 
0406     template <typename T>
0407     using ToolHandle_t = ToolHandle<Gaudi::Interface::Bind::IBinder<std::decay_t<T>>>;
0408 
0409     template <typename T>
0410     using DefaultInputHandle = std::conditional_t<is_tool_v<T>, ToolHandle_t<T>, DataObjectReadHandle<T>>;
0411   } // namespace detail2
0412 
0413   // check whether Traits::BaseClass is a valid type,
0414   // if so, define BaseClass_t<Traits> as being Traits::BaseClass
0415   // else   define                     as being Gaudi::Algorithm
0416   template <typename Tr, typename Base = Gaudi::Algorithm>
0417   using BaseClass_t = Gaudi::cpp17::detected_or_t<Base, detail2::BaseClass_t, Tr>;
0418 
0419   // check whether Traits::{Input,Output}Handle<T> is a valid type,
0420   // if so, define {Input,Output}Handle_t<Traits,T> as being Traits::{Input,Output}Handle<T>
0421   // else   define                                  as being DataObject{Read,,Write}Handle<T>
0422 
0423   template <typename Tr, typename T>
0424   using OutputHandle_t = Gaudi::cpp17::detected_or_t<DataObjectWriteHandle<T>, detail2::OutputHandle_t, Tr, T>;
0425   template <typename Tr, typename T>
0426   using InputHandle_t = Gaudi::cpp17::detected_or_t<detail2::DefaultInputHandle<T>, detail2::InputHandle_t, Tr, T>;
0427 
0428   template <typename Traits>
0429   inline constexpr bool isLegacy =
0430       std::is_base_of_v<Gaudi::details::LegacyAlgorithmAdapter, details::BaseClass_t<Traits>>;
0431 
0432   /////////
0433 #define GAUDI_FUNCTIONAL_MAKE_VECTOR_OF_HANDLES_USES_DATAOBJID
0434 
0435   template <typename Handles>
0436   Handles make_vector_of_handles( IDataHandleHolder* owner, const std::vector<DataObjID>& init ) {
0437     Handles handles;
0438     handles.reserve( init.size() );
0439     std::transform( init.begin(), init.end(), std::back_inserter( handles ),
0440                     [&]( const auto& loc ) -> typename Handles::value_type {
0441                       return { loc, owner };
0442                     } );
0443     return handles;
0444   }
0445 
0446   template <typename Handle, typename Algo>
0447   auto get( const Handle& handle, const Algo&, const EventContext& )
0448       -> decltype( details::deref( handle.get() ) ) // make it SFINAE friendly...
0449   {
0450     return details::deref( handle.get() );
0451   }
0452 
0453   template <typename IFace, typename Algo>
0454   auto get( const ToolHandle<Gaudi::Interface::Bind::IBinder<IFace>>& handle, const Algo&, const EventContext& ctx ) {
0455     return handle.bind( ctx );
0456   }
0457 
0458   template <typename Handle>
0459   auto getKey( const Handle& h ) -> decltype( h.objKey() ) {
0460     return h.objKey();
0461   }
0462 
0463   ///////////////////////
0464   // given a pack, return a corresponding tuple
0465   template <typename... In>
0466   struct filter_evtcontext_t {
0467     using type = std::tuple<In...>;
0468 
0469     static_assert( !std::disjunction_v<std::is_same<EventContext, In>...>,
0470                    "EventContext can only appear as first argument" );
0471 
0472     template <typename Algorithm, typename Handles>
0473     static auto apply( const Algorithm& algo, Handles& handles ) {
0474       return std::apply(
0475           [&]( const auto&... handle ) { return algo( get( handle, algo, Gaudi::Hive::currentContext() )... ); },
0476           handles );
0477     }
0478     template <typename Algorithm, typename Handles>
0479     static auto apply( const Algorithm& algo, const EventContext& ctx, Handles& handles ) {
0480       return std::apply( [&]( const auto&... handle ) { return algo( get( handle, algo, ctx )... ); }, handles );
0481     }
0482   };
0483 
0484   // except when it starts with EventContext, then drop it
0485   template <typename... In>
0486   struct filter_evtcontext_t<EventContext, In...> {
0487     using type = std::tuple<In...>;
0488 
0489     static_assert( !std::disjunction_v<std::is_same<EventContext, In>...>,
0490                    "EventContext can only appear as first argument" );
0491 
0492     template <typename Algorithm, typename Handles>
0493     static auto apply( const Algorithm& algo, const EventContext& ctx, Handles& handles ) {
0494       return std::apply( [&]( const auto&... handle ) { return algo( ctx, get( handle, algo, ctx )... ); }, handles );
0495     }
0496 
0497     template <typename Algorithm, typename Handles>
0498     static auto apply( const Algorithm& algo, Handles& handles ) {
0499       return apply( algo, Gaudi::Hive::currentContext(), handles );
0500     }
0501   };
0502 
0503   template <typename... In>
0504   using filter_evtcontext = typename filter_evtcontext_t<In...>::type;
0505 
0506   template <typename OutputSpec, typename InputSpec, typename Traits_>
0507   class DataHandleMixin;
0508 
0509   template <typename Out, typename In, typename Tr>
0510   void updateHandleLocation( DataHandleMixin<Out, In, Tr>& parent, const std::string& prop,
0511                              const std::string& newLoc ) {
0512     auto sc = parent.setProperty( prop, newLoc );
0513     if ( sc.isFailure() ) throw GaudiException( "Could not set Property", prop + " -> " + newLoc, sc );
0514   }
0515 
0516   template <typename Out, typename In, typename Tr>
0517   void updateHandleLocations( DataHandleMixin<Out, In, Tr>& parent, const std::string& prop,
0518                               const std::vector<std::string>& newLocs ) {
0519     std::ostringstream ss;
0520     GaudiUtils::details::ostream_joiner( ss << '[', newLocs, ", ", []( std::ostream& os, const auto& i ) -> auto& {
0521       return os << "'" << i << "'";
0522     } ) << ']';
0523     auto sc = parent.setProperty( prop, ss.str() );
0524     if ( sc.isFailure() ) throw GaudiException( "Could not set Property", prop + " -> " + ss.str(), sc );
0525   }
0526 
0527   template <typename... Out, typename... In, typename Traits_>
0528   class DataHandleMixin<std::tuple<Out...>, std::tuple<In...>, Traits_> : public BaseClass_t<Traits_> {
0529     static_assert( std::is_base_of_v<Algorithm, BaseClass_t<Traits_>>, "BaseClass must inherit from Algorithm" );
0530 
0531     template <typename IArgs, typename OArgs, std::size_t... I, std::size_t... J>
0532     DataHandleMixin( std::string name, ISvcLocator* pSvcLocator, const IArgs& inputs, std::index_sequence<I...>,
0533                      const OArgs& outputs, std::index_sequence<J...> )
0534         : BaseClass_t<Traits_>( std::move( name ), pSvcLocator )
0535         , m_inputs( std::tuple_cat( std::forward_as_tuple( this ), std::get<I>( inputs ) )... )
0536         , m_outputs( std::tuple_cat( std::forward_as_tuple( this ), std::get<J>( outputs ) )... ) {
0537       // make sure this algorithm is seen as reentrant by Gaudi
0538       this->setProperty( "Cardinality", 0 ).ignore();
0539     }
0540 
0541   public:
0542     constexpr static std::size_t N_in  = sizeof...( In );
0543     constexpr static std::size_t N_out = sizeof...( Out );
0544 
0545     using KeyValue  = std::pair<std::string, std::string>;
0546     using KeyValues = std::pair<std::string, std::vector<std::string>>;
0547 
0548     // generic constructor:  N -> M
0549     DataHandleMixin( std::string name, ISvcLocator* pSvcLocator, RepeatValues_<KeyValue, N_in> const& inputs,
0550                      RepeatValues_<KeyValue, N_out> const& outputs )
0551         : DataHandleMixin( std::move( name ), pSvcLocator, inputs, std::index_sequence_for<In...>{}, outputs,
0552                            std::index_sequence_for<Out...>{} ) {}
0553 
0554     // special cases: forward to the generic case...
0555     // 1 -> 1
0556     DataHandleMixin( std::string name, ISvcLocator* locator, const KeyValue& input, const KeyValue& output )
0557         : DataHandleMixin( std::move( name ), locator, std::forward_as_tuple( input ),
0558                            std::forward_as_tuple( output ) ) {}
0559     // 1 -> N
0560     DataHandleMixin( std::string name, ISvcLocator* locator, const KeyValue& input,
0561                      RepeatValues_<KeyValue, N_out> const& outputs )
0562         : DataHandleMixin( std::move( name ), locator, std::forward_as_tuple( input ), outputs ) {}
0563     // N -> 1
0564     DataHandleMixin( std::string name, ISvcLocator* locator, RepeatValues_<KeyValue, N_in> const& inputs,
0565                      const KeyValue& output )
0566         : DataHandleMixin( std::move( name ), locator, inputs, std::forward_as_tuple( output ) ) {}
0567 
0568     template <std::size_t N = 0>
0569     decltype( auto ) inputLocation() const {
0570       return getKey( std::get<N>( m_inputs ) );
0571     }
0572     template <typename T>
0573     decltype( auto ) inputLocation() const {
0574       return getKey( std::get<details::InputHandle_t<Traits_, std::decay_t<T>>>( m_inputs ) );
0575     }
0576     constexpr unsigned int inputLocationSize() const { return N_in; }
0577 
0578     template <std::size_t N = 0>
0579     decltype( auto ) outputLocation() const {
0580       return getKey( std::get<N>( m_outputs ) );
0581     }
0582     template <typename T>
0583     decltype( auto ) outputLocation() const {
0584       return getKey( std::get<details::OutputHandle_t<Traits_, std::decay_t<T>>>( m_outputs ) );
0585     }
0586     constexpr unsigned int outputLocationSize() const { return N_out; }
0587 
0588   protected:
0589     bool isReEntrant() const override { return true; }
0590 
0591     std::tuple<details::InputHandle_t<Traits_, In>...>   m_inputs;
0592     std::tuple<details::OutputHandle_t<Traits_, Out>...> m_outputs;
0593   };
0594 
0595   template <typename Traits_>
0596   class DataHandleMixin<std::tuple<>, std::tuple<>, Traits_> : public BaseClass_t<Traits_> {
0597     static_assert( std::is_base_of_v<Algorithm, BaseClass_t<Traits_>>, "BaseClass must inherit from Algorithm" );
0598 
0599   public:
0600     using KeyValue  = std::pair<std::string, std::string>;
0601     using KeyValues = std::pair<std::string, std::vector<std::string>>;
0602     DataHandleMixin( std::string name, ISvcLocator* pSvcLocator, std::tuple<> = {}, std::tuple<> = {} )
0603         : BaseClass_t<Traits_>( std::move( name ), pSvcLocator ) {
0604       // make sure this algorithm is seen as reentrant by Gaudi
0605       this->setProperty( "Cardinality", 0 ).ignore();
0606     }
0607 
0608   protected:
0609     bool isReEntrant() const override { return true; }
0610 
0611     std::tuple<> m_inputs;
0612   };
0613 
0614   template <typename... In, typename Traits_>
0615   class DataHandleMixin<std::tuple<>, std::tuple<In...>, Traits_> : public BaseClass_t<Traits_> {
0616     static_assert( std::is_base_of_v<Algorithm, BaseClass_t<Traits_>>, "BaseClass must inherit from Algorithm" );
0617 
0618     template <typename IArgs, std::size_t... I>
0619     DataHandleMixin( std::string name, ISvcLocator* pSvcLocator, const IArgs& inputs, std::index_sequence<I...> )
0620         : BaseClass_t<Traits_>( std::move( name ), pSvcLocator )
0621         , m_inputs( std::tuple_cat( std::forward_as_tuple( this ), std::get<I>( inputs ) )... ) {
0622       // make sure this algorithm is seen as reentrant by Gaudi
0623       this->setProperty( "Cardinality", 0 ).ignore();
0624     }
0625 
0626   public:
0627     using KeyValue                    = std::pair<std::string, std::string>;
0628     using KeyValues                   = std::pair<std::string, std::vector<std::string>>;
0629     constexpr static std::size_t N_in = sizeof...( In );
0630 
0631     // generic constructor:  N -> 0
0632     DataHandleMixin( std::string name, ISvcLocator* pSvcLocator, RepeatValues_<KeyValue, N_in> const& inputs )
0633         : DataHandleMixin( std::move( name ), pSvcLocator, inputs, std::index_sequence_for<In...>{} ) {}
0634 
0635     // special cases: forward to the generic case...
0636     // 1 -> 0
0637     DataHandleMixin( std::string name, ISvcLocator* locator, const KeyValue& input )
0638         : DataHandleMixin( std::move( name ), locator, std::forward_as_tuple( input ) ) {}
0639 
0640     template <std::size_t N = 0>
0641     decltype( auto ) inputLocation() const {
0642       return getKey( std::get<N>( m_inputs ) );
0643     }
0644     template <typename T>
0645     decltype( auto ) inputLocation() const {
0646       return getKey( std::get<details::InputHandle_t<Traits_, std::decay_t<T>>>( m_inputs ) );
0647     }
0648     constexpr unsigned int inputLocationSize() const { return N_in; }
0649 
0650   protected:
0651     bool isReEntrant() const override { return true; }
0652 
0653     std::tuple<details::InputHandle_t<Traits_, In>...> m_inputs;
0654   };
0655 
0656   template <typename Traits_>
0657   class DataHandleMixin<std::tuple<void>, std::tuple<>, Traits_>
0658       : public DataHandleMixin<std::tuple<>, std::tuple<>, Traits_> {
0659   public:
0660     using DataHandleMixin<std::tuple<>, std::tuple<>, Traits_>::DataHandleMixin;
0661   };
0662 
0663   template <typename... Out, typename Traits_>
0664   class DataHandleMixin<std::tuple<Out...>, std::tuple<>, Traits_> : public BaseClass_t<Traits_> {
0665     static_assert( std::is_base_of_v<Algorithm, BaseClass_t<Traits_>>, "BaseClass must inherit from Algorithm" );
0666 
0667     template <typename OArgs, std::size_t... J>
0668     DataHandleMixin( std::string name, ISvcLocator* pSvcLocator, const OArgs& outputs, std::index_sequence<J...> )
0669         : BaseClass_t<Traits_>( std::move( name ), pSvcLocator )
0670         , m_outputs( std::tuple_cat( std::forward_as_tuple( this ), std::get<J>( outputs ) )... ) {
0671       // make sure this algorithm is seen as reentrant by Gaudi
0672       this->setProperty( "Cardinality", 0 ).ignore();
0673     }
0674 
0675   public:
0676     constexpr static std::size_t N_out = sizeof...( Out );
0677     using KeyValue                     = std::pair<std::string, std::string>;
0678     using KeyValues                    = std::pair<std::string, std::vector<std::string>>;
0679 
0680     // generic constructor:  0 -> N
0681     DataHandleMixin( std::string name, ISvcLocator* pSvcLocator, RepeatValues_<KeyValue, N_out> const& outputs )
0682         : DataHandleMixin( std::move( name ), pSvcLocator, outputs, std::index_sequence_for<Out...>{} ) {}
0683 
0684     // 0 -> 1
0685     DataHandleMixin( std::string name, ISvcLocator* locator, const KeyValue& output )
0686         : DataHandleMixin( std::move( name ), locator, std::forward_as_tuple( output ) ) {}
0687 
0688     template <std::size_t N = 0>
0689     decltype( auto ) outputLocation() const {
0690       return getKey( std::get<N>( m_outputs ) );
0691     }
0692     constexpr unsigned int outputLocationSize() const { return N_out; }
0693 
0694   protected:
0695     bool isReEntrant() const override { return true; }
0696 
0697     std::tuple<details::OutputHandle_t<Traits_, Out>...> m_outputs;
0698   };
0699 
0700   /////////////////
0701   template <typename Fun, typename Container, typename... Args>
0702   constexpr void applyPostProcessing( const Fun&, Container&, Args... ) {
0703     static_assert( sizeof...( Args ) == 0, "Args should not be used!" );
0704   }
0705 
0706   template <typename Fun, typename Container>
0707   auto applyPostProcessing( const Fun& fun, Container& c ) -> decltype( fun.postprocess( c ), void() ) {
0708     fun.postprocess( c );
0709   }
0710 
0711 } // namespace Gaudi::Functional::details