Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-06 08:10:15

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