File indexing completed on 2025-08-06 08:10:15
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 <memory>
0016 #include <utility>
0017
0018
0019
0020
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
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> , 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
0127 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
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
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
0201 return *this;
0202 }
0203
0204 if (m_handler == nullptr) {
0205 m_handler = other.m_handler;
0206 copyConstruct(other);
0207 } else {
0208
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
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
0234 return *this;
0235 }
0236
0237 if (m_handler == nullptr) {
0238 m_handler = other.m_handler;
0239 moveConstruct(std::move(other));
0240 } else {
0241
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 = ©ConstructImpl<T>;
0305 }
0306 if constexpr (!std::is_trivially_copy_assignable_v<T> ||
0307 heapAllocated<T>()) {
0308 h.copy = ©Impl<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
0347 setDataPtr(fromAny.dataPtr());
0348
0349 fromAny.m_handler = nullptr;
0350 return;
0351 }
0352
0353 if (m_handler->moveConstruct == nullptr) {
0354
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
0370
0371 m_handler->destroy(dataPtr());
0372 setDataPtr(fromAny.dataPtr());
0373
0374 fromAny.m_handler = nullptr;
0375 return;
0376 }
0377
0378 if (m_handler->move == nullptr) {
0379
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
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
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
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
0429 _ACTS_ANY_VERBOSE("Destroy local at: " << ptr);
0430 obj->~T();
0431 } else {
0432
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 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 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)
0500
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 }