ChimeraTK-ApplicationCore 04.08.00
Loading...
Searching...
No Matches
PyArrayAccessor.cc
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
4#include "PyArrayAccessor.h"
5
6#include <pybind11/stl.h>
7
8namespace py = pybind11;
9
10namespace ChimeraTK {
11
12 /********************************************************************************************************************/
13
14 template<template<typename> class AccessorType>
15 ArrayAccessorVariant PyArrayAccessor::createAccessor(ChimeraTK::DataType type, Module* owner, const std::string& name,
16 std::string unit, size_t nElements, const std::string& description, const std::unordered_set<std::string>& tags) {
17 std::optional<ArrayAccessorVariant> rv;
18 ChimeraTK::callForTypeNoVoid(type, [&](auto t) {
19 using UserType = decltype(t);
20 AccessorType<UserType> acc(owner, name, unit, nElements, description, tags);
21 rv.emplace(std::in_place_type<AccessorType<UserType>>, std::move(acc));
22 });
23 return std::move(rv.value());
24 }
25
26 /********************************************************************************************************************/
27
29 read();
30 return get();
31 }
32
33 /********************************************************************************************************************/
34
35 void PyArrayAccessor::setAndWrite(const UserTypeTemplateVariantNoVoid<Vector>& vec) {
36 set(vec);
37 write();
38 }
39 /********************************************************************************************************************/
40
42 size_t rv;
43 std::visit([&](auto& acc) { rv = acc.getNElements(); }, _accessor);
44 return rv;
45 }
46 /********************************************************************************************************************/
47
48 void PyArrayAccessor::set(const UserTypeTemplateVariantNoVoid<Vector>& vec) {
49 std::visit(
50 [&](auto& acc) {
51 using ACC = typename std::remove_reference<decltype(acc)>::type;
52 using expectedUserType = typename ACC::value_type;
53 std::visit(
54 [&](const auto& vector) {
55 std::vector<expectedUserType> converted(vector.size());
56 std::transform(vector.begin(), vector.end(), converted.begin(),
57 [](auto v) { return userTypeToUserType<expectedUserType>(v); });
58 acc = converted;
59 },
60 vec);
61 },
62 _accessor);
63 }
64 /********************************************************************************************************************/
65
66 py::object PyArrayAccessor::get() const {
67 py::object rv;
68 std::visit(
69 [&](auto& acc) {
70 using ACC = typename std::remove_reference<decltype(acc)>::type;
71 using userType = typename ACC::value_type;
72 auto ndacc = boost::dynamic_pointer_cast<NDRegisterAccessor<userType>>(acc.getHighLevelImplElement());
73 if constexpr(std::is_same<userType, std::string>::value) {
74 // String arrays are not really supported by numpy, so we return a list instead
75 rv = py::cast(ndacc->accessChannel(0));
76 }
77 else {
78 auto ary = py::array(py::dtype::of<userType>(), {acc.getNElements()}, {sizeof(userType)},
79 ndacc->accessChannel(0).data(), py::cast(this));
80 assert(!ary.owndata()); // numpy must not own our buffers
81 rv = ary;
82 }
83 },
84 _accessor);
85 return rv;
86 }
87 /********************************************************************************************************************/
88
89 py::object PyArrayAccessor::getitem(size_t index) const {
90 py::object rv;
91 std::visit([&](auto& acc) { rv = py::cast(acc[index]); }, _accessor);
92 return rv;
93 }
94
95 /********************************************************************************************************************/
96
97 void PyArrayAccessor::setitem(size_t index, const UserTypeVariantNoVoid& val) {
98 std::visit(
99 [&](auto& acc) {
100 std::visit(
101 [&](auto& v) {
102 acc[index] = userTypeToUserType<typename std::remove_reference<decltype(acc)>::type::value_type>(v);
103 },
104 val);
105 },
106 _accessor);
107 }
108
109 /********************************************************************************************************************/
110
111 void PyArrayAccessor::setslice(const py::slice& slice, const UserTypeVariantNoVoid& val) {
112 std::visit(
113 [&](auto& acc) {
114 std::visit(
115 [&](auto& v) {
116 size_t start, stop, step, length;
117 if(!slice.compute(acc.getNElements(), &start, &stop, &step, &length)) {
118 throw pybind11::error_already_set();
119 }
120
121 auto value = userTypeToUserType<typename std::remove_reference<decltype(acc)>::type::value_type>(v);
122 for(size_t i = start; i < stop; i += step) {
123 acc[i] = value;
124 }
125 },
126 val);
127 },
128 _accessor);
129 }
130
131 /********************************************************************************************************************/
132
133 std::string PyArrayAccessor::repr(py::object& acc) {
134 if(not acc.cast<PyArrayAccessor&>().getTE().isInitialised()) {
135 return "<ArrayAccessor(not initialized)>";
136 }
137
138 std::string rep{"<ArrayAccessor(type="};
139 rep.append(py::cast<py::object>(py::cast(&acc).attr("getValueType")()).attr("__repr__")().cast<std::string>());
140 rep.append(", name=");
141 rep.append(py::cast(&acc).attr("getName")().cast<std::string>());
142 rep.append(", data=");
143 rep.append(py::cast<py::object>(py::cast(&acc).attr("__str__")()).cast<std::string>());
144 rep.append(", versionNumber=");
145 rep.append(py::cast<py::object>(py::cast(&acc).attr("getVersionNumber")()).attr("__repr__")().cast<std::string>());
146 rep.append(", dataValidity=");
147 rep.append(py::cast<py::object>(py::cast(&acc).attr("dataValidity")()).attr("__repr__")().cast<std::string>());
148 rep.append(")>");
149 return rep;
150 }
151 /********************************************************************************************************************/
152
154 py::buffer_info info;
155 std::visit(
156 [&](auto& acc) {
157 using ACC = typename std::remove_reference<decltype(acc)>::type;
158 using userType = typename ACC::value_type;
159 auto ndacc = boost::dynamic_pointer_cast<NDRegisterAccessor<userType>>(acc.getHighLevelImplElement());
160 if constexpr(std::is_same<userType, ChimeraTK::Boolean>::value) {
161 info.format = py::format_descriptor<bool>::format();
162 }
163 else if constexpr(std::is_same<userType, std::string>::value) {
164 // cannot implement
165 return;
166 }
167 else {
168 info.format = py::format_descriptor<userType>::format();
169 }
170 info.ptr = ndacc->accessChannel(0).data();
171 info.itemsize = sizeof(userType);
172 info.ndim = 1;
173 info.shape = {acc.getNElements()};
174 info.strides = {sizeof(userType)};
175 },
176 _accessor);
177 return info;
178 }
179
180 /********************************************************************************************************************/
181
183
184 /********************************************************************************************************************/
185
187 py::class_<PyArrayAccessor, PyTransferElementBase, std::unique_ptr<PyArrayAccessor, py::nodelete>> arrayacc(
188 m, "ArrayAccessor", py::buffer_protocol(), py::module_local());
189 arrayacc.def(py::init<>())
191 .def("read", &PyArrayAccessor::read,
192 "Read the data from the device.\n\nIf AccessMode::wait_for_new_data was set, this function will block "
193 "until new data has arrived. Otherwise it still might block for a short time until the data transfer was "
194 "complete.")
195 .def("readNonBlocking", &PyArrayAccessor::readNonBlocking,
196 "Read the next value, if available in the input buffer.\n\nIf AccessMode::wait_for_new_data was set, this "
197 "function returns immediately and the return value indicated if a new value was available (true) or not "
198 "(false).\n\nIf AccessMode::wait_for_new_data was not set, this function is identical to read(), which "
199 "will still return quickly. Depending on the actual transfer implementation, the backend might need to "
200 "transfer data to obtain the current value before returning. Also this function is not guaranteed to be "
201 "lock free. The return value will be always true in this mode.")
202 .def("readLatest", &PyArrayAccessor::readLatest,
203 "Read the latest value, discarding any other update since the last read if present.\n\nOtherwise this "
204 "function is identical to readNonBlocking(), i.e. it will never wait for new values and it will return "
205 "whether a new value was available if AccessMode::wait_for_new_data is set.")
206 .def("write", &PyArrayAccessor::write,
207 "Write the data to device.\n\nThe return value is true, old data was lost on the write transfer (e.g. due "
208 "to an buffer overflow). In case of an unbuffered write transfer, the return value will always be false.")
209 .def("writeDestructively", &PyArrayAccessor::writeDestructively,
210 "Just like write(), but allows the implementation to destroy the content of the user buffer in the "
211 "process.\n\nThis is an optional optimisation, hence there is a default implementation which just calls "
212 "the normal doWriteTransfer(). In any case, the application must expect the user buffer of the "
213 "TransferElement to contain undefined data after calling this function.")
214 .def("getName", &PyArrayAccessor::getName, "Returns the name that identifies the process variable.")
215 .def("getUnit", &PyArrayAccessor::getUnit,
216 "Returns the engineering unit.\n\nIf none was specified, it will default to ' n./ a.'")
217 .def("getDescription", &PyArrayAccessor::getDescription, "Returns the description of this variable/register.")
218 .def("getValueType", &PyArrayAccessor::getValueType,
219 "Returns the std::type_info for the value type of this transfer element.\n\nThis can be used to determine "
220 "the type at runtime.")
221 .def("getVersionNumber", &PyArrayAccessor::getVersionNumber,
222 "Returns the version number that is associated with the last transfer (i.e. last read or write)")
223 .def("isReadOnly", &PyArrayAccessor::isReadOnly,
224 "Check if transfer element is read only, i.e. it is readable but not writeable.")
225 .def("isReadable", &PyArrayAccessor::isReadable, "Check if transfer element is readable.")
226 .def("isWriteable", &PyArrayAccessor::isWriteable, "Check if transfer element is writeable.")
227 .def("getId", &PyArrayAccessor::getId,
228 "Obtain unique ID for the actual implementation of this TransferElement.\n\nThis means that e.g. two "
229 "instances of ScalarRegisterAccessor created by the same call to Device::getScalarRegisterAccessor() (e.g. "
230 "by copying the accessor to another using NDRegisterAccessorBridge::replace()) will have the same ID, "
231 "while two instances obtained by to difference calls to Device::getScalarRegisterAccessor() will have a "
232 "different ID even when accessing the very same register.")
233 .def("dataValidity", &PyArrayAccessor::dataValidity,
234 "Return current validity of the data.\n\nWill always return DataValidity.ok if the backend does not "
235 "support it")
236 .def("getNElements", &PyArrayAccessor::getNElements, "Return number of elements/samples in the register.")
237 .def("get", &PyArrayAccessor::get, "Return an array of UserType (without a previous read).")
238 .def("set", &PyArrayAccessor::set, "Set the values of the array of UserType.", py::arg("newValue"))
239 .def("setAndWrite", &PyArrayAccessor::setAndWrite,
240 "Convenience function to set and write new value.\n\nThe given version number. If versionNumber == {}, a "
241 "new version number is generated.",
242 py::arg("newValue"))
243 .def(
244 "readAndGet", &PyArrayAccessor::readAndGet, "Convenience function to read and return an array of UserType.")
245 .def("__repr__", &PyArrayAccessor::repr)
246 .def("__getitem__", &PyArrayAccessor::getitem)
247 .def("__setitem__", &PyArrayAccessor::setitem)
248 .def("__setitem__", &PyArrayAccessor::setslice)
249 .def("__getattr__", &PyArrayAccessor::getattr);
251 arrayacc.def(fn.c_str(),
252 [fn](PyArrayAccessor& acc, PyArrayAccessor& other) { return acc.get().attr(fn.c_str())(other.get()); });
253 arrayacc.def(
254 fn.c_str(), [fn](PyArrayAccessor& acc, py::object& other) { return acc.get().attr(fn.c_str())(other); });
255 }
257 arrayacc.def(fn.c_str(), [fn](PyArrayAccessor& acc) { return acc.get().attr(fn.c_str())(); });
258 }
259
263 m.def(
264 "ArrayPushInput",
265 [](ChimeraTK::DataType type, VariableGroup& owner, const std::string& name, const std::string& unit,
266 size_t nElements, const std::string& description, const std::unordered_set<std::string>& tags) {
267 return dynamic_cast<PyOwningObject&>(owner).make_child<PyArrayAccessor>(
268 AccessorTypeTag<ArrayPushInput>{}, type, &owner, name, unit, nElements, description, tags);
269 },
270 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("nElements"),
271 py::arg("description"), py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference,
272 "");
273 m.def(
274 "ArrayPushInput",
275 [](ChimeraTK::DataType::TheType type, VariableGroup& owner, const std::string& name, const std::string& unit,
276 size_t nElements, const std::string& description, const std::unordered_set<std::string>& tags) {
277 return dynamic_cast<PyOwningObject&>(owner).make_child<PyArrayAccessor>(
278 AccessorTypeTag<ArrayPushInput>{}, DataType(type), &owner, name, unit, nElements, description, tags);
279 },
280 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("nElements"),
281 py::arg("description"), py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference,
282 "");
283
287 m.def(
288 "ArrayPushInputWB",
289 [](ChimeraTK::DataType type, VariableGroup& owner, const std::string& name, const std::string& unit,
290 size_t nElements, const std::string& description, const std::unordered_set<std::string>& tags) {
291 return dynamic_cast<PyOwningObject&>(owner).make_child<PyArrayAccessor>(
292 AccessorTypeTag<ArrayPushInputWB>{}, type, &owner, name, unit, nElements, description, tags);
293 },
294 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("nElements"),
295 py::arg("description"), py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference,
296 "");
297 m.def(
298 "ArrayPushInputWB",
299 [](ChimeraTK::DataType::TheType type, VariableGroup& owner, const std::string& name, const std::string& unit,
300 size_t nElements, const std::string& description, const std::unordered_set<std::string>& tags) {
301 return dynamic_cast<PyOwningObject&>(owner).make_child<PyArrayAccessor>(
302 AccessorTypeTag<ArrayPushInputWB>{}, DataType(type), &owner, name, unit, nElements, description, tags);
303 },
304 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("nElements"),
305 py::arg("description"), py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference,
306 "");
307
311 m.def(
312 "ArrayPollInput",
313 [](ChimeraTK::DataType type, VariableGroup& owner, const std::string& name, const std::string& unit,
314 size_t nElements, const std::string& description, const std::unordered_set<std::string>& tags) {
315 return dynamic_cast<PyOwningObject&>(owner).make_child<PyArrayAccessor>(
316 AccessorTypeTag<ArrayPollInput>{}, type, &owner, name, unit, nElements, description, tags);
317 },
318 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("nElements"),
319 py::arg("description"), py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference,
320 "");
321 m.def(
322 "ArrayPollInput",
323 [](ChimeraTK::DataType::TheType type, VariableGroup& owner, const std::string& name, const std::string& unit,
324 size_t nElements, const std::string& description, const std::unordered_set<std::string>& tags) {
325 return dynamic_cast<PyOwningObject&>(owner).make_child<PyArrayAccessor>(
326 AccessorTypeTag<ArrayPollInput>{}, DataType(type), &owner, name, unit, nElements, description, tags);
327 },
328 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("nElements"),
329 py::arg("description"), py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference,
330 "");
331
335 m.def(
336 "ArrayOutput",
337 [](ChimeraTK::DataType type, VariableGroup& owner, const std::string& name, const std::string& unit,
338 size_t nElements, const std::string& description, const std::unordered_set<std::string>& tags) {
339 return dynamic_cast<PyOwningObject&>(owner).make_child<PyArrayAccessor>(
340 AccessorTypeTag<ArrayOutput>{}, type, &owner, name, unit, nElements, description, tags);
341 },
342 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("nElements"),
343 py::arg("description"), py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference,
344 "");
345 m.def(
346 "ArrayOutput",
347 [](ChimeraTK::DataType::TheType type, VariableGroup& owner, const std::string& name, const std::string& unit,
348 size_t nElements, const std::string& description, const std::unordered_set<std::string>& tags) {
349 return dynamic_cast<PyOwningObject&>(owner).make_child<PyArrayAccessor>(
350 AccessorTypeTag<ArrayOutput>{}, DataType(type), &owner, name, unit, nElements, description, tags);
351 },
352 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("nElements"),
353 py::arg("description"), py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference,
354 "");
355
359 m.def(
360 "ArrayOutputPushRB",
361 [](ChimeraTK::DataType type, VariableGroup& owner, const std::string& name, const std::string& unit,
362 size_t nElements, const std::string& description, const std::unordered_set<std::string>& tags) {
363 return dynamic_cast<PyOwningObject&>(owner).make_child<PyArrayAccessor>(
364 AccessorTypeTag<ArrayOutputPushRB>{}, type, &owner, name, unit, nElements, description, tags);
365 },
366 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("nElements"),
367 py::arg("description"), py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference,
368 "");
369 m.def(
370 "ArrayOutputPushRB",
371 [](ChimeraTK::DataType::TheType type, VariableGroup& owner, const std::string& name, const std::string& unit,
372 size_t nElements, const std::string& description, const std::unordered_set<std::string>& tags) {
373 return dynamic_cast<PyOwningObject&>(owner).make_child<PyArrayAccessor>(
374 AccessorTypeTag<ArrayOutputPushRB>{}, DataType(type), &owner, name, unit, nElements, description, tags);
375 },
376 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("nElements"),
377 py::arg("description"), py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference,
378 "");
379
383 m.def(
384 "ArrayOutputReverseRecovery",
385 [](ChimeraTK::DataType type, VariableGroup& owner, const std::string& name, const std::string& unit,
386 size_t nElements, const std::string& description, const std::unordered_set<std::string>& tags) {
387 return dynamic_cast<PyOwningObject&>(owner).make_child<PyArrayAccessor>(
388 AccessorTypeTag<ArrayOutputReverseRecovery>{}, type, &owner, name, unit, nElements, description, tags);
389 },
390 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("nElements"),
391 py::arg("description"), py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference,
392 "");
393 m.def(
394 "ArrayOutputReverseRecovery",
395 [](ChimeraTK::DataType::TheType type, VariableGroup& owner, const std::string& name, const std::string& unit,
396 size_t nElements, const std::string& description, const std::unordered_set<std::string>& tags) {
397 return dynamic_cast<PyOwningObject&>(owner).make_child<PyArrayAccessor>(
398 AccessorTypeTag<ArrayOutputReverseRecovery>{}, DataType(type), &owner, name, unit, nElements, description,
399 tags);
400 },
401 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("nElements"),
402 py::arg("description"), py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference,
403 "");
404
408 m.def(
409 "ArrayPushInputNoInitialValue",
410 [](ChimeraTK::DataType type, VariableGroup& owner, const std::string& name, const std::string& unit,
411 size_t nElements, const std::string& description, const std::unordered_set<std::string>& tags) {
412 return dynamic_cast<PyOwningObject&>(owner).make_child<PyArrayAccessor>(
413 AccessorTypeTag<ArrayPushInputNoInitialValue>{}, type, &owner, name, unit, nElements, description, tags);
414 },
415 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("nElements"),
416 py::arg("description"), py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference,
417 "");
418 m.def(
419 "ArrayPushInputNoInitialValue",
420 [](ChimeraTK::DataType::TheType type, VariableGroup& owner, const std::string& name, const std::string& unit,
421 size_t nElements, const std::string& description, const std::unordered_set<std::string>& tags) {
422 return dynamic_cast<PyOwningObject&>(owner).make_child<PyArrayAccessor>(
423 AccessorTypeTag<ArrayPushInputNoInitialValue>{}, DataType(type), &owner, name, unit, nElements,
424 description, tags);
425 },
426 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("nElements"),
427 py::arg("description"), py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference,
428 "");
429
433 m.def(
434 "ArrayPollInputNoInitialValue",
435 [](ChimeraTK::DataType type, VariableGroup& owner, const std::string& name, const std::string& unit,
436 size_t nElements, const std::string& description, const std::unordered_set<std::string>& tags) {
437 return dynamic_cast<PyOwningObject&>(owner).make_child<PyArrayAccessor>(
438 AccessorTypeTag<ArrayPollInputNoInitialValue>{}, type, &owner, name, unit, nElements, description, tags);
439 },
440 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("nElements"),
441 py::arg("description"), py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference,
442 "");
443 m.def(
444 "ArrayPollInputNoInitialValue",
445 [](ChimeraTK::DataType::TheType type, VariableGroup& owner, const std::string& name, const std::string& unit,
446 size_t nElements, const std::string& description, const std::unordered_set<std::string>& tags) {
447 return dynamic_cast<PyOwningObject&>(owner).make_child<PyArrayAccessor>(
448 AccessorTypeTag<ArrayPollInputNoInitialValue>{}, DataType(type), &owner, name, unit, nElements,
449 description, tags);
450 },
451 py::arg("type"), py::arg("owner"), py::arg("name"), py::arg("unit"), py::arg("nElements"),
452 py::arg("description"), py::arg("tags") = std::unordered_set<std::string>{}, py::return_value_policy::reference,
453 "");
454 }
455
456 /********************************************************************************************************************/
457
458} // namespace ChimeraTK
Helper class acting as a ArrayAccessor with a variant UserType.
py::object getitem(size_t index) const
void set(const UserTypeTemplateVariantNoVoid< Vector > &vec)
static void bind(py::module &mod)
void setAndWrite(const UserTypeTemplateVariantNoVoid< Vector > &vec)
void setslice(const py::slice &slice, const UserTypeVariantNoVoid &val)
static std::string repr(py::object &acc)
void setitem(size_t index, const UserTypeVariantNoVoid &val)
py::buffer_info getBufferInfo()
ArrayAccessorVariant _accessor
py::object getattr(const std::string &name) const
Base class used for all objects in the Python world which can own other objects and can be owned them...
static const std::set< std::string > specialUnaryFunctionsToEmulateNumeric
static const std::set< std::string > specialFunctionsToEmulateNumeric
const TransferElementAbstractor & getTE() const final
InvalidityTracer application module.
typename detail::NestedVariant< UserTypeTemplateVariantNoVoid< ArrayAccessor >, UserTypeTemplateVariantNoVoid< ArrayPollInput >, UserTypeTemplateVariantNoVoid< ArrayPushInput >, UserTypeTemplateVariantNoVoid< ArrayOutput >, UserTypeTemplateVariantNoVoid< ArrayPushInputWB >, UserTypeTemplateVariantNoVoid< ArrayOutputPushRB >, UserTypeTemplateVariantNoVoid< ArrayOutputReverseRecovery >, UserTypeTemplateVariantNoVoid< ArrayPushInputNoInitialValue >, UserTypeTemplateVariantNoVoid< ArrayPollInputNoInitialValue > >::type ArrayAccessorVariant
All concrete array accessor types used by the Python bindings.
module_ module