Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:58:58

0001 //
0002 // ********************************************************************
0003 // * License and Disclaimer                                           *
0004 // *                                                                  *
0005 // * The  Geant4 software  is  copyright of the Copyright Holders  of *
0006 // * the Geant4 Collaboration.  It is provided  under  the terms  and *
0007 // * conditions of the Geant4 Software License,  included in the file *
0008 // * LICENSE and available at  http://cern.ch/geant4/license .  These *
0009 // * include a list of copyright holders.                             *
0010 // *                                                                  *
0011 // * Neither the authors of this software system, nor their employing *
0012 // * institutes,nor the agencies providing financial support for this *
0013 // * work  make  any representation or  warranty, express or implied, *
0014 // * regarding  this  software system or assume any liability for its *
0015 // * use.  Please see the license in the file  LICENSE  and URL above *
0016 // * for the full disclaimer and the limitation of liability.         *
0017 // *                                                                  *
0018 // * This  code  implementation is the result of  the  scientific and *
0019 // * technical work of the GEANT4 collaboration.                      *
0020 // * By using,  copying,  modifying or  distributing the software (or *
0021 // * any work based  on the software)  you  agree  to acknowledge its *
0022 // * use  in  resulting  scientific  publications,  and indicate your *
0023 // * acceptance of all terms of the Geant4 Software license.          *
0024 // ********************************************************************
0025 //
0026 // G4Profiler
0027 //
0028 // Template definition file
0029 //
0030 // Author: Jonathan Madsen, LBNL - November 2020
0031 // --------------------------------------------------------------------
0032 
0033 #if !defined(G4PROFILER_ICC_)
0034 #  define G4PROFILER_ICC_ 1
0035 
0036 #  include <functional>
0037 #  include <type_traits>
0038 #  include <tuple>
0039 #  include <initializer_list>
0040 #  include <string>
0041 #  include <sstream>
0042 
0043 // for index_sequence implementation
0044 #  include "PTL/Globals.hh"
0045 
0046 #  if defined(GEANT4_USE_TIMEMORY)
0047 #    include <timemory/utility/utility.hpp>
0048 #  endif
0049 
0050 #  if !defined(GEANT4_FOLD_EXPRESSION)
0051 #    define GEANT4_FOLD_EXPRESSION(...)                                        \
0052       ::G4Impl::consume_parameters(                                            \
0053         ::std::initializer_list<int>{ (__VA_ARGS__, 0)... })
0054 #  endif
0055 
0056 #  if !defined(G4PROFILER_ARG_SET)
0057 #    define G4PROFILER_ARG_SET(...) G4TypeList<__VA_ARGS__>
0058 #  endif
0059 
0060 //----------------------------------------------------------------------------//
0061 // lightweight (w.r.t. compile-time) alternative to std::tuple that doesn't
0062 // store anything and cannot be instantiated because it has no definition. This
0063 // guards against meta-programming mistakes where:
0064 //    std::function<void(const G4Step*)>
0065 // ends up as
0066 //    std::function<void(G4TypeList<const G4Step*>)>
0067 template <typename... Types>
0068 struct G4TypeList;
0069 
0070 // this is used in G4Impl::Functors to add a common set of arguments to all of
0071 // the functors
0072 template <typename... Types>
0073 struct G4CommonTypeList;
0074 
0075 //--------------------------------------------------------------------------------------//
0076 //
0077 template <typename... Types>
0078 struct G4TypeListSize;
0079 
0080 template <typename... Types>
0081 struct G4TypeListSize<G4TypeList<Types...>>
0082 {
0083   static constexpr size_t value = sizeof...(Types);
0084 };
0085 
0086 template <typename... Types>
0087 struct G4TypeListSize<std::tuple<Types...>>
0088 {
0089   static constexpr size_t value = std::tuple_size<std::tuple<Types...>>::value;
0090 };
0091 
0092 namespace G4Impl
0093 {
0094   template <typename Tp>
0095   std::string demangle()
0096   {
0097 #  if defined(GEANT4_USE_TIMEMORY)
0098     return tim::demangle<Tp>();
0099 #  else
0100     return typeid(Tp).name();
0101 #  endif
0102   }
0103 
0104   template <typename... Tp>
0105   void consume_parameters(Tp&&...)
0106   {}
0107   //------------------------------------------------------------------------//
0108   // don't provide a definition that works without G4TypeList
0109   template <typename RetT, typename... Tail>
0110   struct Functors;
0111   //------------------------------------------------------------------------//
0112   template <typename RetT, typename... Tail>
0113   struct Functors<RetT, G4TypeList<Tail...>>
0114   {
0115     using type = std::function<RetT(Tail...)>;
0116   };
0117   //------------------------------------------------------------------------//
0118   template <typename RetT, typename... CommonT, typename... Tail>
0119   struct Functors<RetT, G4CommonTypeList<CommonT...>, G4TypeList<Tail...>>
0120   {
0121     using type = std::function<RetT(CommonT..., Tail...)>;
0122   };
0123   //------------------------------------------------------------------------//
0124   template <typename RetT, typename... Types, typename... Tail>
0125   struct Functors<RetT, G4TypeList<G4TypeList<Types...>, Tail...>>
0126   {
0127     using type = std::tuple<std::function<RetT(Types...)>,
0128                             typename Functors<RetT, Tail>::type...>;
0129   };
0130   //------------------------------------------------------------------------//
0131   template <typename RetT, typename... CommonT, typename... Types,
0132             typename... Tail>
0133   struct Functors<RetT, G4CommonTypeList<CommonT...>,
0134                   G4TypeList<G4TypeList<Types...>, Tail...>>
0135   {
0136     using type = std::tuple<
0137       std::function<RetT(CommonT..., Types...)>,
0138       typename Functors<RetT, G4CommonTypeList<CommonT...>, Tail>::type...>;
0139   };
0140   //------------------------------------------------------------------------//
0141   template <typename RetT, typename... Tail>
0142   using Functors_t = typename Functors<RetT, Tail...>::type;
0143 
0144 }  // namespace G4Impl
0145 
0146 //
0147 //  this allows the generic invocation or assignment of a functor
0148 //
0149 template <typename Type, typename FuncT, typename RetT = void>
0150 struct FuncHandler
0151 {
0152   using this_type = FuncHandler<Type, FuncT, RetT>;
0153 
0154   // until Geant4 updates to C++14 as a minimum
0155   template <typename Tp>
0156   using decay_t = typename std::decay<Tp>::type;
0157   template <bool Bv, typename Tp = void>
0158   using enable_if_t = typename std::enable_if<Bv, Tp>::type;
0159   template <size_t... Idx>
0160   using index_sequence = PTL::mpl::index_sequence<Idx...>;
0161   template <size_t NumT>
0162   using make_index_sequence = PTL::mpl::make_index_sequence<NumT>;
0163 
0164   static constexpr size_t size = std::tuple_size<FuncT>::value;
0165 
0166   FuncHandler(FuncT& _functors)
0167     : m_functors(_functors)
0168   {}
0169 
0170   // overloading the assignment operator will let users
0171   // be able to use one method for the G4ProfilerConfig
0172   // despite the potential variants. Thus this is valid:
0173   //
0174   // GetLabelFunctor() = [](int i) { return std::to_string(i); }
0175   // GetLabelFunctor() = [](float v) { return std::to_string(v); }
0176   //
0177   // but will only compile for types that are explicitly
0178   // supported --> the assign function iterates through the
0179   // specific variants at compile-time
0180   template <typename Func>
0181   void operator=(Func&& f)
0182   {
0183     assign(m_functors, std::forward<Func>(f), 0, make_index_sequence<size>{});
0184   }
0185 
0186  private:
0187   FuncT& m_functors;
0188 
0189   template <typename Tp>
0190   static enable_if_t<std::is_same<decay_t<Tp>, bool>::value, Tp>
0191   get_default_return_value()
0192   {
0193     return false;
0194   }
0195 
0196   template <typename Tp>
0197   static enable_if_t<std::is_same<decay_t<Tp>, std::string>::value, Tp>
0198   get_default_return_value()
0199   {
0200     // this may return an ugly mangled name but will at least but useful
0201     // and can be demangled with c++filt
0202     return std::string("label-functor-not-set-for-") + G4Impl::demangle<Tp>();
0203   }
0204 
0205   template <typename Tp>
0206   static enable_if_t<std::is_pointer<decay_t<Tp>>::value, Tp>
0207   get_default_return_value()
0208   {
0209     return nullptr;
0210   }
0211 
0212  private:
0213   using return_t = decay_t<RetT>;
0214 
0215   //
0216   //  NOTE: All references to "iterations" in the comments
0217   //  below refer to compile-time iterations, which are
0218   //  implemented through recusion below. Iterations stop
0219   //  when a valid statement has been found and thus necessitates
0220   //  four versions of the same function: two of these functions
0221   //  handle the end of the recursion 'sizeof...(Tail) == 0'
0222   //  and the first of these functions (1.a) is used if a valid
0223   //  statement is found and the second (1.b, if reached) introduces
0224   //  a compilation error. The third and fourth start the iteration
0225   //  when 'sizeof...(Tail) > 0'. If a valid statement is found
0226   //  in the third function (2.a), recursion stops. If not, the
0227   //  iteration is continued to the next index via the fourth
0228   //  function (2.b).
0229   //
0230 
0231   // INVOKE 1.a
0232   //
0233   // this is the end of the iteration through the potential
0234   // functor variants and the trailing '->' tests whether the
0235   // functor can be called with the given arguments. The
0236   // 'int' as the second parameter ensures (through overload
0237   // resolution rules) that this gets tested before the
0238   // function after this (1.b).
0239   // If the size of 'FuncT' is equal to 1, then this is also
0240   // the start of the iteration through the potential functor
0241   // variants.
0242   template <typename Tp, size_t Idx, size_t... Tail, typename... Args,
0243             enable_if_t<sizeof...(Tail) == 0, int> = 0>
0244   static auto invoke(Tp& _obj, int, index_sequence<Idx, Tail...>,
0245                      Args&&... _args)
0246     -> decltype(std::get<Idx>(_obj)(std::forward<Args>(_args)...), return_t{})
0247   {
0248     // if the functor has been set, then execute it
0249     if(std::get<Idx>(_obj))
0250       return std::get<Idx>(_obj)(std::forward<Args>(_args)...);
0251     else
0252     {
0253       std::stringstream ss;
0254       ss << "Error! Functor "
0255          << G4Impl::demangle<decltype(std::get<Idx>(_obj))>()
0256          << " was not set for " << G4Impl::demangle<Type>();
0257       throw std::runtime_error(ss.str());
0258     }
0259     // the default for booleans should return false
0260     return get_default_return_value<return_t>();
0261   }
0262 
0263   // INVOKE 1.b
0264   //
0265   // this is the end of the iteration through the potential
0266   // functor variants and if this function is reached during
0267   // compile-time, this means that the given arguments are
0268   // not supported by any of the functors and will fail to
0269   // compile. The 'long' as the second parameter ensures that
0270   // it has lower precedence than the one above
0271   template <typename Tp, size_t Idx, size_t... Tail, typename... Args,
0272             enable_if_t<sizeof...(Tail) == 0, int> = 0>
0273   static auto invoke(Tp&, long, index_sequence<Idx, Tail...>, Args&&...)
0274     -> return_t
0275   {
0276     // this will cause a failure at compile-time.
0277     // this ensures that this static assert is dependent
0278     // on this function getting instantiated, simply putting
0279     // 'false' here would result in compile-time failure
0280     // even if no code ever instantiated this function
0281     static_assert(!std::is_same<Tp, Tp>::value, "Error! No valid functor!");
0282     throw std::runtime_error(
0283       "Error! No valid functor! This should have caused a compilation error!");
0284     return return_t{};
0285   }
0286 
0287   // INVOKE 2.a
0288   //
0289   // If the size of 'FuncT' is greater than one, this is the
0290   // start of the iteration through the potential functor variants.
0291   // This version will be used if the X in '-> decltype(X, Y)'
0292   // is valid. If it is not valid, then overload resolution
0293   // rules will dictate that the compiler will move on to the
0294   // 'invoke' member function 2.b
0295   template <typename Tp, size_t Idx, size_t... Tail, typename... Args,
0296             enable_if_t<(sizeof...(Tail) > 0), int> = 0>
0297   static auto invoke(Tp& _obj, int, index_sequence<Idx, Tail...>,
0298                      Args&&... _args)
0299     -> decltype(std::get<Idx>(_obj)(std::forward<Args>(_args)...), return_t{})
0300   {
0301     return std::get<Idx>(_obj)(std::forward<Args>(_args)...);
0302   }
0303 
0304   // INVOKE 2.b
0305   //
0306   // If the above test was not valid, we discard the current index
0307   // ('Idx') and proceed to the next index. If there is only
0308   // one index remaining, then this will call proceed to the
0309   // first invoke member function (1.a). If there are multiple
0310   // indexes remaining, then this will proceed to the previous
0311   // invoke member function (2.a) and this will continue until
0312   // a valid match is found or will fail to compile.
0313   template <typename Tp, size_t Idx, size_t... Tail, typename... Args,
0314             enable_if_t<(sizeof...(Tail) > 0), int> = 0>
0315   static auto invoke(Tp& _obj, long, index_sequence<Idx, Tail...>,
0316                      Args&&... _args)
0317     -> decltype(invoke(_obj, 0, index_sequence<Tail...>{},
0318                        std::forward<Args>(_args)...))
0319   {
0320     return invoke(_obj, 0, index_sequence<Tail...>{},
0321                   std::forward<Args>(_args)...);
0322   }
0323 
0324  private:
0325   // this uses the same principles as the invoke member function.
0326   // See the comments there.
0327   template <typename LhsT, typename RhsT, size_t Idx, size_t... Tail,
0328             enable_if_t<sizeof...(Tail) == 0, int> = 0>
0329   static auto assign(LhsT& _lhs, RhsT&& _rhs, int, index_sequence<Idx, Tail...>)
0330     -> decltype((std::get<Idx>(_lhs) = std::forward<RhsT>(_rhs)), void())
0331   {
0332     std::get<Idx>(_lhs) = std::forward<RhsT>(_rhs);
0333   }
0334 
0335   // this uses the same principles as the invoke member function.
0336   // See the comments there.
0337   template <typename LhsT, typename RhsT, size_t Idx, size_t... Tail,
0338             enable_if_t<sizeof...(Tail) == 0, int> = 0>
0339   static void assign(LhsT&, RhsT&&, long, index_sequence<Idx, Tail...>)
0340   {
0341     // this will cause a failure at compile-time.
0342     // this ensures that this static assert is dependent
0343     // on this function getting instantiated, simply putting
0344     // 'false' here would result in compile-time failure
0345     // even if no code ever instantiated this function
0346     static_assert(!std::is_same<LhsT, LhsT>::value,
0347                   "Error! No valid functor assignment!");
0348     throw std::runtime_error(
0349       "Error! No valid functor! This should have caused a compilation error!");
0350   }
0351 
0352   // this uses the same principles as the invoke member function.
0353   // See the comments there.
0354   template <typename LhsT, typename RhsT, size_t Idx, size_t... Tail,
0355             enable_if_t<(sizeof...(Tail) > 0), int> = 0>
0356   static auto assign(LhsT& _lhs, RhsT&& _rhs, int, index_sequence<Idx, Tail...>)
0357     -> decltype((std::get<Idx>(_lhs) = std::forward<RhsT>(_rhs)), void())
0358   {
0359     std::get<Idx>(_lhs) = std::forward<RhsT>(_rhs);
0360   }
0361 
0362   // this uses the same principles as the invoke member function.
0363   // See the comments there.
0364   template <typename LhsT, typename RhsT, size_t Idx, size_t... Tail,
0365             enable_if_t<(sizeof...(Tail) > 0), int> = 0>
0366   static void assign(LhsT& _lhs, RhsT&& _rhs, long,
0367                      index_sequence<Idx, Tail...>)
0368   {
0369     assign(_lhs, std::forward<RhsT>(_rhs), 0, index_sequence<Tail...>{});
0370   }
0371 
0372  public:
0373   // overloading the call operator makes it generic to call
0374   // the functors but will only compile for types that are
0375   // explicitly supported --> the invoke function iterates
0376   // through the specific variants at compile-time to
0377   // ensure using SFINAE
0378   template <typename... Args>
0379   auto operator()(Args&&... _args)
0380     -> decltype(std::declval<this_type>().invoke(std::declval<FuncT&>(), 0,
0381                                                  make_index_sequence<size>{},
0382                                                  std::forward<Args>(_args)...))
0383   {
0384     return invoke(m_functors, 0, make_index_sequence<size>{},
0385                   std::forward<Args>(_args)...);
0386   }
0387 };
0388 
0389 //----------------------------------------------------------------------------//
0390 
0391 #endif