Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:11:08

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