Skip to content

Commit 928549a

Browse files
Merge branch 'main' into auto-dependency-upgrades
2 parents 478c68f + f537d63 commit 928549a

5 files changed

Lines changed: 313 additions & 31 deletions

File tree

.github/workflows/analysis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ jobs:
146146
BASE_REF: ${{ github.base_ref }}
147147
run: git fetch origin "$BASE_REF"
148148

149-
- uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
149+
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
150150

151151
- name: Install clang-tidy
152152
run: |

.github/workflows/docs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ jobs:
4949
cmake --build build --target docs
5050
5151
- name: Set up uv
52-
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
52+
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
5353

5454
- name: Check documentation links
5555
run: |

.github/workflows/pypi.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ jobs:
6666

6767
steps:
6868
- name: Download source tarball
69-
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
69+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
7070
with:
7171
name: acts-source
7272

@@ -75,7 +75,7 @@ jobs:
7575

7676
- name: Download build version
7777
if: github.event_name != 'workflow_dispatch' || github.event.inputs.acts_ref == ''
78-
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
78+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
7979
with:
8080
name: build-version
8181

@@ -94,7 +94,7 @@ jobs:
9494
ccache-${{ runner.os }}-${{ github.job }}-${{ env.CCACHE_KEY_SUFFIX }}-
9595
9696
- name: Build wheels
97-
uses: pypa/cibuildwheel@298ed2fb2c105540f5ed055e8a6ad78d82dd3a7e # v3.3.1
97+
uses: pypa/cibuildwheel@8d2b08b68458a16aeb24b64e68a09ab1c8e82084 # v3.4.1
9898
with:
9999
package-dir: acts
100100
env:
@@ -151,7 +151,7 @@ jobs:
151151
runs-on: ubuntu-latest
152152
steps:
153153
- name: Get wheels
154-
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
154+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
155155
with:
156156
pattern: cibw-*
157157
path: dist
@@ -176,7 +176,7 @@ jobs:
176176
runs-on: ubuntu-latest
177177
steps:
178178
- name: Get wheels
179-
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
179+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
180180
with:
181181
pattern: cibw-*
182182
path: dist

Core/include/Acts/Utilities/Any.hpp

Lines changed: 128 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -120,23 +120,23 @@ class AnyBaseAll {};
120120

121121
/// Small opaque type-erased type with configurable small buffer optimization
122122
///
123-
/// @tparam sb_size Size of the internal buffer for small buffer optimization
124-
/// @tparam copyable If true, stored types must be copyable and AnyBase is
123+
/// @tparam SbSize Size of the internal buffer for small buffer optimization
124+
/// @tparam Copyable If true, stored types must be copyable and AnyBase is
125125
/// copyable. If false, move-only types are allowed and AnyBase is
126126
/// move-only (copy constructor and copy assignment are deleted).
127127
///
128128
/// @note
129-
/// Type requirements when copyable is true:
129+
/// Type requirements when Copyable is true:
130130
/// - All stored types must be copy constructible and copy assignable.
131-
/// - Types stored locally (`sizeof(T) <= sb_size`) must also be move
131+
/// - Types stored locally (`sizeof(T) <= SbSize`) must also be move
132132
/// constructible
133133
/// and move assignable because local moves use move operations when not
134134
/// trivially movable (trivial moves fall back to buffer copies).
135-
/// - Types stored on the heap (`sizeof(T) > sb_size`) are moved by stealing the
135+
/// - Types stored on the heap (`sizeof(T) > SbSize`) are moved by stealing the
136136
/// pointer, so no move operations are required in that case.
137137
///
138138
/// @note
139-
/// Type requirements when copyable is false:
139+
/// Type requirements when Copyable is false:
140140
/// - All stored types must be move constructible.
141141
/// - Types stored locally must also be move assignable.
142142
/// - Heap-allocated types only need move constructible (pointer steal).
@@ -148,25 +148,29 @@ class AnyBaseAll {};
148148
/// copies when trivial.
149149
/// - Heap storage: values are allocated on the heap; moves transfer ownership
150150
/// of the pointer; copies allocate and copy-construct the pointee.
151-
template <std::size_t sb_size, bool copyable = true>
151+
template <std::size_t SbSize, bool Copyable = true, typename Base = void>
152152
class AnyBase : public AnyBaseAll {
153-
static_assert(sizeof(void*) <= sb_size, "Size is too small for a pointer");
153+
static_assert(sizeof(void*) <= SbSize, "Size is too small for a pointer");
154154

155-
/// Type trait: T is storable when copyable requires copy+move, else
156-
/// move-only.
155+
/// Type trait: T is storable when Copyable requires copy+move, else
156+
/// move-only. When @c Base is not @c void, the type must additionally be
157+
/// convertible to @c Base* (i.e. publicly and unambiguously derived).
157158
/// @tparam U Type to check
158159
/// @return True if the type is storable, false otherwise
159160
template <typename U>
160161
static constexpr bool isStorable() {
161162
if constexpr (std::is_base_of_v<AnyBaseAll, U>) {
162163
return false;
163-
} else if constexpr (copyable) {
164+
} else if constexpr (!std::is_void_v<Base> &&
165+
!std::is_convertible_v<U*, Base*>) {
166+
return false;
167+
} else if constexpr (Copyable) {
164168
return std::is_copy_assignable_v<U> && std::is_copy_constructible_v<U> &&
165-
(sizeof(U) > sb_size || (std::is_move_assignable_v<U> &&
166-
std::is_move_constructible_v<U>));
169+
(sizeof(U) > SbSize || (std::is_move_assignable_v<U> &&
170+
std::is_move_constructible_v<U>));
167171
} else {
168172
return std::is_move_constructible_v<U> &&
169-
(sizeof(U) > sb_size || std::is_move_assignable_v<U>);
173+
(sizeof(U) > SbSize || std::is_move_assignable_v<U>);
170174
}
171175
}
172176

@@ -304,12 +308,12 @@ class AnyBase : public AnyBaseAll {
304308

305309
~AnyBase() { destroy(); }
306310

307-
/// Copy constructor (only when copyable is true)
311+
/// Copy constructor (only when Copyable is true)
308312
/// @param other The AnyBase to copy from
309313
/// @note Not noexcept: copying a heap-allocated value allocates (may throw
310314
/// std::bad_alloc) and the stored type's copy constructor may throw.
311315
AnyBase(const AnyBase& other)
312-
requires copyable
316+
requires Copyable
313317
{
314318
if (m_handler == nullptr && other.m_handler == nullptr) {
315319
// both are empty, noop
@@ -323,18 +327,18 @@ class AnyBase : public AnyBaseAll {
323327
copyConstruct(other);
324328
}
325329

326-
/// Copy constructor deleted when copyable is false (move-only variant)
330+
/// Copy constructor deleted when Copyable is false (move-only variant)
327331
AnyBase(const AnyBase&)
328-
requires(!copyable)
332+
requires(!Copyable)
329333
= delete;
330334

331-
/// Copy assignment operator (only when copyable is true)
335+
/// Copy assignment operator (only when Copyable is true)
332336
/// @param other The AnyBase to copy from
333337
/// @return Reference to this object
334338
/// @note Not noexcept: copying a heap-allocated value allocates (may throw
335339
/// std::bad_alloc) and the stored type's copy operations may throw.
336340
AnyBase& operator=(const AnyBase& other)
337-
requires copyable
341+
requires Copyable
338342
{
339343
_ACTS_ANY_VERBOSE("Copy assign (this="
340344
<< this << ") at: " << static_cast<void*>(m_data.data()));
@@ -374,9 +378,9 @@ class AnyBase : public AnyBaseAll {
374378
return *this;
375379
}
376380

377-
/// Copy assignment deleted when copyable is false (move-only variant)
381+
/// Copy assignment deleted when Copyable is false (move-only variant)
378382
AnyBase& operator=(const AnyBase&)
379-
requires(!copyable)
383+
requires(!Copyable)
380384
= delete;
381385

382386
/// Move constructor
@@ -438,6 +442,77 @@ class AnyBase : public AnyBaseAll {
438442
return m_handler != nullptr ? m_handler->typeInfo : nullptr;
439443
}
440444

445+
// The base accessors below are member templates on a dummy @c B defaulting to
446+
// @c Base. This keeps their return types (@c B& / @c B*) from being formed
447+
// when @c Base is @c void: the member template is only instantiated on use,
448+
// and the @c requires clause removes it entirely for the untyped variant. A
449+
// plain non-template member would try to form @c void& at class instantiation
450+
// and fail to compile.
451+
452+
/// Get a pointer to the stored value as @c Base*, regardless of its concrete
453+
/// type. Only available when @c Base is not @c void.
454+
/// @return Pointer to the stored value upcast to @c Base*, or nullptr if empty
455+
template <typename B = Base>
456+
requires(!std::is_void_v<B>)
457+
B* asBase() {
458+
if (m_handler == nullptr) {
459+
return nullptr;
460+
}
461+
return m_handler->upcast(dataPtr());
462+
}
463+
464+
/// Get a const pointer to the stored value as @c Base*. Only available when
465+
/// @c Base is not @c void.
466+
/// @return Const pointer to the stored value upcast to @c Base*, or nullptr if empty
467+
template <typename B = Base>
468+
requires(!std::is_void_v<B>)
469+
const B* asBase() const {
470+
if (m_handler == nullptr) {
471+
return nullptr;
472+
}
473+
return m_handler->upcastConst(dataPtr());
474+
}
475+
476+
/// Dereference to the stored value as @c Base&. Only available when @c Base
477+
/// is not @c void.
478+
/// @return Reference to the stored value upcast to @c Base&
479+
template <typename B = Base>
480+
requires(!std::is_void_v<B>)
481+
B& operator*() {
482+
assert(m_handler != nullptr && "operator* on empty AnyBase");
483+
return *m_handler->upcast(dataPtr());
484+
}
485+
486+
/// Dereference to the stored value as @c const @c Base&. Only available when
487+
/// @c Base is not @c void.
488+
/// @return Const reference to the stored value upcast to @c Base&
489+
template <typename B = Base>
490+
requires(!std::is_void_v<B>)
491+
const B& operator*() const {
492+
assert(m_handler != nullptr && "operator* on empty AnyBase");
493+
return *m_handler->upcastConst(dataPtr());
494+
}
495+
496+
/// Member access on the stored value as @c Base*. Only available when
497+
/// @c Base is not @c void.
498+
/// @return Pointer to the stored value upcast to @c Base*
499+
template <typename B = Base>
500+
requires(!std::is_void_v<B>)
501+
B* operator->() {
502+
assert(m_handler != nullptr && "operator-> on empty AnyBase");
503+
return m_handler->upcast(dataPtr());
504+
}
505+
506+
/// Member access on the stored value as @c const @c Base*. Only available
507+
/// when @c Base is not @c void.
508+
/// @return Const pointer to the stored value upcast to @c Base*
509+
template <typename B = Base>
510+
requires(!std::is_void_v<B>)
511+
const B* operator->() const {
512+
assert(m_handler != nullptr && "operator-> on empty AnyBase");
513+
return m_handler->upcastConst(dataPtr());
514+
}
515+
441516
private:
442517
void* dataPtr() {
443518
if (m_handler->heapAllocated) {
@@ -458,6 +533,13 @@ class AnyBase : public AnyBaseAll {
458533
}
459534

460535
struct Handler {
536+
// Maps the stored payload to Base*. Collapses to void*(*)(void*) and stays
537+
// nullptr (never read) when Base == void.
538+
Base* (*upcast)(void*) = nullptr;
539+
// Const-qualified counterpart used by the const accessors so they don't
540+
// have to cast away const on the data pointer. Same as above when
541+
// Base == void.
542+
const Base* (*upcastConst)(const void*) = nullptr;
461543
void (*destroy)(void* ptr) = nullptr;
462544
void (*moveConstruct)(void* from, void* to) = nullptr;
463545
void (*move)(void* from, void* to) = nullptr;
@@ -499,6 +581,15 @@ class AnyBase : public AnyBaseAll {
499581
h.copy = &copyImpl<T>;
500582
}
501583

584+
if constexpr (!std::is_void_v<Base>) {
585+
h.upcast = [](void* p) -> Base* {
586+
return static_cast<Base*>(static_cast<T*>(p));
587+
};
588+
h.upcastConst = [](const void* p) -> const Base* {
589+
return static_cast<const Base*>(static_cast<const T*>(p));
590+
};
591+
}
592+
502593
h.typeHash = typeHash<T>();
503594
h.typeInfo = &typeid(T);
504595

@@ -518,7 +609,7 @@ class AnyBase : public AnyBaseAll {
518609

519610
template <typename T>
520611
static constexpr bool heapAllocated() {
521-
return sizeof(T) > sb_size;
612+
return sizeof(T) > SbSize;
522613
}
523614

524615
template <typename T, typename... Args>
@@ -725,7 +816,7 @@ class AnyBase : public AnyBaseAll {
725816
#endif
726817
);
727818

728-
alignas(kMaxAlignment) std::array<std::byte, sb_size> m_data{};
819+
alignas(kMaxAlignment) std::array<std::byte, SbSize> m_data{};
729820
const Handler* m_handler{nullptr};
730821
};
731822

@@ -740,6 +831,19 @@ using Any = AnyBase<sizeof(void*), true>;
740831
/// Use when storing types that are not copyable.
741832
using AnyMoveOnly = AnyBase<sizeof(void*), false>;
742833

834+
/// @brief Typed type-erased value over a common polymorphic base
835+
/// @details Stores any value of a type @c T convertible to @c Base* (i.e.
836+
/// publicly and unambiguously derived from @c Base) and hands it back
837+
/// as @c Base& / @c Base* through @ref AnyBase::asBase,
838+
/// @ref AnyBase::operator* and @ref AnyBase::operator-> without naming
839+
/// @c T. The concrete type can still be recovered with
840+
/// @ref AnyBase::as / @ref AnyBase::asPtr. Copyable; values up to
841+
/// @c SbSize bytes are stored inline, larger ones on the heap.
842+
/// @tparam Base Common base class exposed by the accessors
843+
/// @tparam SbSize Size of the small-buffer-optimization storage
844+
template <typename Base, std::size_t SbSize = sizeof(void*)>
845+
using AnyOf = AnyBase<SbSize, true, Base>;
846+
743847
/// @}
744848

745849
#undef _ACTS_ANY_VERBOSE

0 commit comments

Comments
 (0)