Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-06-11 07:49:14

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 "Acts/Utilities/HashedString.hpp"
0012 
0013 #include <algorithm>
0014 #include <any>
0015 #include <array>
0016 #include <cassert>
0017 #include <cstddef>
0018 #include <type_traits>
0019 #include <typeinfo>
0020 #include <utility>
0021 
0022 // #define _ACTS_ANY_ENABLE_VERBOSE
0023 // #define _ACTS_ANY_ENABLE_DEBUG
0024 // #define _ACTS_ANY_ENABLE_TRACK_ALLOCATIONS
0025 
0026 #if defined(_ACTS_ANY_ENABLE_TRACK_ALLOCATIONS)
0027 #include <iostream>
0028 #include <mutex>
0029 #include <set>
0030 #include <typeindex>
0031 #endif
0032 
0033 #if defined(_ACTS_ANY_ENABLE_VERBOSE) || defined(_ACTS_ANY_ENABLE_DEBUG)
0034 #include <iomanip>
0035 #include <iostream>
0036 #endif
0037 
0038 #if defined(_ACTS_ANY_ENABLE_DEBUG)
0039 #define _ACTS_ANY_DEBUG(x) std::cout << x << std::endl;
0040 #else
0041 #define _ACTS_ANY_DEBUG(x)
0042 #endif
0043 
0044 #if defined(_ACTS_ANY_ENABLE_VERBOSE)
0045 #define _ACTS_ANY_VERBOSE(x) std::cout << x << std::endl;
0046 #define _ACTS_ANY_VERBOSE_BUFFER(s, b)              \
0047   do {                                              \
0048     std::cout << "" << s << ": 0x";                 \
0049     for (char c : b) {                              \
0050       std::cout << std::hex << static_cast<int>(c); \
0051     }                                               \
0052     std::cout << std::endl;                         \
0053   } while (0)
0054 #else
0055 #define _ACTS_ANY_VERBOSE(x)
0056 #define _ACTS_ANY_VERBOSE_BUFFER(s, b)
0057 #endif
0058 
0059 namespace Acts {
0060 
0061 namespace detail {
0062 #if defined(_ACTS_ANY_ENABLE_TRACK_ALLOCATIONS)
0063 static std::mutex _s_any_mutex;
0064 static std::set<std::pair<std::type_index, void*>> _s_any_allocations;
0065 
0066 #define _ACTS_ANY_TRACK_ALLOCATION(T, heap)                                  \
0067   do {                                                                       \
0068     std::lock_guard guard{detail::_s_any_mutex};                             \
0069     detail::_s_any_allocations.emplace(std::type_index(typeid(T)), heap);    \
0070     _ACTS_ANY_DEBUG("Allocate type: " << typeid(T).name() << " at " << heap) \
0071   } while (0)
0072 
0073 #define _ACTS_ANY_TRACK_DEALLOCATION(T, heap)                           \
0074   do {                                                                  \
0075     std::lock_guard guard{detail::_s_any_mutex};                        \
0076     auto it = detail::_s_any_allocations.find(                          \
0077         std::pair{std::type_index(typeid(T)), heap});                   \
0078     if (it == detail::_s_any_allocations.end()) {                       \
0079       throw std::runtime_error{                                         \
0080           "Trying to deallocate heap address that we didn't allocate"}; \
0081     }                                                                   \
0082     detail::_s_any_allocations.erase(it);                               \
0083   } while (0)
0084 
0085 // Do not make member functions noexcept in the debug case
0086 static constexpr bool kAnyNoexcept = false;
0087 
0088 struct _AnyAllocationReporter {
0089   static void checkAllocations() {
0090     std::lock_guard guard{detail::_s_any_mutex};
0091 
0092     if (!detail::_s_any_allocations.empty()) {
0093       std::cout << "Not all allocations have been released" << std::endl;
0094       for (const auto& [idx, addr] : detail::_s_any_allocations) {
0095         std::cout << "- " << idx.name() << ": " << addr << std::endl;
0096       }
0097       throw std::runtime_error{"AnyCheckAllocations failed"};
0098     }
0099   }
0100 
0101   ~_AnyAllocationReporter() noexcept { checkAllocations(); }
0102 };
0103 static _AnyAllocationReporter s_reporter;
0104 #else
0105 #define _ACTS_ANY_TRACK_ALLOCATION(T, heap) \
0106   do {                                      \
0107   } while (0)
0108 #define _ACTS_ANY_TRACK_DEALLOCATION(T, heap) \
0109   do {                                        \
0110   } while (0)
0111 static constexpr bool kAnyNoexcept = true;
0112 #endif
0113 }  // namespace detail
0114 
0115 /// @addtogroup utilities
0116 /// @{
0117 
0118 /// Base class for all instances of @ref AnyBase regarfless of SBO size
0119 class AnyBaseAll {};
0120 
0121 /// Small opaque type-erased type with configurable small buffer optimization
0122 ///
0123 /// @tparam SbSize Size of the internal buffer for small buffer optimization
0124 /// @tparam Copyable If true, stored types must be copyable and AnyBase is
0125 ///         copyable. If false, move-only types are allowed and AnyBase is
0126 ///         move-only (copy constructor and copy assignment are deleted).
0127 ///
0128 /// @note
0129 /// Type requirements when Copyable is true:
0130 /// - All stored types must be copy constructible and copy assignable.
0131 /// - Types stored locally (`sizeof(T) <= SbSize`) must also be move
0132 /// constructible
0133 ///   and move assignable because local moves use move operations when not
0134 ///   trivially movable (trivial moves fall back to buffer copies).
0135 /// - Types stored on the heap (`sizeof(T) > SbSize`) are moved by stealing the
0136 ///   pointer, so no move operations are required in that case.
0137 ///
0138 /// @note
0139 /// Type requirements when Copyable is false:
0140 /// - All stored types must be move constructible.
0141 /// - Types stored locally must also be move assignable.
0142 /// - Heap-allocated types only need move constructible (pointer steal).
0143 ///
0144 /// @note
0145 /// In summary:
0146 /// - Local storage: values live inside the internal buffer; moves may invoke
0147 ///   move operations or buffer copies; copies use copy operations or buffer
0148 ///   copies when trivial.
0149 /// - Heap storage: values are allocated on the heap; moves transfer ownership
0150 ///   of the pointer; copies allocate and copy-construct the pointee.
0151 template <std::size_t SbSize, bool Copyable = true, typename Base = void>
0152 class AnyBase : public AnyBaseAll {
0153   static_assert(sizeof(void*) <= SbSize, "Size is too small for a pointer");
0154 
0155   /// Type trait: T is storable when Copyable requires copy+move, else
0156   /// move-only. When @c Base is not @c void, the type must additionally be
0157   /// convertible to @c Base* (i.e. publicly and unambiguously derived).
0158   /// @tparam U Type to check
0159   /// @return True if the type is storable, false otherwise
0160   template <typename U>
0161   static constexpr bool isStorable() {
0162     if constexpr (std::is_base_of_v<AnyBaseAll, U>) {
0163       return false;
0164     } else if constexpr (!std::is_void_v<Base> &&
0165                          !std::is_convertible_v<U*, Base*>) {
0166       return false;
0167     } else if constexpr (Copyable) {
0168       return std::is_copy_assignable_v<U> && std::is_copy_constructible_v<U> &&
0169              (sizeof(U) > SbSize || (std::is_move_assignable_v<U> &&
0170                                      std::is_move_constructible_v<U>));
0171     } else {
0172       return std::is_move_constructible_v<U> &&
0173              (sizeof(U) > SbSize || std::is_move_assignable_v<U>);
0174     }
0175   }
0176 
0177  public:
0178   /// Construct with in-place type construction
0179   /// @tparam T Type to construct
0180   /// @tparam Args Constructor argument types
0181   /// @param args Arguments to forward to T's constructor
0182   template <typename T, typename... Args>
0183     requires(isStorable<std::decay_t<T>>())
0184   explicit AnyBase(std::in_place_type_t<T> /*unused*/, Args&&... args) {
0185     using U = std::decay_t<T>;
0186     m_handler = makeHandler<U>();
0187     constructValue<U>(std::forward<Args>(args)...);
0188   }
0189 
0190 #if defined(_ACTS_ANY_ENABLE_VERBOSE)
0191   AnyBase() { _ACTS_ANY_VERBOSE("Default construct this=" << this); };
0192 #else
0193   AnyBase() = default;
0194 #endif
0195 
0196   /// Construct from any value type
0197   /// @tparam T Type of the value to store
0198   /// @param value Value to store in the Any
0199   /// @note Not noexcept: storing a type larger than the small buffer allocates
0200   ///       on the heap (may throw std::bad_alloc) and the stored type's own
0201   ///       constructor may throw.
0202   template <typename T>
0203   explicit AnyBase(T&& value)
0204     requires(!std::is_base_of_v<AnyBaseAll, std::decay_t<T>> &&
0205              isStorable<std::decay_t<T>>())
0206       : AnyBase{std::in_place_type<T>, std::forward<T>(value)} {}
0207 
0208   /// Construct a new value in place, destroying any existing value
0209   /// @tparam T Type to construct
0210   /// @tparam Args Constructor argument types
0211   /// @param args Arguments to forward to T's constructor
0212   /// @return Reference to the newly constructed value
0213   template <typename T, typename... Args>
0214     requires(isStorable<std::decay_t<T>>())
0215   T& emplace(Args&&... args) {
0216     using U = std::decay_t<T>;
0217     destroy();
0218     // Construct the new value before installing the handler. constructValue
0219     // does not depend on m_handler, so if the constructor throws this object is
0220     // left empty (m_handler == nullptr) rather than claiming to hold a value
0221     // whose storage was never set up, which would make the next destroy()
0222     // operate on stale/freed memory.
0223     U* ptr = constructValue<U>(std::forward<Args>(args)...);
0224     m_handler = makeHandler<U>();
0225     return *ptr;
0226   }
0227 
0228   /// Get reference to stored value of specified type
0229   /// @tparam T Type to retrieve (must be exact type, no const/ref)
0230   /// @return Reference to the stored value
0231   /// @throws std::bad_any_cast if stored type doesn't match T
0232   template <typename T>
0233   T& as() {
0234     static_assert(std::is_same_v<T, std::decay_t<T>>,
0235                   "Please pass the raw type, no const or ref");
0236     if (m_handler == nullptr || m_handler->typeHash != typeHash<T>()) {
0237       throw std::bad_any_cast{};
0238     }
0239 
0240     _ACTS_ANY_VERBOSE("Get as "
0241                       << (m_handler->heapAllocated ? "heap" : "local"));
0242 
0243     return *std::bit_cast<T*>(dataPtr());
0244   }
0245 
0246   /// Get const reference to stored value of specified type
0247   /// @tparam T Type to retrieve (must be exact type, no const/ref)
0248   /// @return Const reference to the stored value
0249   /// @throws std::bad_any_cast if stored type doesn't match T
0250   template <typename T>
0251   const T& as() const {
0252     static_assert(std::is_same_v<T, std::decay_t<T>>,
0253                   "Please pass the raw type, no const or ref");
0254     if (m_handler == nullptr || m_handler->typeHash != typeHash<T>()) {
0255       throw std::bad_any_cast{};
0256     }
0257 
0258     _ACTS_ANY_VERBOSE("Get as "
0259                       << (m_handler->heapAllocated ? "heap" : "local"));
0260 
0261     return *std::bit_cast<const T*>(dataPtr());
0262   }
0263 
0264   /// Get pointer to stored value of specified type
0265   /// @tparam T Type to retrieve (must be exact type, no const/ref)
0266   /// @return Pointer to the stored value, or nullptr if the type doesn't match
0267   ///         or the Any is empty
0268   template <typename T>
0269   T* asPtr() {
0270     static_assert(std::is_same_v<T, std::decay_t<T>>,
0271                   "Please pass the raw type, no const or ref");
0272     if (m_handler == nullptr || m_handler->typeHash != typeHash<T>()) {
0273       return nullptr;
0274     }
0275     return std::bit_cast<T*>(dataPtr());
0276   }
0277 
0278   /// Get const pointer to stored value of specified type
0279   /// @tparam T Type to retrieve (must be exact type, no const/ref)
0280   /// @return Const pointer to the stored value, or nullptr if the type doesn't
0281   ///         match or the Any is empty
0282   template <typename T>
0283   const T* asPtr() const {
0284     static_assert(std::is_same_v<T, std::decay_t<T>>,
0285                   "Please pass the raw type, no const or ref");
0286     if (m_handler == nullptr || m_handler->typeHash != typeHash<T>()) {
0287       return nullptr;
0288     }
0289     return std::bit_cast<const T*>(dataPtr());
0290   }
0291 
0292   /// Move the stored value out. Leaves this Any empty.
0293   /// @tparam T Type to retrieve (must be exact type, no const/ref)
0294   /// @return The moved-out value
0295   /// @throws std::bad_any_cast if stored type doesn't match T or Any is empty
0296   template <typename T>
0297   T take() {
0298     static_assert(std::is_same_v<T, std::decay_t<T>>,
0299                   "Please pass the raw type, no const or ref");
0300     if (m_handler == nullptr || m_handler->typeHash != typeHash<T>()) {
0301       throw std::bad_any_cast{};
0302     }
0303     T* ptr = std::bit_cast<T*>(dataPtr());
0304     T value = std::move(*ptr);
0305     destroy();
0306     return value;
0307   }
0308 
0309   ~AnyBase() { destroy(); }
0310 
0311   /// Copy constructor (only when Copyable is true)
0312   /// @param other The AnyBase to copy from
0313   /// @note Not noexcept: copying a heap-allocated value allocates (may throw
0314   ///       std::bad_alloc) and the stored type's copy constructor may throw.
0315   AnyBase(const AnyBase& other)
0316     requires Copyable
0317   {
0318     if (m_handler == nullptr && other.m_handler == nullptr) {
0319       // both are empty, noop
0320       return;
0321     }
0322 
0323     _ACTS_ANY_VERBOSE("Copy construct (this="
0324                       << this << ") at: " << static_cast<void*>(m_data.data()));
0325 
0326     m_handler = other.m_handler;
0327     copyConstruct(other);
0328   }
0329 
0330   /// Copy constructor deleted when Copyable is false (move-only variant)
0331   AnyBase(const AnyBase&)
0332     requires(!Copyable)
0333   = delete;
0334 
0335   /// Copy assignment operator (only when Copyable is true)
0336   /// @param other The AnyBase to copy from
0337   /// @return Reference to this object
0338   /// @note Not noexcept: copying a heap-allocated value allocates (may throw
0339   ///       std::bad_alloc) and the stored type's copy operations may throw.
0340   AnyBase& operator=(const AnyBase& other)
0341     requires Copyable
0342   {
0343     _ACTS_ANY_VERBOSE("Copy assign (this="
0344                       << this << ") at: " << static_cast<void*>(m_data.data()));
0345 
0346     if (this == &other) {
0347       // self-assignment, noop
0348       return *this;
0349     }
0350 
0351     if (m_handler == nullptr && other.m_handler == nullptr) {
0352       // both are empty, noop
0353       return *this;
0354     }
0355 
0356     if (m_handler == other.m_handler) {
0357       // same type, but checked before they're not both nullptr
0358       copy(other);
0359     } else {
0360       if (m_handler != nullptr) {
0361         // this object is not empty, but have different types => destroy
0362         destroy();
0363       }
0364       assert(m_handler == nullptr);
0365       m_handler = other.m_handler;
0366       try {
0367         copyConstruct(other);
0368       } catch (...) {
0369         // copyConstruct allocates and/or runs the stored type's copy
0370         // constructor, either of which may throw. If it does, no value was
0371         // established, so leave *this empty rather than with a handler that
0372         // claims a value that does not exist (which would make destruction
0373         // operate on uninitialized storage).
0374         m_handler = nullptr;
0375         throw;
0376       }
0377     }
0378     return *this;
0379   }
0380 
0381   /// Copy assignment deleted when Copyable is false (move-only variant)
0382   AnyBase& operator=(const AnyBase&)
0383     requires(!Copyable)
0384   = delete;
0385 
0386   /// Move constructor
0387   /// @param other The AnyBase to move from
0388   AnyBase(AnyBase&& other) noexcept(detail::kAnyNoexcept) {
0389     _ACTS_ANY_VERBOSE("Move construct (this="
0390                       << this << ") at: " << static_cast<void*>(m_data.data()));
0391     if (m_handler == nullptr && other.m_handler == nullptr) {
0392       // both are empty, noop
0393       return;
0394     }
0395 
0396     m_handler = other.m_handler;
0397     moveConstruct(std::move(other));
0398   }
0399 
0400   /// Move assignment operator
0401   /// @param other The AnyBase to move from
0402   /// @return Reference to this object
0403   AnyBase& operator=(AnyBase&& other) noexcept(detail::kAnyNoexcept) {
0404     _ACTS_ANY_VERBOSE("Move assign (this="
0405                       << this << ") at: " << static_cast<void*>(m_data.data()));
0406     if (this == &other) {
0407       // self-assignment, noop (avoids destroying our own value)
0408       return *this;
0409     }
0410 
0411     if (m_handler == nullptr && other.m_handler == nullptr) {
0412       // both are empty, noop
0413       return *this;
0414     }
0415 
0416     // At this point they can't be equal and nullptr, so it's safe to
0417     // dereference
0418     if (m_handler == other.m_handler &&
0419         m_handler->typeHash == other.m_handler->typeHash) {
0420       // same type, but checked before they're not both nullptr
0421       move(std::move(other));
0422     } else {
0423       if (m_handler != nullptr) {
0424         // this object is not empty, but have different types => destroy
0425         destroy();
0426       }
0427       assert(m_handler == nullptr);
0428       m_handler = other.m_handler;
0429       moveConstruct(std::move(other));
0430     }
0431 
0432     return *this;
0433   }
0434 
0435   /// Check if the AnyBase contains a value
0436   /// @return True if a value is stored, false if empty
0437   explicit operator bool() const { return m_handler != nullptr; }
0438 
0439   /// Type info of the stored value. Returns nullptr if empty.
0440   /// @return Pointer to the type info of the stored value, or nullptr if empty
0441   const std::type_info* typeInfo() const {
0442     return m_handler != nullptr ? m_handler->typeInfo : nullptr;
0443   }
0444 
0445   // The base accessors below are member templates on a dummy @c B defaulting to
0446   // @c Base. This keeps their return types (@c B& / @c B*) from being formed
0447   // when @c Base is @c void: the member template is only instantiated on use,
0448   // and the @c requires clause removes it entirely for the untyped variant. A
0449   // plain non-template member would try to form @c void& at class instantiation
0450   // and fail to compile.
0451 
0452   /// Get a pointer to the stored value as @c Base*, regardless of its concrete
0453   /// type. Only available when @c Base is not @c void.
0454   /// @return Pointer to the stored value upcast to @c Base*, or nullptr if empty
0455   template <typename B = Base>
0456     requires(!std::is_void_v<B>)
0457   B* asBase() {
0458     if (m_handler == nullptr) {
0459       return nullptr;
0460     }
0461     return m_handler->upcast(dataPtr());
0462   }
0463 
0464   /// Get a const pointer to the stored value as @c Base*. Only available when
0465   /// @c Base is not @c void.
0466   /// @return Const pointer to the stored value upcast to @c Base*, or nullptr if empty
0467   template <typename B = Base>
0468     requires(!std::is_void_v<B>)
0469   const B* asBase() const {
0470     if (m_handler == nullptr) {
0471       return nullptr;
0472     }
0473     return m_handler->upcastConst(dataPtr());
0474   }
0475 
0476   /// Dereference to the stored value as @c Base&. Only available when @c Base
0477   /// is not @c void.
0478   /// @return Reference to the stored value upcast to @c Base&
0479   template <typename B = Base>
0480     requires(!std::is_void_v<B>)
0481   B& operator*() {
0482     assert(m_handler != nullptr && "operator* on empty AnyBase");
0483     return *m_handler->upcast(dataPtr());
0484   }
0485 
0486   /// Dereference to the stored value as @c const @c Base&. Only available when
0487   /// @c Base is not @c void.
0488   /// @return Const reference to the stored value upcast to @c Base&
0489   template <typename B = Base>
0490     requires(!std::is_void_v<B>)
0491   const B& operator*() const {
0492     assert(m_handler != nullptr && "operator* on empty AnyBase");
0493     return *m_handler->upcastConst(dataPtr());
0494   }
0495 
0496   /// Member access on the stored value as @c Base*. Only available when
0497   /// @c Base is not @c void.
0498   /// @return Pointer to the stored value upcast to @c Base*
0499   template <typename B = Base>
0500     requires(!std::is_void_v<B>)
0501   B* operator->() {
0502     assert(m_handler != nullptr && "operator-> on empty AnyBase");
0503     return m_handler->upcast(dataPtr());
0504   }
0505 
0506   /// Member access on the stored value as @c const @c Base*. Only available
0507   /// when @c Base is not @c void.
0508   /// @return Const pointer to the stored value upcast to @c Base*
0509   template <typename B = Base>
0510     requires(!std::is_void_v<B>)
0511   const B* operator->() const {
0512     assert(m_handler != nullptr && "operator-> on empty AnyBase");
0513     return m_handler->upcastConst(dataPtr());
0514   }
0515 
0516  private:
0517   void* dataPtr() {
0518     if (m_handler->heapAllocated) {
0519       return *std::bit_cast<void**>(m_data.data());
0520     } else {
0521       return std::bit_cast<void*>(m_data.data());
0522     }
0523   }
0524 
0525   void setDataPtr(void* ptr) { *std::bit_cast<void**>(m_data.data()) = ptr; }
0526 
0527   const void* dataPtr() const {
0528     if (m_handler->heapAllocated) {
0529       return *std::bit_cast<void* const*>(m_data.data());
0530     } else {
0531       return std::bit_cast<const void*>(m_data.data());
0532     }
0533   }
0534 
0535   struct Handler {
0536     // Maps the stored payload to Base*. Collapses to void*(*)(void*) and stays
0537     // nullptr (never read) when Base == void.
0538     Base* (*upcast)(void*) = nullptr;
0539     // Const-qualified counterpart used by the const accessors so they don't
0540     // have to cast away const on the data pointer. Same as above when
0541     // Base == void.
0542     const Base* (*upcastConst)(const void*) = nullptr;
0543     void (*destroy)(void* ptr) = nullptr;
0544     void (*moveConstruct)(void* from, void* to) = nullptr;
0545     void (*move)(void* from, void* to) = nullptr;
0546     void* (*copyConstruct)(const void* from, void* to) = nullptr;
0547     void (*copy)(const void* from, void* to) = nullptr;
0548     bool heapAllocated{false};
0549     std::uint64_t typeHash{0};
0550     const std::type_info* typeInfo{nullptr};
0551   };
0552 
0553   template <typename T>
0554   static const Handler* makeHandler() {
0555     static_assert(!std::is_base_of_v<AnyBaseAll, std::decay_t<T>>,
0556                   "Cannot wrap Any in Any");
0557     static const Handler static_handler = []() {
0558       Handler h;
0559       h.heapAllocated = heapAllocated<T>();
0560       if constexpr (!std::is_trivially_destructible_v<T> ||
0561                     heapAllocated<T>()) {
0562         h.destroy = &destroyImpl<T>;
0563       }
0564       if constexpr (!heapAllocated<T>() &&
0565                     !std::is_trivially_move_constructible_v<T>) {
0566         h.moveConstruct = &moveConstructImpl<T>;
0567       }
0568       if constexpr (!heapAllocated<T>() &&
0569                     !std::is_trivially_move_assignable_v<T>) {
0570         h.move = &moveImpl<T>;
0571       }
0572       if constexpr (std::is_copy_constructible_v<T> &&
0573                     (!std::is_trivially_copy_constructible_v<T> ||
0574                      heapAllocated<T>())) {
0575         h.copyConstruct = &copyConstructImpl<T>;
0576       }
0577 
0578       if constexpr (std::is_copy_assignable_v<T> &&
0579                     (!std::is_trivially_copy_assignable_v<T> ||
0580                      heapAllocated<T>())) {
0581         h.copy = &copyImpl<T>;
0582       }
0583 
0584       if constexpr (!std::is_void_v<Base>) {
0585         h.upcast = [](void* p) -> Base* {
0586           return static_cast<Base*>(static_cast<T*>(p));
0587         };
0588         h.upcastConst = [](const void* p) -> const Base* {
0589           return static_cast<const Base*>(static_cast<const T*>(p));
0590         };
0591       }
0592 
0593       h.typeHash = typeHash<T>();
0594       h.typeInfo = &typeid(T);
0595 
0596       _ACTS_ANY_DEBUG("Type: " << typeid(T).name());
0597       _ACTS_ANY_DEBUG(" -> destroy: " << h.destroy);
0598       _ACTS_ANY_DEBUG(" -> moveConstruct: " << h.moveConstruct);
0599       _ACTS_ANY_DEBUG(" -> move: " << h.move);
0600       _ACTS_ANY_DEBUG(" -> copyConstruct: " << h.copyConstruct);
0601       _ACTS_ANY_DEBUG(" -> copy: " << h.copy);
0602       _ACTS_ANY_DEBUG(
0603           " -> heapAllocated: " << (h.heapAllocated ? "yes" : "no"));
0604 
0605       return h;
0606     }();
0607     return &static_handler;
0608   }
0609 
0610   template <typename T>
0611   static constexpr bool heapAllocated() {
0612     return sizeof(T) > SbSize;
0613   }
0614 
0615   template <typename T, typename... Args>
0616   T* constructValue(Args&&... args) {
0617     if constexpr (!heapAllocated<T>()) {
0618       // construct into local buffer
0619       auto* ptr = new (m_data.data()) T(std::forward<Args>(args)...);
0620       _ACTS_ANY_VERBOSE("Construct local (this="
0621                         << this
0622                         << ") at: " << static_cast<void*>(m_data.data()));
0623       return ptr;
0624     } else {
0625       // too large, heap allocate
0626       auto* heap = new T(std::forward<Args>(args)...);
0627       _ACTS_ANY_DEBUG("Allocate type: " << typeid(T).name() << " at " << heap);
0628       _ACTS_ANY_TRACK_ALLOCATION(T, heap);
0629       setDataPtr(heap);
0630       return heap;
0631     }
0632   }
0633 
0634   void destroy() {
0635     _ACTS_ANY_VERBOSE("Destructor this=" << this << " handler: " << m_handler);
0636     if (m_handler != nullptr && m_handler->destroy != nullptr) {
0637       _ACTS_ANY_VERBOSE("Non-trivial destruction");
0638       m_handler->destroy(dataPtr());
0639     }
0640     m_handler = nullptr;
0641   }
0642 
0643   void moveConstruct(AnyBase&& fromAny) {
0644     if (m_handler == nullptr) {
0645       return;
0646     }
0647 
0648     void* to = dataPtr();
0649     void* from = fromAny.dataPtr();
0650     if (m_handler->heapAllocated) {
0651       // stored on heap: just copy the pointer
0652       setDataPtr(fromAny.dataPtr());
0653       // do not delete in moved-from any
0654       fromAny.m_handler = nullptr;
0655       return;
0656     }
0657 
0658     if (m_handler->moveConstruct == nullptr) {
0659       _ACTS_ANY_VERBOSE("Trivially move construct");
0660       // trivially move constructible
0661       m_data = std::move(fromAny.m_data);
0662     } else {
0663       m_handler->moveConstruct(from, to);
0664     }
0665   }
0666 
0667   void move(AnyBase&& fromAny) {
0668     if (m_handler == nullptr) {
0669       return;
0670     }
0671 
0672     void* to = dataPtr();
0673     void* from = fromAny.dataPtr();
0674     if (m_handler->heapAllocated) {
0675       // stored on heap: just copy the pointer
0676       // need to delete existing pointer
0677       m_handler->destroy(dataPtr());
0678       setDataPtr(fromAny.dataPtr());
0679       // do not delete in moved-from any
0680       fromAny.m_handler = nullptr;
0681       return;
0682     }
0683 
0684     if (m_handler->move == nullptr) {
0685       _ACTS_ANY_VERBOSE("Trivially move");
0686       // trivially movable
0687       m_data = std::move(fromAny.m_data);
0688     } else {
0689       m_handler->move(from, to);
0690     }
0691   }
0692 
0693   void copyConstruct(const AnyBase& fromAny) {
0694     if (m_handler == nullptr) {
0695       return;
0696     }
0697 
0698     const void* from = fromAny.dataPtr();
0699 
0700     if (m_handler->copyConstruct == nullptr) {
0701       _ACTS_ANY_VERBOSE("Trivially copy construct");
0702       // trivially copy constructible
0703       m_data = fromAny.m_data;
0704     } else if (m_handler->heapAllocated) {
0705       // heap-allocated: always allocate fresh storage. We must not read the
0706       // current contents of m_data here: when copyConstruct runs as part of a
0707       // copy assignment with a type change, the previous value has already been
0708       // destroyed but m_data still holds its stale bytes (a freed pointer, or
0709       // the previous local value's bytes). Passing those as the destination
0710       // would placement-new into garbage. Allocate and store the new pointer.
0711       void* copyAt = m_handler->copyConstruct(from, nullptr);
0712       assert(copyAt != nullptr);
0713       setDataPtr(copyAt);
0714     } else {
0715       // local storage: construct into the internal buffer
0716       m_handler->copyConstruct(from, dataPtr());
0717     }
0718   }
0719 
0720   void copy(const AnyBase& fromAny) {
0721     if (m_handler == nullptr) {
0722       return;
0723     }
0724 
0725     void* to = dataPtr();
0726     const void* from = fromAny.dataPtr();
0727 
0728     if (m_handler->copy == nullptr) {
0729       _ACTS_ANY_VERBOSE("Trivially copy");
0730       // trivially copyable
0731       m_data = fromAny.m_data;
0732     } else {
0733       m_handler->copy(from, to);
0734     }
0735   }
0736 
0737   template <typename T>
0738   static void destroyImpl(void* ptr) {
0739     assert(ptr != nullptr && "Address to destroy is nullptr");
0740     auto* obj = static_cast<T*>(ptr);
0741     if constexpr (!heapAllocated<T>()) {
0742       // stored in place: just call the destructor
0743       _ACTS_ANY_VERBOSE("Destroy local at: " << ptr);
0744       obj->~T();
0745     } else {
0746       // stored on heap: delete
0747       _ACTS_ANY_DEBUG("Delete type: " << typeid(T).name()
0748                                       << " heap at: " << obj);
0749       _ACTS_ANY_TRACK_DEALLOCATION(T, obj);
0750       delete obj;
0751     }
0752   }
0753 
0754   template <typename T>
0755   static void moveConstructImpl(void* from, void* to) {
0756     _ACTS_ANY_VERBOSE("move const: " << from << " -> " << to);
0757     assert(from != nullptr && "Source is null");
0758     assert(to != nullptr && "Target is null");
0759     auto* _from = static_cast<T*>(from);
0760     /*T* ptr =*/new (to) T(std::move(*_from));
0761   }
0762 
0763   template <typename T>
0764   static void moveImpl(void* from, void* to) {
0765     _ACTS_ANY_VERBOSE("move: " << from << " -> " << to);
0766     assert(from != nullptr && "Source is null");
0767     assert(to != nullptr && "Target is null");
0768 
0769     auto* _from = static_cast<T*>(from);
0770     auto* _to = static_cast<T*>(to);
0771 
0772     (*_to) = std::move(*_from);
0773   }
0774 
0775   template <typename T>
0776   static void* copyConstructImpl(const void* from, void* to) {
0777     _ACTS_ANY_VERBOSE("copy const: " << from << " -> " << to);
0778     assert(from != nullptr && "Source is null");
0779     const auto* _from = static_cast<const T*>(from);
0780     if (to == nullptr) {
0781       assert(heapAllocated<T>() && "Received nullptr in local buffer case");
0782       to = new T(*_from);
0783       _ACTS_ANY_TRACK_ALLOCATION(T, to);
0784 
0785     } else {
0786       assert(!heapAllocated<T>() && "Received non-nullptr in heap case");
0787       /*T* ptr =*/new (to) T(*_from);
0788     }
0789     return to;
0790   }
0791 
0792   template <typename T>
0793   static void copyImpl(const void* from, void* to) {
0794     _ACTS_ANY_VERBOSE("copy: " << from << " -> " << to);
0795     assert(from != nullptr && "Source is null");
0796     assert(to != nullptr && "Target is null");
0797 
0798     const auto* _from = static_cast<const T*>(from);
0799     auto* _to = static_cast<T*>(to);
0800 
0801     (*_to) = *_from;
0802   }
0803 
0804   static constexpr std::size_t kMaxAlignment =
0805       std::max(alignof(std::max_align_t),
0806 #if defined(__AVX512F__)
0807                std::size_t{64}
0808 #elif defined(__AVX__)
0809                std::size_t{32}
0810 #elif defined(__SSE__)
0811                std::size_t{16}
0812 #else
0813                std::size_t{0}
0814   // Neutral element
0815   // for maximum
0816 #endif
0817       );
0818 
0819   alignas(kMaxAlignment) std::array<std::byte, SbSize> m_data{};
0820   const Handler* m_handler{nullptr};
0821 };
0822 
0823 /// @brief A type-safe container for single values of any type
0824 /// @details This is a custom implementation similar to `std::any` but optimized for small types
0825 ///          that can fit into a pointer-sized buffer. Values larger than a
0826 ///          pointer are stored on the heap.
0827 using Any = AnyBase<sizeof(void*), true>;
0828 
0829 /// @brief Move-only variant that can store move-only types (e.g. std::unique_ptr)
0830 /// @details Same as Any but copy constructor and copy assignment are deleted.
0831 ///          Use when storing types that are not copyable.
0832 using AnyMoveOnly = AnyBase<sizeof(void*), false>;
0833 
0834 /// @brief Typed type-erased value over a common polymorphic base
0835 /// @details Stores any value of a type @c T convertible to @c Base* (i.e.
0836 ///          publicly and unambiguously derived from @c Base) and hands it back
0837 ///          as @c Base& / @c Base* through @ref AnyBase::asBase,
0838 ///          @ref AnyBase::operator* and @ref AnyBase::operator-> without naming
0839 ///          @c T. The concrete type can still be recovered with
0840 ///          @ref AnyBase::as / @ref AnyBase::asPtr. Copyable; values up to
0841 ///          @c SbSize bytes are stored inline, larger ones on the heap.
0842 /// @tparam Base Common base class exposed by the accessors
0843 /// @tparam SbSize Size of the small-buffer-optimization storage
0844 template <typename Base, std::size_t SbSize = sizeof(void*)>
0845 using AnyOf = AnyBase<SbSize, true, Base>;
0846 
0847 /// @}
0848 
0849 #undef _ACTS_ANY_VERBOSE
0850 #undef _ACTS_ANY_VERBOSE_BUFFER
0851 #undef _ACTS_ANY_ENABLE_VERBOSE
0852 
0853 }  // namespace Acts