Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-14 09:39:52

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #pragma once
0010 
0011 #include <format>
0012 #include <functional>
0013 #include <type_traits>
0014 #include <typeindex>
0015 #include <typeinfo>
0016 #include <unordered_map>
0017 #include <vector>
0018 
0019 #include <boost/core/demangle.hpp>
0020 
0021 namespace Acts {
0022 
0023 /// Template class for type-based function dispatch
0024 ///
0025 /// This class allows registering function pointers associated with specific
0026 /// derived types of a base class. When invoked with a base class reference,
0027 /// it will look up and call the appropriate registered function.
0028 ///
0029 /// @tparam base_t The base class type that will be the first parameter
0030 /// @tparam signature_t The function signature (e.g., return_t(Args...))
0031 template <typename base_t, typename signature_t>
0032 class TypeDispatcher;
0033 
0034 template <typename base_t, typename return_t, typename... args_t>
0035 class TypeDispatcher<base_t, return_t(args_t...)> {
0036  public:
0037   // Type aliases for frequently used template parameters
0038   using base_type = base_t;
0039   using return_type = return_t;
0040   using self_type = TypeDispatcher<base_type, return_type(args_t...)>;
0041 
0042   using function_signature = return_t(const base_t&, args_t...);
0043   using function_pointer_type = return_t (*)(args_t...);
0044   using function_type = std::function<function_signature>;
0045 
0046   /// Default constructor
0047   TypeDispatcher() = default;
0048 
0049   /// Constructor that registers multiple function pointers with auto-detected
0050   /// types
0051   template <typename... derived_types_t>
0052   explicit TypeDispatcher(return_t (*... funcs)(const derived_types_t&,
0053                                                 args_t...))
0054     requires(std::is_base_of_v<base_t, derived_types_t> && ...)
0055   {
0056     (registerFunction(funcs),
0057      ...);  // Fold expression to register each function
0058   }
0059 
0060   /// Register a free function with explicit derived type
0061   /// @tparam derived_t The derived type to associate the function with
0062   /// @param func The function pointer
0063   template <typename derived_t>
0064     requires std::is_base_of_v<base_t, derived_t>
0065   self_type& registerFunction(return_t (*func)(const derived_t&, args_t...)) {
0066     std::type_index typeIdx(typeid(derived_t));
0067 
0068     // Check if this exact type is already registered
0069     if (m_functions.find(typeIdx) != m_functions.end()) {
0070       throw std::runtime_error(
0071           std::format("Function already registered for type: {}",
0072                       boost::core::demangle(typeIdx.name())));
0073     }
0074 
0075     // Try to detect conflicts with existing registrations if the type is
0076     // default constructible
0077     if constexpr (std::is_default_constructible_v<derived_t>) {
0078       derived_t tempObj{};
0079       for (const auto& [existingTypeIdx, checker] : m_castCheckers) {
0080         if (checker(tempObj)) {
0081           throw std::runtime_error(
0082               std::format("Registration conflict: type {} would be handled by "
0083                           "existing function registered for type: {}",
0084                           boost::core::demangle(typeIdx.name()),
0085                           boost::core::demangle(existingTypeIdx.name())));
0086         }
0087       }
0088     }
0089 
0090     // Store a cast checker that tests if dynamic_cast<derived_t*> will work
0091     m_castCheckers[typeIdx] = [](const base_t& obj) -> bool {
0092       const auto* derived = dynamic_cast<const derived_t*>(&obj);
0093       return derived != nullptr;
0094     };
0095 
0096     // Wrap the function in a lambda that performs the dynamic cast
0097     m_functions[typeIdx] = [func]<typename... Ts>(const base_t& base,
0098                                                   Ts&&... args) -> return_t {
0099       const auto* derived = dynamic_cast<const derived_t*>(&base);
0100       if (derived == nullptr) {
0101         throw std::bad_cast();
0102       }
0103       return func(*derived, std::forward<Ts>(args)...);
0104     };
0105 
0106     return *this;
0107   }
0108 
0109   /// Call the registered function for the given object's type
0110   /// @param obj The object to dispatch on
0111   /// @param args Additional arguments to pass to the function
0112   /// @return The return value from the registered function
0113   template <typename... func_args_t>
0114   return_t operator()(const base_t& obj, func_args_t&&... args) const
0115     requires std::invocable<function_pointer_type, func_args_t...>
0116   {
0117     std::vector<std::type_index> compatibleTypes;
0118 
0119     // Find all registered functions that can handle this object type
0120     for (const auto& [registeredTypeIdx, checker] : m_castCheckers) {
0121       if (checker(obj)) {
0122         compatibleTypes.push_back(registeredTypeIdx);
0123       }
0124     }
0125 
0126     if (compatibleTypes.empty()) {
0127       throw std::runtime_error(
0128           std::format("No function registered for type: {}",
0129                       boost::core::demangle(typeid(obj).name())));
0130     }
0131 
0132     if (compatibleTypes.size() > 1) {
0133       std::string typeNames;
0134       for (std::size_t i = 0; i < compatibleTypes.size(); ++i) {
0135         if (i > 0) {
0136           typeNames += ", ";
0137         }
0138         typeNames += boost::core::demangle(compatibleTypes[i].name());
0139       }
0140       throw std::runtime_error(
0141           std::format("Ambiguous dispatch for type {}: multiple functions can "
0142                       "handle it: {}",
0143                       boost::core::demangle(typeid(obj).name()), typeNames));
0144     }
0145 
0146     // Exactly one compatible function found
0147     auto funcIt = m_functions.find(compatibleTypes[0]);
0148     if (funcIt != m_functions.end()) {
0149       return funcIt->second(obj, std::forward<func_args_t>(args)...);
0150     }
0151 
0152     // This should never happen if our data structures are consistent
0153     throw std::runtime_error(
0154         "Internal error: function not found for compatible type");
0155   }
0156 
0157   /// Check if a function is registered for the given object's type
0158   /// @param obj The object to check
0159   /// @return true if a function is registered, false otherwise
0160   bool hasFunction(const base_t& obj) const {
0161     // Find all registered functions that can handle this object type
0162     for (const auto& [registeredTypeIdx, checker] : m_castCheckers) {
0163       if (checker(obj)) {
0164         return true;
0165       }
0166     }
0167     return false;
0168   }
0169 
0170   /// Check if a function is registered for the given type
0171   /// @tparam derived_t The type to check
0172   /// @return true if a function is registered, false otherwise
0173   template <typename derived_t>
0174     requires std::is_base_of_v<base_t, derived_t>
0175   bool hasFunction() const {
0176     std::type_index typeIdx(typeid(derived_t));
0177     return m_functions.find(typeIdx) != m_functions.end();
0178   }
0179 
0180   /// Clear all registered functions
0181   void clear() {
0182     m_functions.clear();
0183     m_castCheckers.clear();
0184   }
0185 
0186   /// Get the number of registered functions
0187   std::size_t size() const { return m_functions.size(); }
0188 
0189  private:
0190   using cast_checker_type = std::function<bool(const base_t&)>;
0191 
0192   std::unordered_map<std::type_index, function_type> m_functions;
0193   std::unordered_map<std::type_index, cast_checker_type> m_castCheckers;
0194 };
0195 
0196 }  // namespace Acts