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