Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-13 08:56:39

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