The Gaudi Framework  v30r2 (9eca68f7)
FunctionalDetails.h
Go to the documentation of this file.
1 #ifndef FUNCTIONAL_DETAILS_H
2 #define FUNCTIONAL_DETAILS_H
3 
4 #include <cassert>
5 #include <sstream>
6 #include <stdexcept>
7 #include <type_traits>
8 
9 // TODO: fwd declare instead?
11 #include "GaudiKernel/Algorithm.h"
15 #include "GaudiKernel/detected.h"
16 
17 // Boost
18 #include "boost/optional.hpp"
19 
20 // Range V3
21 #include <range/v3/view/const.hpp>
22 #include <range/v3/view/zip.hpp>
23 
24 namespace Gaudi
25 {
26  namespace Functional
27  {
28  namespace details
29  {
30 
31  // CRJ : Stuff for zipping
32  namespace zip
33  {
34 
36  template <typename OS, typename Arg>
37  void printSizes( OS& out, Arg&& arg )
38  {
39  out << "SizeOf'" << System::typeinfoName( typeid( Arg ) ) << "'=" << std::forward<Arg>( arg ).size();
40  }
41 
43  template <typename OS, typename Arg, typename... Args>
44  void printSizes( OS& out, Arg&& arg, Args&&... args )
45  {
46  printSizes( out, arg );
47  out << ", ";
48  printSizes( out, args... );
49  }
50 
52  template <typename A>
53  inline bool check_sizes( const A& ) noexcept
54  {
55  return true;
56  }
57 
59  template <typename A, typename B>
60  inline bool check_sizes( const A& a, const B& b ) noexcept
61  {
62  return a.size() == b.size();
63  }
64 
66  template <typename A, typename B, typename... C>
67  inline bool check_sizes( const A& a, const B& b, const C&... c ) noexcept
68  {
69  return ( check_sizes( a, b ) && check_sizes( b, c... ) );
70  }
71 
73  template <typename... Args>
74  inline decltype( auto ) verifySizes( Args&... args )
75  {
76  if ( UNLIKELY( !check_sizes( args... ) ) ) {
77  std::ostringstream mess;
78  mess << "Zipped containers have different sizes : ";
79  printSizes( mess, args... );
80  throw GaudiException( mess.str(), "Gaudi::Functional::details::zip::verifySizes", StatusCode::FAILURE );
81  }
82  }
83 
85  template <typename... Args>
86  inline decltype( auto ) range( Args&&... args )
87  {
88 #ifndef NDEBUG
89  verifySizes( args... );
90 #endif
91  return ranges::view::zip( std::forward<Args>( args )... );
92  }
93 
95  template <typename... Args>
96  inline decltype( auto ) const_range( Args&&... args )
97  {
98 #ifndef NDEBUG
99  verifySizes( args... );
100 #endif
101  return ranges::view::const_( ranges::view::zip( std::forward<Args>( args )... ) );
102  }
103  }
104 
105 #if __cplusplus < 201703L
106  // implementation of C++17 std::as_const, see http://en.cppreference.com/w/cpp/utility/as_const
107  template <typename T>
108  constexpr std::add_const_t<T>& as_const( T& t ) noexcept
109  {
110  return t;
111  }
112 
113  template <typename T>
114  void as_const( T&& t ) = delete;
115 #else
116  using std::as_const;
117 #endif
118 
120  template <typename Out1, typename Out2, typename = std::enable_if_t<std::is_constructible<Out1, Out2>::value>>
121  Out1* put( DataObjectHandle<Out1>& out_handle, Out2&& out )
122  {
123  return out_handle.put( std::make_unique<Out1>( std::forward<Out2>( out ) ) );
124  }
125 
126  template <typename Out1, typename Out2, typename = std::enable_if_t<std::is_constructible<Out1, Out2>::value>>
127  void put( AnyDataHandle<Out1>& out_handle, Out2&& out )
128  {
129  out_handle.put( std::forward<Out2>( out ) );
130  }
131 
132  // optional put
133  template <typename OutHandle, typename Out>
134  void put( OutHandle& out_handle, boost::optional<Out>&& out )
135  {
136  if ( out ) put( out_handle, std::move( *out ) );
137  }
139  // adapt to differences between eg. std::vector (which has push_back) and KeyedContainer (which has insert)
140  // adapt to getting a T, and a container wanting T* by doing new T{ std::move(out) }
141  // adapt to getting a boost::optional<T>
142 
143  constexpr struct insert_t {
144  // for Container<T*>, return T
145  template <typename Container>
146  using c_remove_ptr_t = std::remove_pointer_t<typename Container::value_type>;
147 
148  template <typename Container, typename Value>
149  auto operator()( Container& c, Value&& v ) const -> decltype( c.push_back( v ) )
150  {
151  return c.push_back( std::forward<Value>( v ) );
152  }
153 
154  template <typename Container, typename Value>
155  auto operator()( Container& c, Value&& v ) const -> decltype( c.insert( v ) )
156  {
157  return c.insert( std::forward<Value>( v ) );
158  }
159 
160  // Container<T*> with T&& as argument
161  template <typename Container,
162  typename = std::enable_if_t<std::is_pointer<typename Container::value_type>::value>>
163  auto operator()( Container& c, c_remove_ptr_t<Container>&& v ) const
164  {
165  return operator()( c, new c_remove_ptr_t<Container>{std::move( v )} );
166  }
167 
168  template <typename Container, typename Value>
169  void operator()( Container& c, boost::optional<Value>&& v ) const
170  {
171  if ( v ) operator()( c, std::move( *v ) );
172  }
173  } insert{};
174 
176 
177  constexpr struct deref_t {
178  template <typename In, typename = std::enable_if_t<!std::is_pointer<In>::value>>
179  const In& operator()( const In& in ) const
180  {
181  return in;
182  }
183 
184  template <typename In>
185  const In& operator()( const In* in ) const
186  {
187  assert( in != nullptr );
188  return *in;
189  }
190  } deref{};
191 
193 
194  namespace details2
195  {
196  template <typename T>
198  typedef T type;
199  };
200  template <typename T>
201  struct remove_optional<boost::optional<T>> {
202  typedef T type;
203  };
204  // template< typename T > struct remove_optional< std::optional<T> > {typedef T type;};
205  }
206  template <typename T>
208  template <typename T>
210  };
211  template <typename T>
212  struct is_optional<boost::optional<T>> : std::true_type {
213  };
214  // C++17: template <typename T> constexpr bool is_optional_v = is_optional<T>::value;
215 
217  // if Container is a pointer, then we're optional items
218  namespace details2
219  {
220  template <typename Container, typename Value>
221  void push_back( Container& c, const Value& v, std::true_type )
222  {
223  c.push_back( v );
224  }
225  template <typename Container, typename Value>
226  void push_back( Container& c, const Value& v, std::false_type )
227  {
228  c.push_back( &v );
229  }
230 
231  template <typename In>
233  template <template <typename> class Handle, typename I,
234  typename = std::enable_if_t<std::is_convertible<I, In>::value>>
235  auto operator()( const Handle<I>& h ) -> const In&
236  {
237  return *h.get();
238  }
239  template <template <typename> class Handle, typename I,
240  typename = std::enable_if_t<std::is_convertible<I*, In>::value>>
241  auto operator()( const Handle<I>& h ) -> const In
242  {
243  return h.getIfExists();
244  } // In is-a pointer
245  };
246 
247  template <typename T>
248  T* deref_if( T* const t, std::false_type )
249  {
250  return t;
251  }
252  template <typename T>
253  T& deref_if( T* const t, std::true_type )
254  {
255  return *t;
256  }
257  }
258 
259  template <typename Container>
261  {
263  using val_t = std::add_const_t<std::remove_pointer_t<Container>>;
264  using ptr_t = std::add_pointer_t<val_t>;
265  using ref_t = std::add_lvalue_reference_t<val_t>;
268 
269  public:
270  using value_type = std::conditional_t<is_optional, ptr_t, val_t>;
271  using size_type = typename ContainerVector::size_type;
272  class iterator
273  {
274  typename ContainerVector::const_iterator m_i;
275  friend class vector_of_const_;
276  iterator( typename ContainerVector::const_iterator iter ) : m_i( iter ) {}
277  using ret_t = std::conditional_t<is_optional, ptr_t, ref_t>;
278 
279  public:
280  friend bool operator!=( const iterator& lhs, const iterator& rhs ) { return lhs.m_i != rhs.m_i; }
283  {
284  ++m_i;
285  return *this;
286  }
288  {
289  --m_i;
290  return *this;
291  }
292  bool is_null() const { return !*m_i; }
293  explicit operator bool() const { return !is_null(); }
294  };
295  vector_of_const_() = default;
296  void reserve( size_type size ) { m_containers.reserve( size ); }
297  template <typename T> // , typename = std::is_convertible<T,std::conditional_t<is_optional,ptr_t,val_t>>
298  void push_back( T&& container )
299  {
300  details2::push_back( m_containers, std::forward<T>( container ),
302  } // note: does not copy its argument, so we're not really a container...
303  iterator begin() const { return m_containers.begin(); }
304  iterator end() const { return m_containers.end(); }
305  size_type size() const { return m_containers.size(); }
306  const Container& operator[]( size_type i ) const { return *m_containers[i]; }
307  const Container& at( size_type i ) const
308  {
309  if ( UNLIKELY( i >= size() ) ) throw std::out_of_range{"vector_of_const_::at"};
310  return *m_containers[i];
311  }
312  bool is_null( size_type i ) const { return !m_containers[i]; }
313  };
314 
316  namespace detail2
317  { // utilities for detected_or_t{,_} usage
318  template <typename Tr>
319  using BaseClass_t = typename Tr::BaseClass;
320  template <typename Tr, typename T>
321  using OutputHandle_t = typename Tr::template OutputHandle<T>;
322  template <typename Tr, typename T>
323  using InputHandle_t = typename Tr::template InputHandle<T>;
324  }
325 
326  // check whether Traits::BaseClass is a valid type,
327  // if so, define BaseClass_t<Traits> as being Traits::BaseClass
328  // else define as being GaudiAlgorithm
329  template <typename Tr>
330  using BaseClass_t = Gaudi::cpp17::detected_or_t<GaudiAlgorithm, detail2::BaseClass_t, Tr>;
331 
332  // check whether Traits::{Input,Output}Handle<T> is a valid type,
333  // if so, define {Input,Output}Handle_t<Traits,T> as being Traits::{Input,Output}Handle<T>
334  // else define as being DataObject{Read,,Write}Handle<T>
335  template <typename Tr, typename T>
336  using OutputHandle_t = Gaudi::cpp17::detected_or_t<DataObjectWriteHandle<T>, detail2::OutputHandle_t, Tr, T>;
337  template <typename Tr, typename T>
338  using InputHandle_t = Gaudi::cpp17::detected_or_t<DataObjectReadHandle<T>, detail2::InputHandle_t, Tr, T>;
339 
341 
342  template <typename Handles>
344  {
345  Handles handles;
346  handles.reserve( init.size() );
347  std::transform( init.begin(), init.end(), std::back_inserter( handles ),
348  [&]( const std::string& loc ) -> typename Handles::value_type {
349  return {loc, owner};
350  } );
351  return handles;
352  }
353 
355 
356  template <typename OutputSpec, typename InputSpec, typename Traits_>
358 
359  template <typename... Out, typename... In, typename Traits_>
360  class DataHandleMixin<std::tuple<Out...>, std::tuple<In...>, Traits_> : public BaseClass_t<Traits_>
361  {
362  static_assert( std::is_base_of<Algorithm, BaseClass_t<Traits_>>::value,
363  "BaseClass must inherit from Algorithm" );
364 
365  template <typename IArgs, typename OArgs, std::size_t... I, std::size_t... J>
366  DataHandleMixin( const std::string& name, ISvcLocator* pSvcLocator, const IArgs& inputs,
367  std::index_sequence<I...>, const OArgs& outputs, std::index_sequence<J...> )
368  : BaseClass_t<Traits_>( name, pSvcLocator )
369  , m_inputs( std::tuple_cat( std::forward_as_tuple( this ), std::get<I>( inputs ) )... )
370  , m_outputs( std::tuple_cat( std::forward_as_tuple( this ), std::get<J>( outputs ) )... )
371  {
372  // make sure this algorithm is seen as reentrant by Gaudi
373  this->setProperty( "Cardinality", 0 );
374  }
375 
376  public:
378  constexpr static std::size_t N_in = sizeof...( In );
379  constexpr static std::size_t N_out = sizeof...( Out );
380 
381  // generic constructor: N -> M
383  const std::array<KeyValue, N_out>& outputs )
384  : DataHandleMixin( name, pSvcLocator, inputs, std::index_sequence_for<In...>{}, outputs,
385  std::index_sequence_for<Out...>{} )
386  {
387  }
388 
389  // special cases: forward to the generic case...
390  // 1 -> 1
391  DataHandleMixin( const std::string& name, ISvcLocator* locator, const KeyValue& input, const KeyValue& output )
392  : DataHandleMixin( name, locator, std::array<KeyValue, 1>{input}, std::array<KeyValue, 1>{output} )
393  {
394  }
395  // 1 -> N
396  DataHandleMixin( const std::string& name, ISvcLocator* locator, const KeyValue& input,
397  const std::array<KeyValue, N_out>& outputs )
398  : DataHandleMixin( name, locator, std::array<KeyValue, 1>{input}, outputs )
399  {
400  }
401  // N -> 1
403  const KeyValue& output )
404  : DataHandleMixin( name, locator, inputs, std::array<KeyValue, 1>{output} )
405  {
406  }
407 
408  template <std::size_t N = 0>
409  const std::string& inputLocation() const
410  {
411  return std::get<N>( m_inputs ).objKey();
412  }
413  constexpr unsigned int inputLocationSize() const { return N_in; }
414 
415  template <std::size_t N = 0>
417  {
418  return std::get<N>( m_outputs ).objKey();
419  }
420  constexpr unsigned int outputLocationSize() const { return N_out; }
421 
422  protected:
425  };
426 
427  template <typename... In, typename Traits_>
428  class DataHandleMixin<void, std::tuple<In...>, Traits_> : public BaseClass_t<Traits_>
429  {
430  static_assert( std::is_base_of<Algorithm, BaseClass_t<Traits_>>::value,
431  "BaseClass must inherit from Algorithm" );
432 
433  template <typename IArgs, std::size_t... I>
434  DataHandleMixin( const std::string& name, ISvcLocator* pSvcLocator, const IArgs& inputs,
435  std::index_sequence<I...> )
436  : BaseClass_t<Traits_>( name, pSvcLocator )
437  , m_inputs( std::tuple_cat( std::forward_as_tuple( this ), std::get<I>( inputs ) )... )
438  {
439  // make sure this algorithm is seen as reentrant by Gaudi
440  this->setProperty( "Cardinality", 0 );
441  }
442 
443  public:
445  constexpr static std::size_t N_in = sizeof...( In );
446 
447  // generic constructor: N -> 0
449  : DataHandleMixin( name, pSvcLocator, inputs, std::index_sequence_for<In...>{} )
450  {
451  }
452 
453  // special cases: forward to the generic case...
454  // 1 -> 0
455  DataHandleMixin( const std::string& name, ISvcLocator* locator, const KeyValue& input )
456  : DataHandleMixin( name, locator, std::array<KeyValue, 1>{input} )
457  {
458  }
459 
460  template <std::size_t N = 0>
461  const std::string& inputLocation() const
462  {
463  return std::get<N>( m_inputs ).objKey();
464  }
465  constexpr unsigned int inputLocationSize() const { return N_in; }
466 
467  protected:
469  };
470 
471  template <typename... Out, typename Traits_>
472  class DataHandleMixin<std::tuple<Out...>, void, Traits_> : public BaseClass_t<Traits_>
473  {
474  static_assert( std::is_base_of<Algorithm, BaseClass_t<Traits_>>::value,
475  "BaseClass must inherit from Algorithm" );
476 
477  template <typename OArgs, std::size_t... J>
478  DataHandleMixin( const std::string& name, ISvcLocator* pSvcLocator, const OArgs& outputs,
479  std::index_sequence<J...> )
480  : BaseClass_t<Traits_>( name, pSvcLocator )
481  , m_outputs( std::tuple_cat( std::forward_as_tuple( this ), std::get<J>( outputs ) )... )
482  {
483  // make sure this algorithm is seen as reentrant by Gaudi
484  this->setProperty( "Cardinality", 0 );
485  }
486 
487  public:
489  constexpr static std::size_t N_out = sizeof...( Out );
490 
491  // generic constructor: 0 -> N
493  : DataHandleMixin( name, pSvcLocator, outputs, std::index_sequence_for<Out...>{} )
494  {
495  }
496 
497  // 0 -> 1
499  : DataHandleMixin( name, locator, std::array<KeyValue, 1>{output} )
500  {
501  }
502 
503  template <std::size_t N = 0>
505  {
506  return std::get<N>( m_outputs ).objKey();
507  }
508  constexpr unsigned int outputLocationSize() const { return N_out; }
509 
510  protected:
512  };
513 
515  template <typename Fun, typename Container, typename... Args>
516  constexpr void applyPostProcessing( const Fun&, Container&, Args... )
517  {
518  static_assert( sizeof...( Args ) == 0, "Args should not be used!" );
519  }
520 
521  template <typename Fun, typename Container>
522  auto applyPostProcessing( const Fun& fun, Container& c ) -> decltype( fun.postprocess( c ), void() )
523  {
524  fun.postprocess( c );
525  }
526 
528  }
529  }
530 }
531 
532 #endif
typename Tr::template InputHandle< T > InputHandle_t
DataHandleMixin(const std::string &name, ISvcLocator *pSvcLocator, const std::array< KeyValue, N_out > &outputs)
DataHandleMixin(const std::string &name, ISvcLocator *pSvcLocator, const std::array< KeyValue, N_in > &inputs)
auto operator()(Container &c, Value &&v) const -> decltype(c.insert(v))
StatusCode setProperty(IProperty *component, const std::string &name, const TYPE &value, const std::string &doc)
simple function to set the property of the given object from the value
Definition: Property.h:1234
#define UNLIKELY(x)
Definition: Kernel.h:128
constexpr static const auto FAILURE
Definition: StatusCode.h:88
DataHandleMixin(const std::string &name, ISvcLocator *pSvcLocator, const IArgs &inputs, std::index_sequence< I... >, const OArgs &outputs, std::index_sequence< J... >)
const In & operator()(const In &in) const
Define general base for Gaudi exception.
DataHandleMixin(const std::string &name, ISvcLocator *locator, const std::array< KeyValue, N_in > &inputs, const KeyValue &output)
The ISvcLocator is the interface implemented by the Service Factory in the Application Manager to loc...
Definition: ISvcLocator.h:25
friend bool operator!=(const iterator &lhs, const iterator &rhs)
void as_const(T &&t)=delete
GAUDI_API const std::string typeinfoName(const std::type_info &)
Get platform independent information about the class type.
Definition: System.cpp:332
auto operator()(Container &c, c_remove_ptr_t< Container > &&v) const
Gaudi::cpp17::detected_or_t< DataObjectReadHandle< T >, detail2::InputHandle_t, Tr, T > InputHandle_t
void printSizes(OS &out, Arg &&arg)
Print the parameter.
constexpr struct Gaudi::Functional::details::insert_t insert
DataHandleMixin(const std::string &name, ISvcLocator *pSvcLocator, const std::array< KeyValue, N_in > &inputs, const std::array< KeyValue, N_out > &outputs)
Header file for class GaudiAlgorithm.
DataHandleMixin(const std::string &name, ISvcLocator *locator, const KeyValue &input, const KeyValue &output)
std::add_const_t< std::remove_pointer_t< Container >> val_t
STL namespace.
class MergingTransformer< Out(const vector_of_const_< In > void
auto operator()(Container &c, Value &&v) const -> decltype(c.push_back(v))
T end(T...args)
DataHandleMixin(const std::string &name, ISvcLocator *pSvcLocator, const OArgs &outputs, std::index_sequence< J... >)
void push_back(Container &c, const Value &v, std::true_type)
std::remove_pointer_t< typename Container::value_type > c_remove_ptr_t
iterator(typename ContainerVector::const_iterator iter)
std::add_lvalue_reference_t< val_t > ref_t
T * deref_if(T *const t, std::false_type)
const Container & operator[](size_type i) const
constexpr struct Gaudi::Functional::details::deref_t deref
constexpr auto size(const C &c) noexcept(noexcept(c.size())) -> decltype(c.size())
STL class.
std::conditional_t< is_optional, ptr_t, val_t > value_type
DataObjectHandle.h GaudiKernel/DataObjectHandle.h.
Definition: AlgTool.h:26
typename Tr::template OutputHandle< T > OutputHandle_t
constexpr void applyPostProcessing(const Fun &, Container &, Args...)
DataHandleMixin(const std::string &name, ISvcLocator *locator, const KeyValue &input, const std::array< KeyValue, N_out > &outputs)
auto operator()(const Handle< I > &h) -> const In &
typename details2::remove_optional< T >::type remove_optional_t
auto operator()(const Handle< I > &h) -> const In
DataHandleMixin(const std::string &name, ISvcLocator *locator, const KeyValue &output)
decltype(auto) range(Args &&...args)
Zips multiple containers together to form a single range.
T move(T...args)
decltype(auto) verifySizes(Args &...args)
Verify the data container sizes have the same sizes.
constexpr std::add_const_t< T > & as_const(T &t) noexcept
Base class from which all concrete algorithm classes should be derived.
Definition: Algorithm.h:78
T size(T...args)
T * put(std::unique_ptr< T > object)
Register object in transient store.
std::vector< InputHandle_t< In > > m_inputs
struct GAUDI_API array
Parametrisation class for redirection array - like implementation.
std::conditional_t< is_optional, ptr_t, ref_t > ret_t
virtual Out operator()(const vector_of_const_< In > &inputs) const =0
void operator()(Container &c, boost::optional< Value > &&v) const
T begin(T...args)
bool check_sizes(const A &) noexcept
Resolve case there is only one container in the range.
Gaudi::cpp17::detected_or_t< DataObjectWriteHandle< T >, detail2::OutputHandle_t, Tr, T > OutputHandle_t
T back_inserter(T...args)
const Container & at(size_type i) const
std::tuple< details::OutputHandle_t< Traits_, Out >... > m_outputs
double fun(const std::vector< double > &x)
Definition: PFuncTest.cpp:26
STL class.
DataHandleMixin(const std::string &name, ISvcLocator *locator, const KeyValue &input)
struct[[deprecated("use MergingTransformer instead")]] Traits_
T transform(T...args)
const In & operator()(const In *in) const
decltype(auto) const_range(Args &&...args)
Zips multiple containers together to form a single const range.
Handles make_vector_of_handles(IDataHandleHolder *owner, const std::vector< std::string > &init)
T & deref_if(T *const t, std::true_type)
Helper functions to set/get the application return code.
Definition: __init__.py:1
Gaudi::cpp17::detected_or_t< GaudiAlgorithm, detail2::BaseClass_t, Tr > BaseClass_t
Out1 * put(DataObjectHandle< Out1 > &out_handle, Out2 &&out)
void push_back(Container &c, const Value &v, std::false_type)
DataHandleMixin(const std::string &name, ISvcLocator *pSvcLocator, const IArgs &inputs, std::index_sequence< I... >)
T reserve(T...args)
typename ContainerVector::size_type size_type