Warning, file /acts/Core/include/Acts/Utilities/Any.hpp was not indexed
or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include "Acts/Utilities/HashedString.hpp"
0012
0013 #include <any>
0014 #include <array>
0015 #include <cassert>
0016 #include <cstddef>
0017 #include <utility>
0018
0019
0020
0021
0022
0023 #if defined(_ACTS_ANY_ENABLE_TRACK_ALLOCATIONS)
0024 #include <iostream>
0025 #include <mutex>
0026 #include <set>
0027 #include <typeindex>
0028 #include <typeinfo>
0029 #endif
0030
0031 #if defined(_ACTS_ANY_ENABLE_VERBOSE) || defined(_ACTS_ANY_ENABLE_DEBUG)
0032 #include <iomanip>
0033 #include <iostream>
0034 #endif
0035
0036 #if defined(_ACTS_ANY_ENABLE_DEBUG)
0037 #define _ACTS_ANY_DEBUG(x) std::cout << x << std::endl;
0038 #else
0039 #define _ACTS_ANY_DEBUG(x)
0040 #endif
0041
0042 #if defined(_ACTS_ANY_ENABLE_VERBOSE)
0043 #define _ACTS_ANY_VERBOSE(x) std::cout << x << std::endl;
0044 #define _ACTS_ANY_VERBOSE_BUFFER(s, b) \
0045 do { \
0046 std::cout << "" << s << ": 0x"; \
0047 for (char c : b) { \
0048 std::cout << std::hex << static_cast<int>(c); \
0049 } \
0050 std::cout << std::endl; \
0051 } while (0)
0052 #else
0053 #define _ACTS_ANY_VERBOSE(x)
0054 #define _ACTS_ANY_VERBOSE_BUFFER(s, b)
0055 #endif
0056
0057 namespace Acts {
0058
0059 #if defined(_ACTS_ANY_ENABLE_TRACK_ALLOCATIONS)
0060 static std::mutex _s_any_mutex;
0061 static std::set<std::pair<std::type_index, void*>> _s_any_allocations;
0062
0063 #define _ACTS_ANY_TRACK_ALLOCATION(T, heap) \
0064 do { \
0065 std::lock_guard guard{_s_any_mutex}; \
0066 _s_any_allocations.emplace(std::type_index(typeid(T)), heap); \
0067 _ACTS_ANY_DEBUG("Allocate type: " << typeid(T).name() << " at " << heap) \
0068 } while (0)
0069
0070 #define _ACTS_ANY_TRACK_DEALLOCATION(T, heap) \
0071 do { \
0072 std::lock_guard guard{_s_any_mutex}; \
0073 auto it = \
0074 _s_any_allocations.find(std::pair{std::type_index(typeid(T)), heap}); \
0075 if (it == _s_any_allocations.end()) { \
0076 throw std::runtime_error{ \
0077 "Trying to deallocate heap address that we didn't allocate"}; \
0078 } \
0079 _s_any_allocations.erase(it); \
0080 } while (0)
0081
0082
0083 #define _ACTS_ANY_NOEXCEPT
0084
0085 struct _AnyAllocationReporter {
0086 static void checkAllocations() {
0087 std::lock_guard guard{_s_any_mutex};
0088
0089 if (!_s_any_allocations.empty()) {
0090 std::cout << "Not all allocations have been released" << std::endl;
0091 for (const auto& [idx, addr] : _s_any_allocations) {
0092 std::cout << "- " << idx.name() << ": " << addr << std::endl;
0093 }
0094 throw std::runtime_error{"AnyCheckAllocations failed"};
0095 }
0096 }
0097
0098 ~_AnyAllocationReporter() noexcept { checkAllocations(); }
0099 };
0100 static _AnyAllocationReporter s_reporter;
0101 #else
0102 #define _ACTS_ANY_TRACK_ALLOCATION(T, heap) \
0103 do { \
0104 } while (0)
0105 #define _ACTS_ANY_TRACK_DEALLOCATION(T, heap) \
0106 do { \
0107 } while (0)
0108 #define _ACTS_ANY_NOEXCEPT noexcept
0109 #endif
0110
0111 class AnyBaseAll {};
0112
0113
0114 template <std::size_t SIZE>
0115 class AnyBase : public AnyBaseAll {
0116 static_assert(sizeof(void*) <= SIZE, "Size is too small for a pointer");
0117
0118 public:
0119
0120
0121
0122
0123 template <typename T, typename... Args>
0124 explicit AnyBase(std::in_place_type_t<T> , Args&&... args) {
0125 using U = std::decay_t<T>;
0126 static_assert(
0127 std::is_move_assignable_v<U> && std::is_move_constructible_v<U>,
0128 "Type needs to be move assignable and move constructible");
0129 static_assert(
0130 std::is_copy_assignable_v<U> && std::is_copy_constructible_v<U>,
0131 "Type needs to be copy assignable and copy constructible");
0132
0133 m_handler = makeHandler<U>();
0134 if constexpr (!heapAllocated<U>()) {
0135
0136 new (m_data.data()) U(std::forward<Args>(args)...);
0137 _ACTS_ANY_VERBOSE("Construct local (this="
0138 << this
0139 << ") at: " << static_cast<void*>(m_data.data()));
0140 } else {
0141
0142 U* heap = new U(std::forward<Args>(args)...);
0143 _ACTS_ANY_DEBUG("Allocate type: " << typeid(U).name() << " at " << heap);
0144 _ACTS_ANY_TRACK_ALLOCATION(T, heap);
0145 setDataPtr(heap);
0146 }
0147 }
0148
0149 #if defined(_ACTS_ANY_ENABLE_VERBOSE)
0150 AnyBase() { _ACTS_ANY_VERBOSE("Default construct this=" << this); };
0151 #else
0152 AnyBase() = default;
0153 #endif
0154
0155
0156
0157
0158 template <typename T>
0159 explicit AnyBase(T&& value) _ACTS_ANY_NOEXCEPT
0160 requires(!std::same_as<std::decay_t<T>, AnyBase<SIZE>>)
0161 : AnyBase{std::in_place_type<T>, std::forward<T>(value)} {}
0162
0163
0164
0165
0166
0167 template <typename T>
0168 T& as() {
0169 static_assert(std::is_same_v<T, std::decay_t<T>>,
0170 "Please pass the raw type, no const or ref");
0171 if (m_handler == nullptr || m_handler->typeHash != typeHash<T>()) {
0172 throw std::bad_any_cast{};
0173 }
0174
0175 _ACTS_ANY_VERBOSE("Get as "
0176 << (m_handler->heapAllocated ? "heap" : "local"));
0177
0178 return *reinterpret_cast<T*>(dataPtr());
0179 }
0180
0181
0182
0183
0184
0185 template <typename T>
0186 const T& as() const {
0187 static_assert(std::is_same_v<T, std::decay_t<T>>,
0188 "Please pass the raw type, no const or ref");
0189 if (m_handler == nullptr || m_handler->typeHash != typeHash<T>()) {
0190 throw std::bad_any_cast{};
0191 }
0192
0193 _ACTS_ANY_VERBOSE("Get as " << (m_handler->heap ? "heap" : "local"));
0194
0195 return *reinterpret_cast<const T*>(dataPtr());
0196 }
0197
0198 ~AnyBase() { destroy(); }
0199
0200
0201
0202 AnyBase(const AnyBase& other) _ACTS_ANY_NOEXCEPT {
0203 if (m_handler == nullptr && other.m_handler == nullptr) {
0204
0205 return;
0206 }
0207
0208 _ACTS_ANY_VERBOSE("Copy construct (this="
0209 << this << ") at: " << static_cast<void*>(m_data.data()));
0210
0211 m_handler = other.m_handler;
0212 copyConstruct(other);
0213 }
0214
0215
0216
0217
0218 AnyBase& operator=(const AnyBase& other) _ACTS_ANY_NOEXCEPT {
0219 _ACTS_ANY_VERBOSE("Copy assign (this="
0220 << this << ") at: " << static_cast<void*>(m_data.data()));
0221
0222 if (m_handler == nullptr && other.m_handler == nullptr) {
0223
0224 return *this;
0225 }
0226
0227 if (m_handler == other.m_handler) {
0228
0229 copy(std::move(other));
0230 } else {
0231 if (m_handler != nullptr) {
0232
0233 destroy();
0234 }
0235 assert(m_handler == nullptr);
0236 m_handler = other.m_handler;
0237 copyConstruct(std::move(other));
0238 }
0239 return *this;
0240 }
0241
0242
0243
0244 AnyBase(AnyBase&& other) _ACTS_ANY_NOEXCEPT {
0245 _ACTS_ANY_VERBOSE("Move construct (this="
0246 << this << ") at: " << static_cast<void*>(m_data.data()));
0247 if (m_handler == nullptr && other.m_handler == nullptr) {
0248
0249 return;
0250 }
0251
0252 m_handler = other.m_handler;
0253 moveConstruct(std::move(other));
0254 }
0255
0256
0257
0258
0259 AnyBase& operator=(AnyBase&& other) _ACTS_ANY_NOEXCEPT {
0260 _ACTS_ANY_VERBOSE("Move assign (this="
0261 << this << ") at: " << static_cast<void*>(m_data.data()));
0262 if (m_handler == nullptr && other.m_handler == nullptr) {
0263
0264 return *this;
0265 }
0266
0267 if (m_handler == other.m_handler) {
0268
0269 move(std::move(other));
0270 } else {
0271 if (m_handler != nullptr) {
0272
0273 destroy();
0274 }
0275 assert(m_handler == nullptr);
0276 m_handler = other.m_handler;
0277 moveConstruct(std::move(other));
0278 }
0279
0280 return *this;
0281 }
0282
0283
0284
0285 explicit operator bool() const { return m_handler != nullptr; }
0286
0287 private:
0288 void* dataPtr() {
0289 if (m_handler->heapAllocated) {
0290 return *reinterpret_cast<void**>(m_data.data());
0291 } else {
0292 return reinterpret_cast<void*>(m_data.data());
0293 }
0294 }
0295
0296 void setDataPtr(void* ptr) { *reinterpret_cast<void**>(m_data.data()) = ptr; }
0297
0298 const void* dataPtr() const {
0299 if (m_handler->heapAllocated) {
0300 return *reinterpret_cast<void* const*>(m_data.data());
0301 } else {
0302 return reinterpret_cast<const void*>(m_data.data());
0303 }
0304 }
0305
0306 template <typename T>
0307 static consteval std::uint64_t typeHash() {
0308
0309
0310 return detail::fnv1a_64(__PRETTY_FUNCTION__);
0311 }
0312
0313 struct Handler {
0314 void (*destroy)(void* ptr) = nullptr;
0315 void (*moveConstruct)(void* from, void* to) = nullptr;
0316 void (*move)(void* from, void* to) = nullptr;
0317 void* (*copyConstruct)(const void* from, void* to) = nullptr;
0318 void (*copy)(const void* from, void* to) = nullptr;
0319 bool heapAllocated{false};
0320 std::uint64_t typeHash{0};
0321 };
0322
0323 template <typename T>
0324 static const Handler* makeHandler() {
0325 static_assert(!std::is_same_v<T, AnyBase<SIZE>>, "Cannot wrap any in any");
0326 static const Handler static_handler = []() {
0327 Handler h;
0328 h.heapAllocated = heapAllocated<T>();
0329 if constexpr (!std::is_trivially_destructible_v<T> ||
0330 heapAllocated<T>()) {
0331 h.destroy = &destroyImpl<T>;
0332 }
0333 if constexpr (!std::is_trivially_move_constructible_v<T> ||
0334 heapAllocated<T>()) {
0335 h.moveConstruct = &moveConstructImpl<T>;
0336 }
0337 if constexpr (!std::is_trivially_move_assignable_v<T> ||
0338 heapAllocated<T>()) {
0339 h.move = &moveImpl<T>;
0340 }
0341 if constexpr (!std::is_trivially_copy_constructible_v<T> ||
0342 heapAllocated<T>()) {
0343 h.copyConstruct = ©ConstructImpl<T>;
0344 }
0345 if constexpr (!std::is_trivially_copy_assignable_v<T> ||
0346 heapAllocated<T>()) {
0347 h.copy = ©Impl<T>;
0348 }
0349
0350 h.typeHash = typeHash<T>();
0351
0352 _ACTS_ANY_DEBUG("Type: " << typeid(T).name());
0353 _ACTS_ANY_DEBUG(" -> destroy: " << h.destroy);
0354 _ACTS_ANY_DEBUG(" -> moveConstruct: " << h.moveConstruct);
0355 _ACTS_ANY_DEBUG(" -> move: " << h.move);
0356 _ACTS_ANY_DEBUG(" -> copyConstruct: " << h.copyConstruct);
0357 _ACTS_ANY_DEBUG(" -> copy: " << h.copy);
0358 _ACTS_ANY_DEBUG(
0359 " -> heapAllocated: " << (h.heapAllocated ? "yes" : "no"));
0360
0361 return h;
0362 }();
0363 return &static_handler;
0364 }
0365
0366 template <typename T>
0367 static constexpr bool heapAllocated() {
0368 return sizeof(T) > SIZE;
0369 }
0370
0371 void destroy() {
0372 _ACTS_ANY_VERBOSE("Destructor this=" << this << " handler: " << m_handler);
0373 if (m_handler != nullptr && m_handler->destroy != nullptr) {
0374 _ACTS_ANY_VERBOSE("Non-trivial destruction");
0375 m_handler->destroy(dataPtr());
0376 }
0377 m_handler = nullptr;
0378 }
0379
0380 void moveConstruct(AnyBase&& fromAny) {
0381 if (m_handler == nullptr) {
0382 return;
0383 }
0384
0385 void* to = dataPtr();
0386 void* from = fromAny.dataPtr();
0387 if (m_handler->heapAllocated) {
0388
0389 setDataPtr(fromAny.dataPtr());
0390
0391 fromAny.m_handler = nullptr;
0392 return;
0393 }
0394
0395 if (m_handler->moveConstruct == nullptr) {
0396 _ACTS_ANY_VERBOSE("Trivially move construct");
0397
0398 m_data = std::move(fromAny.m_data);
0399 } else {
0400 m_handler->moveConstruct(from, to);
0401 }
0402 }
0403
0404 void move(AnyBase&& fromAny) {
0405 if (m_handler == nullptr) {
0406 return;
0407 }
0408
0409 void* to = dataPtr();
0410 void* from = fromAny.dataPtr();
0411 if (m_handler->heapAllocated) {
0412
0413
0414 m_handler->destroy(dataPtr());
0415 setDataPtr(fromAny.dataPtr());
0416
0417 fromAny.m_handler = nullptr;
0418 return;
0419 }
0420
0421 if (m_handler->move == nullptr) {
0422 _ACTS_ANY_VERBOSE("Trivially move");
0423
0424 m_data = std::move(fromAny.m_data);
0425 } else {
0426 m_handler->move(from, to);
0427 }
0428 }
0429
0430 void copyConstruct(const AnyBase& fromAny) {
0431 if (m_handler == nullptr) {
0432 return;
0433 }
0434
0435 void* to = dataPtr();
0436 const void* from = fromAny.dataPtr();
0437
0438 if (m_handler->copyConstruct == nullptr) {
0439 _ACTS_ANY_VERBOSE("Trivially copy construct");
0440
0441 m_data = fromAny.m_data;
0442 } else {
0443 void* copyAt = m_handler->copyConstruct(from, to);
0444 if (to == nullptr) {
0445 assert(copyAt != nullptr);
0446
0447 setDataPtr(copyAt);
0448 }
0449 }
0450 }
0451
0452 void copy(const AnyBase& fromAny) {
0453 if (m_handler == nullptr) {
0454 return;
0455 }
0456
0457 void* to = dataPtr();
0458 const void* from = fromAny.dataPtr();
0459
0460 if (m_handler->copy == nullptr) {
0461 _ACTS_ANY_VERBOSE("Trivially copy");
0462
0463 m_data = fromAny.m_data;
0464 } else {
0465 m_handler->copy(from, to);
0466 }
0467 }
0468
0469 template <typename T>
0470 static void destroyImpl(void* ptr) {
0471 assert(ptr != nullptr && "Address to destroy is nullptr");
0472 T* obj = static_cast<T*>(ptr);
0473 if constexpr (!heapAllocated<T>()) {
0474
0475 _ACTS_ANY_VERBOSE("Destroy local at: " << ptr);
0476 obj->~T();
0477 } else {
0478
0479 _ACTS_ANY_DEBUG("Delete type: " << typeid(T).name()
0480 << " heap at: " << obj);
0481 _ACTS_ANY_TRACK_DEALLOCATION(T, obj);
0482 delete obj;
0483 }
0484 }
0485
0486 template <typename T>
0487 static void moveConstructImpl(void* from, void* to) {
0488 _ACTS_ANY_VERBOSE("move const: " << from << " -> " << to);
0489 assert(from != nullptr && "Source is null");
0490 assert(to != nullptr && "Target is null");
0491 T* _from = static_cast<T*>(from);
0492 new (to) T(std::move(*_from));
0493 }
0494
0495 template <typename T>
0496 static void moveImpl(void* from, void* to) {
0497 _ACTS_ANY_VERBOSE("move: " << from << " -> " << to);
0498 assert(from != nullptr && "Source is null");
0499 assert(to != nullptr && "Target is null");
0500
0501 T* _from = static_cast<T*>(from);
0502 T* _to = static_cast<T*>(to);
0503
0504 (*_to) = std::move(*_from);
0505 }
0506
0507 template <typename T>
0508 static void* copyConstructImpl(const void* from, void* to) {
0509 _ACTS_ANY_VERBOSE("copy const: " << from << " -> " << to);
0510 assert(from != nullptr && "Source is null");
0511 const T* _from = static_cast<const T*>(from);
0512 if (to == nullptr) {
0513 assert(heapAllocated<T>() && "Received nullptr in local buffer case");
0514 to = new T(*_from);
0515 _ACTS_ANY_TRACK_ALLOCATION(T, to);
0516
0517 } else {
0518 assert(!heapAllocated<T>() && "Received non-nullptr in heap case");
0519 new (to) T(*_from);
0520 }
0521 return to;
0522 }
0523
0524 template <typename T>
0525 static void copyImpl(const void* from, void* to) {
0526 _ACTS_ANY_VERBOSE("copy: " << from << " -> " << to);
0527 assert(from != nullptr && "Source is null");
0528 assert(to != nullptr && "Target is null");
0529
0530 const T* _from = static_cast<const T*>(from);
0531 T* _to = static_cast<T*>(to);
0532
0533 (*_to) = *_from;
0534 }
0535
0536 static constexpr std::size_t kMaxAlignment =
0537 std::max(alignof(std::max_align_t),
0538 #if defined(__AVX512F__)
0539 std::size_t{64}
0540 #elif defined(__AVX__)
0541 std::size_t{32}
0542 #elif defined(__SSE__)
0543 std::size_t{16}
0544 #else
0545 std::size_t{0}
0546
0547
0548 #endif
0549 );
0550
0551 alignas(kMaxAlignment) std::array<std::byte, SIZE> m_data{};
0552 const Handler* m_handler{nullptr};
0553 };
0554
0555
0556
0557
0558
0559 using Any = AnyBase<sizeof(void*)>;
0560
0561 #undef _ACTS_ANY_VERBOSE
0562 #undef _ACTS_ANY_VERBOSE_BUFFER
0563 #undef _ACTS_ANY_ENABLE_VERBOSE
0564
0565 }