Skip to content

Commit 1aa3354

Browse files
committed
explore alternatives
1 parent ff469ae commit 1aa3354

3 files changed

Lines changed: 148 additions & 52 deletions

File tree

Python/Examples/src/Framework.cpp

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,13 @@ class PyReadDataHandle : public ReadDataHandleBase {
106106
PyReadDataHandle(SequenceElement* parent, py::object pytype,
107107
const std::string& name)
108108
: ReadDataHandleBase(parent, name) {
109-
m_entry = WhiteBoardRegistry::find(pytype);
109+
m_entry = WhiteBoardRegistry::find(pytype.ptr());
110110
if (m_entry == nullptr) {
111111
throw py::type_error("Type '" +
112112
pytype.attr("__qualname__").cast<std::string>() +
113113
"' is not registered for WhiteBoard access");
114114
}
115-
if (m_entry->typeinfo == nullptr) {
115+
if (WhiteBoardRegistry::typeInfo(m_entry) == nullptr) {
116116
throw py::type_error("Type '" +
117117
pytype.attr("__qualname__").cast<std::string>() +
118118
"' is not registered for WhiteBoard access");
@@ -121,9 +121,13 @@ class PyReadDataHandle : public ReadDataHandleBase {
121121
registerAsReadHandle();
122122
}
123123

124-
const std::type_info& typeInfo() const override { return *m_entry->typeinfo; }
124+
const std::type_info& typeInfo() const override {
125+
return *WhiteBoardRegistry::typeInfo(m_entry);
126+
}
125127

126-
std::uint64_t typeHash() const override { return m_entry->typeHash; }
128+
std::uint64_t typeHash() const override {
129+
return WhiteBoardRegistry::typeHash(m_entry);
130+
}
127131

128132
py::object call(const py::object& wbPy) const {
129133
if (!isInitialized()) {
@@ -138,42 +142,54 @@ class PyReadDataHandle : public ReadDataHandleBase {
138142

139143
auto [holder, storedTypeHash] = getHolder(wb);
140144

141-
if (m_entry->typeHash != storedTypeHash) {
142-
const auto& expected = boost::core::demangle(m_entry->typeinfo->name());
145+
if (WhiteBoardRegistry::typeHash(m_entry) != storedTypeHash) {
146+
const auto& expected =
147+
boost::core::demangle(WhiteBoardRegistry::typeInfo(m_entry)->name());
143148
const auto& actual = boost::core::demangle(
144149
(holder->typeInfo() != nullptr) ? holder->typeInfo()->name()
145150
: "unknown");
146151
throw py::type_error("Type mismatch for key '" + key() + "'. Expected " +
147152
expected + " but got " + actual);
148153
}
149154

150-
return m_entry->toPython(*holder, wbPy);
155+
PyObject* out = WhiteBoardRegistry::toPython(m_entry, *holder, wbPy.ptr());
156+
return py::reinterpret_steal<py::object>(out);
151157
}
152158

153159
private:
154-
const WhiteBoardRegistry::RegistryEntry* m_entry{nullptr};
160+
const WhiteBoardRegistry::EntryHandle* m_entry{nullptr};
155161
};
156162

157163
class PyWriteDataHandle : public WriteDataHandleBase {
158164
public:
159165
PyWriteDataHandle(SequenceElement* parent, const py::object& pytype,
160166
const std::string& name)
161167
: WriteDataHandleBase(parent, name) {
162-
m_entry = WhiteBoardRegistry::find(pytype);
168+
m_entry = WhiteBoardRegistry::find(pytype.ptr());
169+
if (m_entry == nullptr) {
170+
throw py::type_error("Type '" +
171+
pytype.attr("__qualname__").cast<std::string>() +
172+
"' is not registered for WhiteBoard access");
173+
}
163174
registerAsWriteHandle();
164175
}
165176

166177
void call(const AlgorithmContext& ctx, const py::object& obj) const {
167-
auto any = m_entry->fromPython(obj);
168-
addHolder(ctx.eventStore, std::move(any), m_entry->typeHash);
178+
auto any = WhiteBoardRegistry::fromPython(m_entry, obj.ptr());
179+
addHolder(ctx.eventStore, std::move(any),
180+
WhiteBoardRegistry::typeHash(m_entry));
169181
}
170182

171-
const std::type_info& typeInfo() const override { return *m_entry->typeinfo; }
183+
const std::type_info& typeInfo() const override {
184+
return *WhiteBoardRegistry::typeInfo(m_entry);
185+
}
172186

173-
std::uint64_t typeHash() const override { return m_entry->typeHash; }
187+
std::uint64_t typeHash() const override {
188+
return WhiteBoardRegistry::typeHash(m_entry);
189+
}
174190

175191
private:
176-
const WhiteBoardRegistry::RegistryEntry* m_entry{nullptr};
192+
const WhiteBoardRegistry::EntryHandle* m_entry{nullptr};
177193
};
178194

179195
void trigger_divbyzero() {

Python/Utilities/include/ActsPython/Utilities/WhiteBoardRegistry.hpp

Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@
1010

1111
#include "Acts/Utilities/Any.hpp"
1212
#include "Acts/Utilities/HashedString.hpp"
13-
#include "Acts/Utilities/Visibility.hpp"
1413

1514
#include <concepts>
15+
#include <cstdint>
1616
#include <functional>
17-
#include <unordered_map>
17+
#include <memory>
18+
#include <typeinfo>
1819

1920
#include <pybind11/pybind11.h>
2021

@@ -54,6 +55,21 @@ class WhiteBoardRegistry {
5455
using FromPythonFunction = std::function<std::unique_ptr<Acts::AnyMoveOnly>(
5556
const pybind11::object& obj)>;
5657

58+
public:
59+
/// Opaque handle to an internal registry entry.
60+
struct EntryHandle {
61+
protected:
62+
EntryHandle() = default;
63+
~EntryHandle() = default;
64+
};
65+
66+
private:
67+
static void registerTypeImpl(PyObject* pyType, ToPythonFunction toPython,
68+
FromPythonFunction fromPython,
69+
const std::type_info* typeinfo,
70+
std::uint64_t typeHash);
71+
72+
public:
5773
/// Register a C++ type T with its pybind11 Python type for WhiteBoard
5874
/// access. Use when the `py::class_<T>` type cannot be deduced (e.g. for
5975
/// template types).
@@ -75,26 +91,22 @@ class WhiteBoardRegistry {
7591

7692
using type = T;
7793

78-
instance()[pyType.ptr()] = {
79-
.toPython = [](const Acts::AnyMoveOnly& any,
80-
const py::object& wbPy) -> py::object {
94+
registerTypeImpl(
95+
pyType.ptr(),
96+
[](const Acts::AnyMoveOnly& any, const py::object& wbPy) -> py::object {
8197
// wb needed to ensure correct lifetime
8298
return py::cast(any.as<type>(),
8399
py::return_value_policy::reference_internal, wbPy);
84100
},
85-
.fromPython =
86-
[](const py::object& obj) {
87-
// This communicates to pybind11's smart_holder that the object is
88-
// consumed in C++
89-
auto up = py::cast<std::unique_ptr<T>>(obj);
90-
return std::make_unique<Acts::AnyMoveOnly>(std::move(*up));
91-
},
92-
.typeinfo = &typeid(type),
93-
.typeHash = Acts::typeHash<type>(),
94-
};
101+
[](const py::object& obj) {
102+
// This communicates to pybind11's smart_holder that the object is
103+
// consumed in C++
104+
auto up = py::cast<std::unique_ptr<T>>(obj);
105+
return std::make_unique<Acts::AnyMoveOnly>(std::move(*up));
106+
},
107+
&typeid(type), Acts::typeHash<type>());
95108
}
96109

97-
public:
98110
/// Register a pybind11-bound type T for WhiteBoard read access.
99111
/// Call this after the `py::class_<T>` definition.
100112
/// @tparam Ts The types to register.
@@ -103,35 +115,32 @@ class WhiteBoardRegistry {
103115
static void registerClass(const pybind11::class_<Ts...>& pyClass)
104116
requires PyClassWithSmartHolder<Ts...>
105117
{
106-
namespace py = pybind11;
107118
using type = pybind11::class_<Ts...>::type;
108119
registerType<type>(pyClass);
109120
}
110121

111-
/// Per-type registry entry: downcast function and type metadata for lookups.
112-
struct ACTS_SYMBOL_LOCAL RegistryEntry {
113-
ToPythonFunction toPython{
114-
nullptr}; ///< Converts `void*` + `WhiteBoard` -> `py::object`
122+
/// Look up a registered type by Python type object.
123+
/// @param pyType The Python type object to look up.
124+
/// @return Opaque handle to the internal entry, or nullptr if not registered.
125+
static const EntryHandle* find(PyObject* pyType) noexcept;
115126

116-
FromPythonFunction fromPython{nullptr};
117-
const std::type_info* typeinfo{nullptr}; ///< C++ type for type checking
118-
std::uint64_t typeHash{0}; ///< Hash for runtime type verification
119-
};
127+
/// Access the registered C++ type metadata.
128+
static const std::type_info* typeInfo(const EntryHandle* entry) noexcept;
120129

121-
/// Look up a registered type by its pybind11 Python type object.
122-
/// @param pyType The pybind11 Python type object to look up.
123-
/// @return Pointer to the `RegistryEntry`, or `nullptr` if not registered
124-
static RegistryEntry* find(const pybind11::object& pyType) {
125-
if (auto it = instance().find(pyType.ptr()); it != instance().end()) {
126-
return &it->second;
127-
}
128-
return nullptr;
129-
}
130+
/// Access the registered hash for runtime type verification.
131+
static std::uint64_t typeHash(const EntryHandle* entry) noexcept;
132+
133+
/// Convert a WhiteBoard object into a Python object.
134+
/// @return New reference (`PyObject*`) owned by the caller.
135+
static PyObject* toPython(const EntryHandle* entry,
136+
const Acts::AnyMoveOnly& any, PyObject* wbPy);
137+
138+
/// Convert a Python object into a WhiteBoard-storable holder.
139+
static std::unique_ptr<Acts::AnyMoveOnly> fromPython(const EntryHandle* entry,
140+
PyObject* obj);
130141

131142
private:
132143
WhiteBoardRegistry() = default;
133-
134-
static std::unordered_map<PyObject*, RegistryEntry>& instance();
135144
};
136145

137146
} // namespace ActsPython

Python/Utilities/src/WhiteBoardRegistry.cpp

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,83 @@
88

99
#include "ActsPython/Utilities/WhiteBoardRegistry.hpp"
1010

11+
#include <unordered_map>
12+
13+
namespace py = pybind11;
14+
1115
namespace ActsPython {
1216

13-
std::unordered_map<PyObject*, WhiteBoardRegistry::RegistryEntry>&
14-
WhiteBoardRegistry::instance() {
17+
namespace {
18+
19+
struct RegistryEntry : WhiteBoardRegistry::EntryHandle {
20+
std::function<py::object(const Acts::AnyMoveOnly& any,
21+
const py::object& wbPy)>
22+
toPython{nullptr};
23+
std::function<std::unique_ptr<Acts::AnyMoveOnly>(const py::object& obj)>
24+
fromPython{nullptr};
25+
const std::type_info* typeinfo{nullptr};
26+
std::uint64_t typeHash{0};
27+
};
28+
29+
std::unordered_map<PyObject*, RegistryEntry>& instance() {
1530
static std::unordered_map<PyObject*, RegistryEntry> map;
1631
return map;
1732
}
1833

34+
} // namespace
35+
36+
void WhiteBoardRegistry::registerTypeImpl(PyObject* pyType,
37+
ToPythonFunction toPython,
38+
FromPythonFunction fromPython,
39+
const std::type_info* typeinfo,
40+
std::uint64_t typeHash) {
41+
instance()[pyType] = {
42+
.toPython = std::move(toPython),
43+
.fromPython = std::move(fromPython),
44+
.typeinfo = typeinfo,
45+
.typeHash = typeHash,
46+
};
47+
}
48+
49+
const WhiteBoardRegistry::EntryHandle* WhiteBoardRegistry::find(
50+
PyObject* pyType) noexcept {
51+
if (auto it = instance().find(pyType); it != instance().end()) {
52+
return &it->second;
53+
}
54+
return nullptr;
55+
}
56+
57+
const std::type_info* WhiteBoardRegistry::typeInfo(
58+
const EntryHandle* entry) noexcept {
59+
auto* e = static_cast<const RegistryEntry*>(entry);
60+
return (e != nullptr) ? e->typeinfo : nullptr;
61+
}
62+
63+
std::uint64_t WhiteBoardRegistry::typeHash(const EntryHandle* entry) noexcept {
64+
auto* e = static_cast<const RegistryEntry*>(entry);
65+
return (e != nullptr) ? e->typeHash : 0;
66+
}
67+
68+
PyObject* WhiteBoardRegistry::toPython(const EntryHandle* entry,
69+
const Acts::AnyMoveOnly& any,
70+
PyObject* wbPy) {
71+
if (entry == nullptr) {
72+
throw py::type_error("Type is not registered for WhiteBoard access");
73+
}
74+
auto* e = static_cast<const RegistryEntry*>(entry);
75+
py::object pyWb = py::reinterpret_borrow<py::object>(wbPy);
76+
py::object obj = e->toPython(any, pyWb);
77+
return obj.release().ptr();
78+
}
79+
80+
std::unique_ptr<Acts::AnyMoveOnly> WhiteBoardRegistry::fromPython(
81+
const EntryHandle* entry, PyObject* obj) {
82+
if (entry == nullptr) {
83+
throw py::type_error("Type is not registered for WhiteBoard access");
84+
}
85+
auto* e = static_cast<const RegistryEntry*>(entry);
86+
py::object pyObj = py::reinterpret_borrow<py::object>(obj);
87+
return e->fromPython(pyObj);
88+
}
89+
1990
} // namespace ActsPython

0 commit comments

Comments
 (0)