File indexing completed on 2026-04-13 07:34:46
0001
0002
0003
0004
0005
0006
0007
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
0023
0024
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
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 }
0114
0115
0116
0117
0118
0119 class AnyBaseAll {};
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149
0150
0151 template <std::size_t sb_size, bool copyable = true>
0152 class AnyBase : public AnyBaseAll {
0153 static_assert(sizeof(void*) <= sb_size, "Size is too small for a pointer");
0154
0155
0156
0157
0158
0159 template <typename U>
0160 static constexpr bool isStorable() {
0161 if constexpr (std::is_base_of_v<AnyBaseAll, U>) {
0162 return false;
0163 } else if constexpr (copyable) {
0164 return std::is_copy_assignable_v<U> && std::is_copy_constructible_v<U> &&
0165 (sizeof(U) > sb_size || (std::is_move_assignable_v<U> &&
0166 std::is_move_constructible_v<U>));
0167 } else {
0168 return std::is_move_constructible_v<U> &&
0169 (sizeof(U) > sb_size || std::is_move_assignable_v<U>);
0170 }
0171 }
0172
0173 public:
0174
0175
0176
0177
0178 template <typename T, typename... Args>
0179 requires(isStorable<std::decay_t<T>>())
0180 explicit AnyBase(std::in_place_type_t<T> , Args&&... args) {
0181 using U = std::decay_t<T>;
0182 m_handler = makeHandler<U>();
0183 constructValue<U>(std::forward<Args>(args)...);
0184 }
0185
0186 #if defined(_ACTS_ANY_ENABLE_VERBOSE)
0187 AnyBase() { _ACTS_ANY_VERBOSE("Default construct this=" << this); };
0188 #else
0189 AnyBase() = default;
0190 #endif
0191
0192
0193
0194
0195 template <typename T>
0196 explicit AnyBase(T&& value) noexcept(detail::kAnyNoexcept)
0197 requires(isStorable<std::decay_t<T>>())
0198 : AnyBase{std::in_place_type<T>, std::forward<T>(value)} {}
0199
0200
0201
0202
0203
0204
0205 template <typename T, typename... Args>
0206 requires(isStorable<std::decay_t<T>>())
0207 T& emplace(Args&&... args) {
0208 using U = std::decay_t<T>;
0209 destroy();
0210 m_handler = makeHandler<U>();
0211 return *constructValue<U>(std::forward<Args>(args)...);
0212 }
0213
0214
0215
0216
0217
0218 template <typename T>
0219 T& as() {
0220 static_assert(std::is_same_v<T, std::decay_t<T>>,
0221 "Please pass the raw type, no const or ref");
0222 if (m_handler == nullptr || m_handler->typeHash != typeHash<T>()) {
0223 throw std::bad_any_cast{};
0224 }
0225
0226 _ACTS_ANY_VERBOSE("Get as "
0227 << (m_handler->heapAllocated ? "heap" : "local"));
0228
0229 return *std::bit_cast<T*>(dataPtr());
0230 }
0231
0232
0233
0234
0235
0236 template <typename T>
0237 const T& as() const {
0238 static_assert(std::is_same_v<T, std::decay_t<T>>,
0239 "Please pass the raw type, no const or ref");
0240 if (m_handler == nullptr || m_handler->typeHash != typeHash<T>()) {
0241 throw std::bad_any_cast{};
0242 }
0243
0244 _ACTS_ANY_VERBOSE("Get as "
0245 << (m_handler->heapAllocated ? "heap" : "local"));
0246
0247 return *std::bit_cast<const T*>(dataPtr());
0248 }
0249
0250
0251
0252
0253
0254 template <typename T>
0255 T* asPtr() {
0256 static_assert(std::is_same_v<T, std::decay_t<T>>,
0257 "Please pass the raw type, no const or ref");
0258 if (m_handler == nullptr || m_handler->typeHash != typeHash<T>()) {
0259 return nullptr;
0260 }
0261 return std::bit_cast<T*>(dataPtr());
0262 }
0263
0264
0265
0266
0267
0268 template <typename T>
0269 const T* asPtr() const {
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<const T*>(dataPtr());
0276 }
0277
0278
0279
0280
0281
0282 template <typename T>
0283 T take() {
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 throw std::bad_any_cast{};
0288 }
0289 T* ptr = std::bit_cast<T*>(dataPtr());
0290 T value = std::move(*ptr);
0291 destroy();
0292 return value;
0293 }
0294
0295 ~AnyBase() { destroy(); }
0296
0297
0298
0299 AnyBase(const AnyBase& other) noexcept(detail::kAnyNoexcept)
0300 requires copyable
0301 {
0302 if (m_handler == nullptr && other.m_handler == nullptr) {
0303
0304 return;
0305 }
0306
0307 _ACTS_ANY_VERBOSE("Copy construct (this="
0308 << this << ") at: " << static_cast<void*>(m_data.data()));
0309
0310 m_handler = other.m_handler;
0311 copyConstruct(other);
0312 }
0313
0314
0315 AnyBase(const AnyBase&)
0316 requires(!copyable)
0317 = delete;
0318
0319
0320
0321
0322 AnyBase& operator=(const AnyBase& other) noexcept(detail::kAnyNoexcept)
0323 requires copyable
0324 {
0325 _ACTS_ANY_VERBOSE("Copy assign (this="
0326 << this << ") at: " << static_cast<void*>(m_data.data()));
0327
0328 if (m_handler == nullptr && other.m_handler == nullptr) {
0329
0330 return *this;
0331 }
0332
0333 if (m_handler == other.m_handler) {
0334
0335 copy(other);
0336 } else {
0337 if (m_handler != nullptr) {
0338
0339 destroy();
0340 }
0341 assert(m_handler == nullptr);
0342 m_handler = other.m_handler;
0343 copyConstruct(other);
0344 }
0345 return *this;
0346 }
0347
0348
0349 AnyBase& operator=(const AnyBase&)
0350 requires(!copyable)
0351 = delete;
0352
0353
0354
0355 AnyBase(AnyBase&& other) noexcept(detail::kAnyNoexcept) {
0356 _ACTS_ANY_VERBOSE("Move construct (this="
0357 << this << ") at: " << static_cast<void*>(m_data.data()));
0358 if (m_handler == nullptr && other.m_handler == nullptr) {
0359
0360 return;
0361 }
0362
0363 m_handler = other.m_handler;
0364 moveConstruct(std::move(other));
0365 }
0366
0367
0368
0369
0370 AnyBase& operator=(AnyBase&& other) noexcept(detail::kAnyNoexcept) {
0371 _ACTS_ANY_VERBOSE("Move assign (this="
0372 << this << ") at: " << static_cast<void*>(m_data.data()));
0373 if (m_handler == nullptr && other.m_handler == nullptr) {
0374
0375 return *this;
0376 }
0377
0378
0379
0380 if (m_handler == other.m_handler &&
0381 m_handler->typeHash == other.m_handler->typeHash) {
0382
0383 move(std::move(other));
0384 } else {
0385 if (m_handler != nullptr) {
0386
0387 destroy();
0388 }
0389 assert(m_handler == nullptr);
0390 m_handler = other.m_handler;
0391 moveConstruct(std::move(other));
0392 }
0393
0394 return *this;
0395 }
0396
0397
0398
0399 explicit operator bool() const { return m_handler != nullptr; }
0400
0401
0402
0403 const std::type_info* typeInfo() const {
0404 return m_handler != nullptr ? m_handler->typeInfo : nullptr;
0405 }
0406
0407 private:
0408 void* dataPtr() {
0409 if (m_handler->heapAllocated) {
0410 return *std::bit_cast<void**>(m_data.data());
0411 } else {
0412 return std::bit_cast<void*>(m_data.data());
0413 }
0414 }
0415
0416 void setDataPtr(void* ptr) { *std::bit_cast<void**>(m_data.data()) = ptr; }
0417
0418 const void* dataPtr() const {
0419 if (m_handler->heapAllocated) {
0420 return *std::bit_cast<void* const*>(m_data.data());
0421 } else {
0422 return std::bit_cast<const void*>(m_data.data());
0423 }
0424 }
0425
0426 struct Handler {
0427 void (*destroy)(void* ptr) = nullptr;
0428 void (*moveConstruct)(void* from, void* to) = nullptr;
0429 void (*move)(void* from, void* to) = nullptr;
0430 void* (*copyConstruct)(const void* from, void* to) = nullptr;
0431 void (*copy)(const void* from, void* to) = nullptr;
0432 bool heapAllocated{false};
0433 std::uint64_t typeHash{0};
0434 const std::type_info* typeInfo{nullptr};
0435 };
0436
0437 template <typename T>
0438 static const Handler* makeHandler() {
0439 static_assert(!std::is_base_of_v<AnyBaseAll, std::decay_t<T>>,
0440 "Cannot wrap Any in Any");
0441 static const Handler static_handler = []() {
0442 Handler h;
0443 h.heapAllocated = heapAllocated<T>();
0444 if constexpr (!std::is_trivially_destructible_v<T> ||
0445 heapAllocated<T>()) {
0446 h.destroy = &destroyImpl<T>;
0447 }
0448 if constexpr (!heapAllocated<T>() &&
0449 !std::is_trivially_move_constructible_v<T>) {
0450 h.moveConstruct = &moveConstructImpl<T>;
0451 }
0452 if constexpr (!heapAllocated<T>() &&
0453 !std::is_trivially_move_assignable_v<T>) {
0454 h.move = &moveImpl<T>;
0455 }
0456 if constexpr (std::is_copy_constructible_v<T> &&
0457 (!std::is_trivially_copy_constructible_v<T> ||
0458 heapAllocated<T>())) {
0459 h.copyConstruct = ©ConstructImpl<T>;
0460 }
0461
0462 if constexpr (std::is_copy_assignable_v<T> &&
0463 (!std::is_trivially_copy_assignable_v<T> ||
0464 heapAllocated<T>())) {
0465 h.copy = ©Impl<T>;
0466 }
0467
0468 h.typeHash = typeHash<T>();
0469 h.typeInfo = &typeid(T);
0470
0471 _ACTS_ANY_DEBUG("Type: " << typeid(T).name());
0472 _ACTS_ANY_DEBUG(" -> destroy: " << h.destroy);
0473 _ACTS_ANY_DEBUG(" -> moveConstruct: " << h.moveConstruct);
0474 _ACTS_ANY_DEBUG(" -> move: " << h.move);
0475 _ACTS_ANY_DEBUG(" -> copyConstruct: " << h.copyConstruct);
0476 _ACTS_ANY_DEBUG(" -> copy: " << h.copy);
0477 _ACTS_ANY_DEBUG(
0478 " -> heapAllocated: " << (h.heapAllocated ? "yes" : "no"));
0479
0480 return h;
0481 }();
0482 return &static_handler;
0483 }
0484
0485 template <typename T>
0486 static constexpr bool heapAllocated() {
0487 return sizeof(T) > sb_size;
0488 }
0489
0490 template <typename T, typename... Args>
0491 T* constructValue(Args&&... args) {
0492 if constexpr (!heapAllocated<T>()) {
0493
0494 auto* ptr = new (m_data.data()) T(std::forward<Args>(args)...);
0495 _ACTS_ANY_VERBOSE("Construct local (this="
0496 << this
0497 << ") at: " << static_cast<void*>(m_data.data()));
0498 return ptr;
0499 } else {
0500
0501 auto* heap = new T(std::forward<Args>(args)...);
0502 _ACTS_ANY_DEBUG("Allocate type: " << typeid(T).name() << " at " << heap);
0503 _ACTS_ANY_TRACK_ALLOCATION(T, heap);
0504 setDataPtr(heap);
0505 return heap;
0506 }
0507 }
0508
0509 void destroy() {
0510 _ACTS_ANY_VERBOSE("Destructor this=" << this << " handler: " << m_handler);
0511 if (m_handler != nullptr && m_handler->destroy != nullptr) {
0512 _ACTS_ANY_VERBOSE("Non-trivial destruction");
0513 m_handler->destroy(dataPtr());
0514 }
0515 m_handler = nullptr;
0516 }
0517
0518 void moveConstruct(AnyBase&& fromAny) {
0519 if (m_handler == nullptr) {
0520 return;
0521 }
0522
0523 void* to = dataPtr();
0524 void* from = fromAny.dataPtr();
0525 if (m_handler->heapAllocated) {
0526
0527 setDataPtr(fromAny.dataPtr());
0528
0529 fromAny.m_handler = nullptr;
0530 return;
0531 }
0532
0533 if (m_handler->moveConstruct == nullptr) {
0534 _ACTS_ANY_VERBOSE("Trivially move construct");
0535
0536 m_data = std::move(fromAny.m_data);
0537 } else {
0538 m_handler->moveConstruct(from, to);
0539 }
0540 }
0541
0542 void move(AnyBase&& fromAny) {
0543 if (m_handler == nullptr) {
0544 return;
0545 }
0546
0547 void* to = dataPtr();
0548 void* from = fromAny.dataPtr();
0549 if (m_handler->heapAllocated) {
0550
0551
0552 m_handler->destroy(dataPtr());
0553 setDataPtr(fromAny.dataPtr());
0554
0555 fromAny.m_handler = nullptr;
0556 return;
0557 }
0558
0559 if (m_handler->move == nullptr) {
0560 _ACTS_ANY_VERBOSE("Trivially move");
0561
0562 m_data = std::move(fromAny.m_data);
0563 } else {
0564 m_handler->move(from, to);
0565 }
0566 }
0567
0568 void copyConstruct(const AnyBase& fromAny) {
0569 if (m_handler == nullptr) {
0570 return;
0571 }
0572
0573 void* to = dataPtr();
0574 const void* from = fromAny.dataPtr();
0575
0576 if (m_handler->copyConstruct == nullptr) {
0577 _ACTS_ANY_VERBOSE("Trivially copy construct");
0578
0579 m_data = fromAny.m_data;
0580 } else {
0581 void* copyAt = m_handler->copyConstruct(from, to);
0582 if (to == nullptr) {
0583 assert(copyAt != nullptr);
0584
0585 setDataPtr(copyAt);
0586 }
0587 }
0588 }
0589
0590 void copy(const AnyBase& fromAny) {
0591 if (m_handler == nullptr) {
0592 return;
0593 }
0594
0595 void* to = dataPtr();
0596 const void* from = fromAny.dataPtr();
0597
0598 if (m_handler->copy == nullptr) {
0599 _ACTS_ANY_VERBOSE("Trivially copy");
0600
0601 m_data = fromAny.m_data;
0602 } else {
0603 m_handler->copy(from, to);
0604 }
0605 }
0606
0607 template <typename T>
0608 static void destroyImpl(void* ptr) {
0609 assert(ptr != nullptr && "Address to destroy is nullptr");
0610 auto* obj = static_cast<T*>(ptr);
0611 if constexpr (!heapAllocated<T>()) {
0612
0613 _ACTS_ANY_VERBOSE("Destroy local at: " << ptr);
0614 obj->~T();
0615 } else {
0616
0617 _ACTS_ANY_DEBUG("Delete type: " << typeid(T).name()
0618 << " heap at: " << obj);
0619 _ACTS_ANY_TRACK_DEALLOCATION(T, obj);
0620 delete obj;
0621 }
0622 }
0623
0624 template <typename T>
0625 static void moveConstructImpl(void* from, void* to) {
0626 _ACTS_ANY_VERBOSE("move const: " << from << " -> " << to);
0627 assert(from != nullptr && "Source is null");
0628 assert(to != nullptr && "Target is null");
0629 auto* _from = static_cast<T*>(from);
0630 new (to) T(std::move(*_from));
0631 }
0632
0633 template <typename T>
0634 static void moveImpl(void* from, void* to) {
0635 _ACTS_ANY_VERBOSE("move: " << from << " -> " << to);
0636 assert(from != nullptr && "Source is null");
0637 assert(to != nullptr && "Target is null");
0638
0639 auto* _from = static_cast<T*>(from);
0640 auto* _to = static_cast<T*>(to);
0641
0642 (*_to) = std::move(*_from);
0643 }
0644
0645 template <typename T>
0646 static void* copyConstructImpl(const void* from, void* to) {
0647 _ACTS_ANY_VERBOSE("copy const: " << from << " -> " << to);
0648 assert(from != nullptr && "Source is null");
0649 const auto* _from = static_cast<const T*>(from);
0650 if (to == nullptr) {
0651 assert(heapAllocated<T>() && "Received nullptr in local buffer case");
0652 to = new T(*_from);
0653 _ACTS_ANY_TRACK_ALLOCATION(T, to);
0654
0655 } else {
0656 assert(!heapAllocated<T>() && "Received non-nullptr in heap case");
0657 new (to) T(*_from);
0658 }
0659 return to;
0660 }
0661
0662 template <typename T>
0663 static void copyImpl(const void* from, void* to) {
0664 _ACTS_ANY_VERBOSE("copy: " << from << " -> " << to);
0665 assert(from != nullptr && "Source is null");
0666 assert(to != nullptr && "Target is null");
0667
0668 const auto* _from = static_cast<const T*>(from);
0669 auto* _to = static_cast<T*>(to);
0670
0671 (*_to) = *_from;
0672 }
0673
0674 static constexpr std::size_t kMaxAlignment =
0675 std::max(alignof(std::max_align_t),
0676 #if defined(__AVX512F__)
0677 std::size_t{64}
0678 #elif defined(__AVX__)
0679 std::size_t{32}
0680 #elif defined(__SSE__)
0681 std::size_t{16}
0682 #else
0683 std::size_t{0}
0684
0685
0686 #endif
0687 );
0688
0689 alignas(kMaxAlignment) std::array<std::byte, sb_size> m_data{};
0690 const Handler* m_handler{nullptr};
0691 };
0692
0693
0694
0695
0696
0697 using Any = AnyBase<sizeof(void*), true>;
0698
0699
0700
0701
0702 using AnyMoveOnly = AnyBase<sizeof(void*), false>;
0703
0704
0705
0706 #undef _ACTS_ANY_VERBOSE
0707 #undef _ACTS_ANY_VERBOSE_BUFFER
0708 #undef _ACTS_ANY_ENABLE_VERBOSE
0709
0710 }