Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-10-15 08:03:38

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 <any>
0012 #include <array>
0013 #include <cassert>
0014 #include <cstddef>
0015 #include <utility>
0016 
0017 // #define _ACTS_ANY_ENABLE_VERBOSE
0018 // #define _ACTS_ANY_ENABLE_DEBUG
0019 // #define _ACTS_ANY_ENABLE_TRACK_ALLOCATIONS
0020 
0021 #if defined(_ACTS_ANY_ENABLE_TRACK_ALLOCATIONS)
0022 #include <iostream>
0023 #include <mutex>
0024 #include <set>
0025 #include <typeindex>
0026 #include <typeinfo>
0027 #endif
0028 
0029 #if defined(_ACTS_ANY_ENABLE_VERBOSE) || defined(_ACTS_ANY_ENABLE_DEBUG)
0030 #include <iomanip>
0031 #include <iostream>
0032 #endif
0033 
0034 #if defined(_ACTS_ANY_ENABLE_DEBUG)
0035 #define _ACTS_ANY_DEBUG(x) std::cout << x << std::endl;
0036 #else
0037 #define _ACTS_ANY_DEBUG(x)
0038 #endif
0039 
0040 #if defined(_ACTS_ANY_ENABLE_VERBOSE)
0041 #define _ACTS_ANY_VERBOSE(x) std::cout << x << std::endl;
0042 #define _ACTS_ANY_VERBOSE_BUFFER(s, b)              \
0043   do {                                              \
0044     std::cout << "" << s << ": 0x";                 \
0045     for (char c : b) {                              \
0046       std::cout << std::hex << static_cast<int>(c); \
0047     }                                               \
0048     std::cout << std::endl;                         \
0049   } while (0)
0050 #else
0051 #define _ACTS_ANY_VERBOSE(x)
0052 #define _ACTS_ANY_VERBOSE_BUFFER(s, b)
0053 #endif
0054 
0055 namespace Acts {
0056 
0057 #if defined(_ACTS_ANY_ENABLE_TRACK_ALLOCATIONS)
0058 static std::mutex _s_any_mutex;
0059 static std::set<std::pair<std::type_index, void*>> _s_any_allocations;
0060 
0061 #define _ACTS_ANY_TRACK_ALLOCATION(T, heap)                                  \
0062   do {                                                                       \
0063     std::lock_guard guard{_s_any_mutex};                                     \
0064     _s_any_allocations.emplace(std::type_index(typeid(T)), heap);            \
0065     _ACTS_ANY_DEBUG("Allocate type: " << typeid(T).name() << " at " << heap) \
0066   } while (0)
0067 
0068 #define _ACTS_ANY_TRACK_DEALLOCATION(T, heap)                                 \
0069   do {                                                                        \
0070     std::lock_guard guard{_s_any_mutex};                                      \
0071     auto it =                                                                 \
0072         _s_any_allocations.find(std::pair{std::type_index(typeid(T)), heap}); \
0073     if (it == _s_any_allocations.end()) {                                     \
0074       throw std::runtime_error{                                               \
0075           "Trying to deallocate heap address that we didn't allocate"};       \
0076     }                                                                         \
0077     _s_any_allocations.erase(it);                                             \
0078   } while (0)
0079 
0080 // Do not make member functions noexcept in the debug case
0081 #define _ACTS_ANY_NOEXCEPT /*nothing*/
0082 
0083 struct _AnyAllocationReporter {
0084   static void checkAllocations() {
0085     std::lock_guard guard{_s_any_mutex};
0086 
0087     if (!_s_any_allocations.empty()) {
0088       std::cout << "Not all allocations have been released" << std::endl;
0089       for (const auto& [idx, addr] : _s_any_allocations) {
0090         std::cout << "- " << idx.name() << ": " << addr << std::endl;
0091       }
0092       throw std::runtime_error{"AnyCheckAllocations failed"};
0093     }
0094   }
0095 
0096   ~_AnyAllocationReporter() noexcept { checkAllocations(); }
0097 };
0098 static _AnyAllocationReporter s_reporter;
0099 #else
0100 #define _ACTS_ANY_TRACK_ALLOCATION(T, heap) \
0101   do {                                      \
0102   } while (0)
0103 #define _ACTS_ANY_TRACK_DEALLOCATION(T, heap) \
0104   do {                                        \
0105   } while (0)
0106 #define _ACTS_ANY_NOEXCEPT noexcept
0107 #endif
0108 
0109 class AnyBaseAll {};
0110 
0111 /// Small opaque cache type which uses small buffer optimization
0112 template <std::size_t SIZE>
0113 class AnyBase : public AnyBaseAll {
0114   static_assert(sizeof(void*) <= SIZE, "Size is too small for a pointer");
0115 
0116  public:
0117   /// Construct with in-place type construction
0118   /// @tparam T Type to construct
0119   /// @tparam Args Constructor argument types
0120   /// @param args Arguments to forward to T's constructor
0121   template <typename T, typename... Args>
0122   explicit AnyBase(std::in_place_type_t<T> /*unused*/, Args&&... args) {
0123     using U = std::decay_t<T>;
0124     static_assert(
0125         std::is_move_assignable_v<U> && std::is_move_constructible_v<U>,
0126         "Type needs to be move assignable and move constructible");
0127     static_assert(
0128         std::is_copy_assignable_v<U> && std::is_copy_constructible_v<U>,
0129         "Type needs to be copy assignable and copy constructible");
0130 
0131     m_handler = makeHandler<U>();
0132     if constexpr (!heapAllocated<U>()) {
0133       // construct into local buffer
0134       /*U* ptr =*/new (m_data.data()) U(std::forward<Args>(args)...);
0135       _ACTS_ANY_VERBOSE("Construct local (this="
0136                         << this
0137                         << ") at: " << static_cast<void*>(m_data.data()));
0138     } else {
0139       // too large, heap allocate
0140       U* heap = new U(std::forward<Args>(args)...);
0141       _ACTS_ANY_DEBUG("Allocate type: " << typeid(U).name() << " at " << heap);
0142       _ACTS_ANY_TRACK_ALLOCATION(T, heap);
0143       setDataPtr(heap);
0144     }
0145   }
0146 
0147 #if defined(_ACTS_ANY_ENABLE_VERBOSE)
0148   AnyBase() { _ACTS_ANY_VERBOSE("Default construct this=" << this); };
0149 #else
0150   AnyBase() = default;
0151 #endif
0152 
0153   /// Construct from any value type
0154   /// @tparam T Type of the value to store
0155   /// @param value Value to store in the Any
0156   template <typename T>
0157   explicit AnyBase(T&& value) _ACTS_ANY_NOEXCEPT
0158     requires(!std::same_as<std::decay_t<T>, AnyBase<SIZE>>)
0159       : AnyBase{std::in_place_type<T>, std::forward<T>(value)} {}
0160 
0161   /// Get reference to stored value of specified type
0162   /// @tparam T Type to retrieve (must be exact type, no const/ref)
0163   /// @return Reference to the stored value
0164   /// @throws std::bad_any_cast if stored type doesn't match T
0165   template <typename T>
0166   T& as() {
0167     static_assert(std::is_same_v<T, std::decay_t<T>>,
0168                   "Please pass the raw type, no const or ref");
0169     if (makeHandler<T>() != m_handler) {
0170       throw std::bad_any_cast{};
0171     }
0172 
0173     _ACTS_ANY_VERBOSE("Get as "
0174                       << (m_handler->heapAllocated ? "heap" : "local"));
0175 
0176     return *reinterpret_cast<T*>(dataPtr());
0177   }
0178 
0179   /// Get const reference to stored value of specified type
0180   /// @tparam T Type to retrieve (must be exact type, no const/ref)
0181   /// @return Const reference to the stored value
0182   /// @throws std::bad_any_cast if stored type doesn't match T
0183   template <typename T>
0184   const T& as() const {
0185     static_assert(std::is_same_v<T, std::decay_t<T>>,
0186                   "Please pass the raw type, no const or ref");
0187     if (makeHandler<T>() != m_handler) {
0188       throw std::bad_any_cast{};
0189     }
0190 
0191     _ACTS_ANY_VERBOSE("Get as " << (m_handler->heap ? "heap" : "local"));
0192 
0193     return *reinterpret_cast<const T*>(dataPtr());
0194   }
0195 
0196   ~AnyBase() { destroy(); }
0197 
0198   /// Copy constructor
0199   /// @param other The AnyBase to copy from
0200   AnyBase(const AnyBase& other) _ACTS_ANY_NOEXCEPT {
0201     if (m_handler == nullptr && other.m_handler == nullptr) {
0202       // both are empty, noop
0203       return;
0204     }
0205 
0206     _ACTS_ANY_VERBOSE("Copy construct (this="
0207                       << this << ") at: " << static_cast<void*>(m_data.data()));
0208 
0209     m_handler = other.m_handler;
0210     copyConstruct(other);
0211   }
0212 
0213   /// Copy assignment operator
0214   /// @param other The AnyBase to copy from
0215   /// @return Reference to this object
0216   AnyBase& operator=(const AnyBase& other) _ACTS_ANY_NOEXCEPT {
0217     _ACTS_ANY_VERBOSE("Copy assign (this="
0218                       << this << ") at: " << static_cast<void*>(m_data.data()));
0219 
0220     if (m_handler == nullptr && other.m_handler == nullptr) {
0221       // both are empty, noop
0222       return *this;
0223     }
0224 
0225     if (m_handler == other.m_handler) {
0226       // same type, but checked before they're not both nullptr
0227       copy(std::move(other));
0228     } else {
0229       if (m_handler != nullptr) {
0230         // this object is not empty, but have different types => destroy
0231         destroy();
0232       }
0233       assert(m_handler == nullptr);
0234       m_handler = other.m_handler;
0235       copyConstruct(std::move(other));
0236     }
0237     return *this;
0238   }
0239 
0240   /// Move constructor
0241   /// @param other The AnyBase to move from
0242   AnyBase(AnyBase&& other) _ACTS_ANY_NOEXCEPT {
0243     _ACTS_ANY_VERBOSE("Move construct (this="
0244                       << this << ") at: " << static_cast<void*>(m_data.data()));
0245     if (m_handler == nullptr && other.m_handler == nullptr) {
0246       // both are empty, noop
0247       return;
0248     }
0249 
0250     m_handler = other.m_handler;
0251     moveConstruct(std::move(other));
0252   }
0253 
0254   /// Move assignment operator
0255   /// @param other The AnyBase to move from
0256   /// @return Reference to this object
0257   AnyBase& operator=(AnyBase&& other) _ACTS_ANY_NOEXCEPT {
0258     _ACTS_ANY_VERBOSE("Move assign (this="
0259                       << this << ") at: " << static_cast<void*>(m_data.data()));
0260     if (m_handler == nullptr && other.m_handler == nullptr) {
0261       // both are empty, noop
0262       return *this;
0263     }
0264 
0265     if (m_handler == other.m_handler) {
0266       // same type, but checked before they're not both nullptr
0267       move(std::move(other));
0268     } else {
0269       if (m_handler != nullptr) {
0270         // this object is not empty, but have different types => destroy
0271         destroy();
0272       }
0273       assert(m_handler == nullptr);
0274       m_handler = other.m_handler;
0275       moveConstruct(std::move(other));
0276     }
0277 
0278     return *this;
0279   }
0280 
0281   /// Check if the AnyBase contains a value
0282   /// @return True if a value is stored, false if empty
0283   explicit operator bool() const { return m_handler != nullptr; }
0284 
0285  private:
0286   void* dataPtr() {
0287     if (m_handler->heapAllocated) {
0288       return *reinterpret_cast<void**>(m_data.data());
0289     } else {
0290       return reinterpret_cast<void*>(m_data.data());
0291     }
0292   }
0293 
0294   void setDataPtr(void* ptr) { *reinterpret_cast<void**>(m_data.data()) = ptr; }
0295 
0296   const void* dataPtr() const {
0297     if (m_handler->heapAllocated) {
0298       return *reinterpret_cast<void* const*>(m_data.data());
0299     } else {
0300       return reinterpret_cast<const void*>(m_data.data());
0301     }
0302   }
0303 
0304   struct Handler {
0305     void (*destroy)(void* ptr) = nullptr;
0306     void (*moveConstruct)(void* from, void* to) = nullptr;
0307     void (*move)(void* from, void* to) = nullptr;
0308     void* (*copyConstruct)(const void* from, void* to) = nullptr;
0309     void (*copy)(const void* from, void* to) = nullptr;
0310     bool heapAllocated{false};
0311   };
0312 
0313   template <typename T>
0314   static const Handler* makeHandler() {
0315     static_assert(!std::is_same_v<T, AnyBase<SIZE>>, "Cannot wrap any in any");
0316     static const Handler static_handler = []() {
0317       Handler h;
0318       h.heapAllocated = heapAllocated<T>();
0319       if constexpr (!std::is_trivially_destructible_v<T> ||
0320                     heapAllocated<T>()) {
0321         h.destroy = &destroyImpl<T>;
0322       }
0323       if constexpr (!std::is_trivially_move_constructible_v<T> ||
0324                     heapAllocated<T>()) {
0325         h.moveConstruct = &moveConstructImpl<T>;
0326       }
0327       if constexpr (!std::is_trivially_move_assignable_v<T> ||
0328                     heapAllocated<T>()) {
0329         h.move = &moveImpl<T>;
0330       }
0331       if constexpr (!std::is_trivially_copy_constructible_v<T> ||
0332                     heapAllocated<T>()) {
0333         h.copyConstruct = &copyConstructImpl<T>;
0334       }
0335       if constexpr (!std::is_trivially_copy_assignable_v<T> ||
0336                     heapAllocated<T>()) {
0337         h.copy = &copyImpl<T>;
0338       }
0339 
0340       _ACTS_ANY_DEBUG("Type: " << typeid(T).name());
0341       _ACTS_ANY_DEBUG(" -> destroy: " << h.destroy);
0342       _ACTS_ANY_DEBUG(" -> moveConstruct: " << h.moveConstruct);
0343       _ACTS_ANY_DEBUG(" -> move: " << h.move);
0344       _ACTS_ANY_DEBUG(" -> copyConstruct: " << h.copyConstruct);
0345       _ACTS_ANY_DEBUG(" -> copy: " << h.copy);
0346       _ACTS_ANY_DEBUG(
0347           " -> heapAllocated: " << (h.heapAllocated ? "yes" : "no"));
0348 
0349       return h;
0350     }();
0351     return &static_handler;
0352   }
0353 
0354   template <typename T>
0355   static constexpr bool heapAllocated() {
0356     return sizeof(T) > SIZE;
0357   }
0358 
0359   void destroy() {
0360     _ACTS_ANY_VERBOSE("Destructor this=" << this << " handler: " << m_handler);
0361     if (m_handler != nullptr && m_handler->destroy != nullptr) {
0362       _ACTS_ANY_VERBOSE("Non-trivial destruction");
0363       m_handler->destroy(dataPtr());
0364     }
0365     m_handler = nullptr;
0366   }
0367 
0368   void moveConstruct(AnyBase&& fromAny) {
0369     if (m_handler == nullptr) {
0370       return;
0371     }
0372 
0373     void* to = dataPtr();
0374     void* from = fromAny.dataPtr();
0375     if (m_handler->heapAllocated) {
0376       // stored on heap: just copy the pointer
0377       setDataPtr(fromAny.dataPtr());
0378       // do not delete in moved-from any
0379       fromAny.m_handler = nullptr;
0380       return;
0381     }
0382 
0383     if (m_handler->moveConstruct == nullptr) {
0384       _ACTS_ANY_VERBOSE("Trivially move construct");
0385       // trivially move constructible
0386       m_data = std::move(fromAny.m_data);
0387     } else {
0388       m_handler->moveConstruct(from, to);
0389     }
0390   }
0391 
0392   void move(AnyBase&& fromAny) {
0393     if (m_handler == nullptr) {
0394       return;
0395     }
0396 
0397     void* to = dataPtr();
0398     void* from = fromAny.dataPtr();
0399     if (m_handler->heapAllocated) {
0400       // stored on heap: just copy the pointer
0401       // need to delete existing pointer
0402       m_handler->destroy(dataPtr());
0403       setDataPtr(fromAny.dataPtr());
0404       // do not delete in moved-from any
0405       fromAny.m_handler = nullptr;
0406       return;
0407     }
0408 
0409     if (m_handler->move == nullptr) {
0410       _ACTS_ANY_VERBOSE("Trivially move");
0411       // trivially movable
0412       m_data = std::move(fromAny.m_data);
0413     } else {
0414       m_handler->move(from, to);
0415     }
0416   }
0417 
0418   void copyConstruct(const AnyBase& fromAny) {
0419     if (m_handler == nullptr) {
0420       return;
0421     }
0422 
0423     void* to = dataPtr();
0424     const void* from = fromAny.dataPtr();
0425 
0426     if (m_handler->copyConstruct == nullptr) {
0427       _ACTS_ANY_VERBOSE("Trivially copy construct");
0428       // trivially copy constructible
0429       m_data = fromAny.m_data;
0430     } else {
0431       void* copyAt = m_handler->copyConstruct(from, to);
0432       if (to == nullptr) {
0433         assert(copyAt != nullptr);
0434         // copy allocated, store pointer
0435         setDataPtr(copyAt);
0436       }
0437     }
0438   }
0439 
0440   void copy(const AnyBase& fromAny) {
0441     if (m_handler == nullptr) {
0442       return;
0443     }
0444 
0445     void* to = dataPtr();
0446     const void* from = fromAny.dataPtr();
0447 
0448     if (m_handler->copy == nullptr) {
0449       _ACTS_ANY_VERBOSE("Trivially copy");
0450       // trivially copyable
0451       m_data = fromAny.m_data;
0452     } else {
0453       m_handler->copy(from, to);
0454     }
0455   }
0456 
0457   template <typename T>
0458   static void destroyImpl(void* ptr) {
0459     assert(ptr != nullptr && "Address to destroy is nullptr");
0460     T* obj = static_cast<T*>(ptr);
0461     if constexpr (!heapAllocated<T>()) {
0462       // stored in place: just call the destructor
0463       _ACTS_ANY_VERBOSE("Destroy local at: " << ptr);
0464       obj->~T();
0465     } else {
0466       // stored on heap: delete
0467       _ACTS_ANY_DEBUG("Delete type: " << typeid(T).name()
0468                                       << " heap at: " << obj);
0469       _ACTS_ANY_TRACK_DEALLOCATION(T, obj);
0470       delete obj;
0471     }
0472   }
0473 
0474   template <typename T>
0475   static void moveConstructImpl(void* from, void* to) {
0476     _ACTS_ANY_VERBOSE("move const: " << from << " -> " << to);
0477     assert(from != nullptr && "Source is null");
0478     assert(to != nullptr && "Target is null");
0479     T* _from = static_cast<T*>(from);
0480     /*T* ptr =*/new (to) T(std::move(*_from));
0481   }
0482 
0483   template <typename T>
0484   static void moveImpl(void* from, void* to) {
0485     _ACTS_ANY_VERBOSE("move: " << from << " -> " << to);
0486     assert(from != nullptr && "Source is null");
0487     assert(to != nullptr && "Target is null");
0488 
0489     T* _from = static_cast<T*>(from);
0490     T* _to = static_cast<T*>(to);
0491 
0492     (*_to) = std::move(*_from);
0493   }
0494 
0495   template <typename T>
0496   static void* copyConstructImpl(const void* from, void* to) {
0497     _ACTS_ANY_VERBOSE("copy const: " << from << " -> " << to);
0498     assert(from != nullptr && "Source is null");
0499     const T* _from = static_cast<const T*>(from);
0500     if (to == nullptr) {
0501       assert(heapAllocated<T>() && "Received nullptr in local buffer case");
0502       to = new T(*_from);
0503       _ACTS_ANY_TRACK_ALLOCATION(T, to);
0504 
0505     } else {
0506       assert(!heapAllocated<T>() && "Received non-nullptr in heap case");
0507       /*T* ptr =*/new (to) T(*_from);
0508     }
0509     return to;
0510   }
0511 
0512   template <typename T>
0513   static void copyImpl(const void* from, void* to) {
0514     _ACTS_ANY_VERBOSE("copy: " << from << " -> " << to);
0515     assert(from != nullptr && "Source is null");
0516     assert(to != nullptr && "Target is null");
0517 
0518     const T* _from = static_cast<const T*>(from);
0519     T* _to = static_cast<T*>(to);
0520 
0521     (*_to) = *_from;
0522   }
0523 
0524   static constexpr std::size_t kMaxAlignment =
0525       std::max(alignof(std::max_align_t),
0526 #if defined(__AVX512F__)
0527                std::size_t{64}
0528 #elif defined(__AVX__)
0529                std::size_t{32}
0530 #elif defined(__SSE__)
0531                std::size_t{16}
0532 #else
0533                std::size_t{0}
0534   // Neutral element
0535   // for maximum
0536 #endif
0537       );
0538 
0539   alignas(kMaxAlignment) std::array<std::byte, SIZE> m_data{};
0540   const Handler* m_handler{nullptr};
0541 };
0542 
0543 /// @brief A type-safe container for single values of any type
0544 /// @details This is a custom implementation similar to std::any but optimized for small types
0545 ///          that can fit into a pointer-sized buffer. Values larger than a
0546 ///          pointer are stored on the heap.
0547 using Any = AnyBase<sizeof(void*)>;
0548 
0549 #undef _ACTS_ANY_VERBOSE
0550 #undef _ACTS_ANY_VERBOSE_BUFFER
0551 #undef _ACTS_ANY_ENABLE_VERBOSE
0552 
0553 }  // namespace Acts