Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-15 08:13:53

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   template <typename T, typename... Args>
0118   explicit AnyBase(std::in_place_type_t<T> /*unused*/, Args&&... args) {
0119     using U = std::decay_t<T>;
0120     static_assert(
0121         std::is_move_assignable_v<U> && std::is_move_constructible_v<U>,
0122         "Type needs to be move assignable and move constructible");
0123     static_assert(
0124         std::is_copy_assignable_v<U> && std::is_copy_constructible_v<U>,
0125         "Type needs to be copy assignable and copy constructible");
0126 
0127     m_handler = makeHandler<U>();
0128     if constexpr (!heapAllocated<U>()) {
0129       // construct into local buffer
0130       /*U* ptr =*/new (m_data.data()) U(std::forward<Args>(args)...);
0131       _ACTS_ANY_VERBOSE("Construct local (this="
0132                         << this
0133                         << ") at: " << static_cast<void*>(m_data.data()));
0134     } else {
0135       // too large, heap allocate
0136       U* heap = new U(std::forward<Args>(args)...);
0137       _ACTS_ANY_DEBUG("Allocate type: " << typeid(U).name() << " at " << heap);
0138       _ACTS_ANY_TRACK_ALLOCATION(T, heap);
0139       setDataPtr(heap);
0140     }
0141   }
0142 
0143 #if defined(_ACTS_ANY_ENABLE_VERBOSE)
0144   AnyBase() { _ACTS_ANY_VERBOSE("Default construct this=" << this); };
0145 #else
0146   AnyBase() = default;
0147 #endif
0148 
0149   template <typename T>
0150   explicit AnyBase(T&& value) _ACTS_ANY_NOEXCEPT
0151     requires(!std::same_as<std::decay_t<T>, AnyBase<SIZE>>)
0152       : AnyBase{std::in_place_type<T>, std::forward<T>(value)} {}
0153 
0154   template <typename T>
0155   T& as() {
0156     static_assert(std::is_same_v<T, std::decay_t<T>>,
0157                   "Please pass the raw type, no const or ref");
0158     if (makeHandler<T>() != m_handler) {
0159       throw std::bad_any_cast{};
0160     }
0161 
0162     _ACTS_ANY_VERBOSE("Get as "
0163                       << (m_handler->heapAllocated ? "heap" : "local"));
0164 
0165     return *reinterpret_cast<T*>(dataPtr());
0166   }
0167 
0168   template <typename T>
0169   const T& as() const {
0170     static_assert(std::is_same_v<T, std::decay_t<T>>,
0171                   "Please pass the raw type, no const or ref");
0172     if (makeHandler<T>() != m_handler) {
0173       throw std::bad_any_cast{};
0174     }
0175 
0176     _ACTS_ANY_VERBOSE("Get as " << (m_handler->heap ? "heap" : "local"));
0177 
0178     return *reinterpret_cast<const T*>(dataPtr());
0179   }
0180 
0181   ~AnyBase() { destroy(); }
0182 
0183   AnyBase(const AnyBase& other) _ACTS_ANY_NOEXCEPT {
0184     if (m_handler == nullptr && other.m_handler == nullptr) {
0185       // both are empty, noop
0186       return;
0187     }
0188 
0189     _ACTS_ANY_VERBOSE("Copy construct (this="
0190                       << this << ") at: " << static_cast<void*>(m_data.data()));
0191 
0192     m_handler = other.m_handler;
0193     copyConstruct(other);
0194   }
0195 
0196   AnyBase& operator=(const AnyBase& other) _ACTS_ANY_NOEXCEPT {
0197     _ACTS_ANY_VERBOSE("Copy assign (this="
0198                       << this << ") at: " << static_cast<void*>(m_data.data()));
0199 
0200     if (m_handler == nullptr && other.m_handler == nullptr) {
0201       // both are empty, noop
0202       return *this;
0203     }
0204 
0205     if (m_handler == other.m_handler) {
0206       // same type, but checked before they're not both nullptr
0207       copy(std::move(other));
0208     } else {
0209       if (m_handler != nullptr) {
0210         // this object is not empty, but have different types => destroy
0211         destroy();
0212       }
0213       assert(m_handler == nullptr);
0214       m_handler = other.m_handler;
0215       copyConstruct(std::move(other));
0216     }
0217     return *this;
0218   }
0219 
0220   AnyBase(AnyBase&& other) _ACTS_ANY_NOEXCEPT {
0221     _ACTS_ANY_VERBOSE("Move construct (this="
0222                       << this << ") at: " << static_cast<void*>(m_data.data()));
0223     if (m_handler == nullptr && other.m_handler == nullptr) {
0224       // both are empty, noop
0225       return;
0226     }
0227 
0228     m_handler = other.m_handler;
0229     moveConstruct(std::move(other));
0230   }
0231 
0232   AnyBase& operator=(AnyBase&& other) _ACTS_ANY_NOEXCEPT {
0233     _ACTS_ANY_VERBOSE("Move assign (this="
0234                       << this << ") at: " << static_cast<void*>(m_data.data()));
0235     if (m_handler == nullptr && other.m_handler == nullptr) {
0236       // both are empty, noop
0237       return *this;
0238     }
0239 
0240     if (m_handler == other.m_handler) {
0241       // same type, but checked before they're not both nullptr
0242       move(std::move(other));
0243     } else {
0244       if (m_handler != nullptr) {
0245         // this object is not empty, but have different types => destroy
0246         destroy();
0247       }
0248       assert(m_handler == nullptr);
0249       m_handler = other.m_handler;
0250       moveConstruct(std::move(other));
0251     }
0252 
0253     return *this;
0254   }
0255 
0256   explicit operator bool() const { return m_handler != nullptr; }
0257 
0258  private:
0259   void* dataPtr() {
0260     if (m_handler->heapAllocated) {
0261       return *reinterpret_cast<void**>(m_data.data());
0262     } else {
0263       return reinterpret_cast<void*>(m_data.data());
0264     }
0265   }
0266 
0267   void setDataPtr(void* ptr) { *reinterpret_cast<void**>(m_data.data()) = ptr; }
0268 
0269   const void* dataPtr() const {
0270     if (m_handler->heapAllocated) {
0271       return *reinterpret_cast<void* const*>(m_data.data());
0272     } else {
0273       return reinterpret_cast<const void*>(m_data.data());
0274     }
0275   }
0276 
0277   struct Handler {
0278     void (*destroy)(void* ptr) = nullptr;
0279     void (*moveConstruct)(void* from, void* to) = nullptr;
0280     void (*move)(void* from, void* to) = nullptr;
0281     void* (*copyConstruct)(const void* from, void* to) = nullptr;
0282     void (*copy)(const void* from, void* to) = nullptr;
0283     bool heapAllocated{false};
0284   };
0285 
0286   template <typename T>
0287   static const Handler* makeHandler() {
0288     static_assert(!std::is_same_v<T, AnyBase<SIZE>>, "Cannot wrap any in any");
0289     static const Handler static_handler = []() {
0290       Handler h;
0291       h.heapAllocated = heapAllocated<T>();
0292       if constexpr (!std::is_trivially_destructible_v<T> ||
0293                     heapAllocated<T>()) {
0294         h.destroy = &destroyImpl<T>;
0295       }
0296       if constexpr (!std::is_trivially_move_constructible_v<T> ||
0297                     heapAllocated<T>()) {
0298         h.moveConstruct = &moveConstructImpl<T>;
0299       }
0300       if constexpr (!std::is_trivially_move_assignable_v<T> ||
0301                     heapAllocated<T>()) {
0302         h.move = &moveImpl<T>;
0303       }
0304       if constexpr (!std::is_trivially_copy_constructible_v<T> ||
0305                     heapAllocated<T>()) {
0306         h.copyConstruct = &copyConstructImpl<T>;
0307       }
0308       if constexpr (!std::is_trivially_copy_assignable_v<T> ||
0309                     heapAllocated<T>()) {
0310         h.copy = &copyImpl<T>;
0311       }
0312 
0313       _ACTS_ANY_DEBUG("Type: " << typeid(T).name());
0314       _ACTS_ANY_DEBUG(" -> destroy: " << h.destroy);
0315       _ACTS_ANY_DEBUG(" -> moveConstruct: " << h.moveConstruct);
0316       _ACTS_ANY_DEBUG(" -> move: " << h.move);
0317       _ACTS_ANY_DEBUG(" -> copyConstruct: " << h.copyConstruct);
0318       _ACTS_ANY_DEBUG(" -> copy: " << h.copy);
0319       _ACTS_ANY_DEBUG(
0320           " -> heapAllocated: " << (h.heapAllocated ? "yes" : "no"));
0321 
0322       return h;
0323     }();
0324     return &static_handler;
0325   }
0326 
0327   template <typename T>
0328   static constexpr bool heapAllocated() {
0329     return sizeof(T) > SIZE;
0330   }
0331 
0332   void destroy() {
0333     _ACTS_ANY_VERBOSE("Destructor this=" << this << " handler: " << m_handler);
0334     if (m_handler != nullptr && m_handler->destroy != nullptr) {
0335       _ACTS_ANY_VERBOSE("Non-trivial destruction");
0336       m_handler->destroy(dataPtr());
0337     }
0338     m_handler = nullptr;
0339   }
0340 
0341   void moveConstruct(AnyBase&& fromAny) {
0342     if (m_handler == nullptr) {
0343       return;
0344     }
0345 
0346     void* to = dataPtr();
0347     void* from = fromAny.dataPtr();
0348     if (m_handler->heapAllocated) {
0349       // stored on heap: just copy the pointer
0350       setDataPtr(fromAny.dataPtr());
0351       // do not delete in moved-from any
0352       fromAny.m_handler = nullptr;
0353       return;
0354     }
0355 
0356     if (m_handler->moveConstruct == nullptr) {
0357       _ACTS_ANY_VERBOSE("Trivially move construct");
0358       // trivially move constructible
0359       m_data = std::move(fromAny.m_data);
0360     } else {
0361       m_handler->moveConstruct(from, to);
0362     }
0363   }
0364 
0365   void move(AnyBase&& fromAny) {
0366     if (m_handler == nullptr) {
0367       return;
0368     }
0369 
0370     void* to = dataPtr();
0371     void* from = fromAny.dataPtr();
0372     if (m_handler->heapAllocated) {
0373       // stored on heap: just copy the pointer
0374       // need to delete existing pointer
0375       m_handler->destroy(dataPtr());
0376       setDataPtr(fromAny.dataPtr());
0377       // do not delete in moved-from any
0378       fromAny.m_handler = nullptr;
0379       return;
0380     }
0381 
0382     if (m_handler->move == nullptr) {
0383       _ACTS_ANY_VERBOSE("Trivially move");
0384       // trivially movable
0385       m_data = std::move(fromAny.m_data);
0386     } else {
0387       m_handler->move(from, to);
0388     }
0389   }
0390 
0391   void copyConstruct(const AnyBase& fromAny) {
0392     if (m_handler == nullptr) {
0393       return;
0394     }
0395 
0396     void* to = dataPtr();
0397     const void* from = fromAny.dataPtr();
0398 
0399     if (m_handler->copyConstruct == nullptr) {
0400       _ACTS_ANY_VERBOSE("Trivially copy construct");
0401       // trivially copy constructible
0402       m_data = fromAny.m_data;
0403     } else {
0404       void* copyAt = m_handler->copyConstruct(from, to);
0405       if (to == nullptr) {
0406         assert(copyAt != nullptr);
0407         // copy allocated, store pointer
0408         setDataPtr(copyAt);
0409       }
0410     }
0411   }
0412 
0413   void copy(const AnyBase& fromAny) {
0414     if (m_handler == nullptr) {
0415       return;
0416     }
0417 
0418     void* to = dataPtr();
0419     const void* from = fromAny.dataPtr();
0420 
0421     if (m_handler->copy == nullptr) {
0422       _ACTS_ANY_VERBOSE("Trivially copy");
0423       // trivially copyable
0424       m_data = fromAny.m_data;
0425     } else {
0426       m_handler->copy(from, to);
0427     }
0428   }
0429 
0430   template <typename T>
0431   static void destroyImpl(void* ptr) {
0432     assert(ptr != nullptr && "Address to destroy is nullptr");
0433     T* obj = static_cast<T*>(ptr);
0434     if constexpr (!heapAllocated<T>()) {
0435       // stored in place: just call the destructor
0436       _ACTS_ANY_VERBOSE("Destroy local at: " << ptr);
0437       obj->~T();
0438     } else {
0439       // stored on heap: delete
0440       _ACTS_ANY_DEBUG("Delete type: " << typeid(T).name()
0441                                       << " heap at: " << obj);
0442       _ACTS_ANY_TRACK_DEALLOCATION(T, obj);
0443       delete obj;
0444     }
0445   }
0446 
0447   template <typename T>
0448   static void moveConstructImpl(void* from, void* to) {
0449     _ACTS_ANY_VERBOSE("move const: " << from << " -> " << to);
0450     assert(from != nullptr && "Source is null");
0451     assert(to != nullptr && "Target is null");
0452     T* _from = static_cast<T*>(from);
0453     /*T* ptr =*/new (to) T(std::move(*_from));
0454   }
0455 
0456   template <typename T>
0457   static void moveImpl(void* from, void* to) {
0458     _ACTS_ANY_VERBOSE("move: " << from << " -> " << to);
0459     assert(from != nullptr && "Source is null");
0460     assert(to != nullptr && "Target is null");
0461 
0462     T* _from = static_cast<T*>(from);
0463     T* _to = static_cast<T*>(to);
0464 
0465     (*_to) = std::move(*_from);
0466   }
0467 
0468   template <typename T>
0469   static void* copyConstructImpl(const void* from, void* to) {
0470     _ACTS_ANY_VERBOSE("copy const: " << from << " -> " << to);
0471     assert(from != nullptr && "Source is null");
0472     const T* _from = static_cast<const T*>(from);
0473     if (to == nullptr) {
0474       assert(heapAllocated<T>() && "Received nullptr in local buffer case");
0475       to = new T(*_from);
0476       _ACTS_ANY_TRACK_ALLOCATION(T, to);
0477 
0478     } else {
0479       assert(!heapAllocated<T>() && "Received non-nullptr in heap case");
0480       /*T* ptr =*/new (to) T(*_from);
0481     }
0482     return to;
0483   }
0484 
0485   template <typename T>
0486   static void copyImpl(const void* from, void* to) {
0487     _ACTS_ANY_VERBOSE("copy: " << from << " -> " << to);
0488     assert(from != nullptr && "Source is null");
0489     assert(to != nullptr && "Target is null");
0490 
0491     const T* _from = static_cast<const T*>(from);
0492     T* _to = static_cast<T*>(to);
0493 
0494     (*_to) = *_from;
0495   }
0496 
0497   static constexpr std::size_t kMaxAlignment =
0498       std::max(alignof(std::max_align_t),
0499 #if defined(__AVX512F__)
0500                std::size_t{64}
0501 #elif defined(__AVX__)
0502                std::size_t{32}
0503 #elif defined(__SSE__)
0504                std::size_t{16}
0505 #else
0506                std::size_t{0}
0507   // Neutral element
0508   // for maximum
0509 #endif
0510       );
0511 
0512   alignas(kMaxAlignment) std::array<std::byte, SIZE> m_data{};
0513   const Handler* m_handler{nullptr};
0514 };
0515 
0516 using Any = AnyBase<sizeof(void*)>;
0517 
0518 #undef _ACTS_ANY_VERBOSE
0519 #undef _ACTS_ANY_VERBOSE_BUFFER
0520 #undef _ACTS_ANY_ENABLE_VERBOSE
0521 
0522 }  // namespace Acts