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