Logo ROOT  
Reference Guide
TupleOfInstances.cxx
Go to the documentation of this file.
1// Bindings
2#include "CPyCppyy.h"
3#include "TupleOfInstances.h"
4#include "ProxyWrappers.h"
5
6
7namespace {
8
9typedef struct {
10 PyObject_HEAD
11 Cppyy::TCppType_t ia_klass;
12 void* ia_array_start;
13 Py_ssize_t ia_pos;
14 Py_ssize_t ia_len;
15 Py_ssize_t ia_stride;
16} ia_iterobject;
17
18static PyObject* ia_iternext(ia_iterobject* ia) {
19 if (ia->ia_len != -1 && ia->ia_pos >= ia->ia_len) {
20 ia->ia_pos = 0; // debatable, but since the iterator is cached, this
21 return nullptr; // allows for multiple conversions to e.g. a tuple
22 } else if (ia->ia_stride == 0 && ia->ia_pos != 0) {
23 PyErr_SetString(PyExc_ReferenceError, "no stride available for indexing");
24 return nullptr;
25 }
27 (char*)ia->ia_array_start + ia->ia_pos*ia->ia_stride, ia->ia_klass);
28 ia->ia_pos += 1;
29 return result;
30}
31
32static int ia_traverse(ia_iterobject*, visitproc, void*) {
33 return 0;
34}
35
36static PyObject* ia_getsize(ia_iterobject* ia, void*) {
37 return PyInt_FromSsize_t(ia->ia_len);
38}
39
40static int ia_setsize(ia_iterobject* ia, PyObject* pysize, void*) {
42 if (size == (Py_ssize_t)-1 && PyErr_Occurred())
43 return -1;
44 ia->ia_len = size;
45 return 0;
46}
47
48static PyGetSetDef ia_getset[] = {
49 {(char*)"size", (getter)ia_getsize, (setter)ia_setsize,
50 (char*)"set size of array to which this iterator refers", nullptr},
51 {(char*)nullptr, nullptr, nullptr, nullptr, nullptr}
52};
53
54} // unnamed namespace
55
56
57namespace CPyCppyy {
58
59PyTypeObject InstanceArrayIter_Type = {
60 PyVarObject_HEAD_INIT(&PyType_Type, 0)
61 (char*)"cppyy.instancearrayiter", // tp_name
62 sizeof(ia_iterobject), // tp_basicsize
63 0,
64 (destructor)PyObject_GC_Del, // tp_dealloc
65 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
66 Py_TPFLAGS_DEFAULT |
67 Py_TPFLAGS_HAVE_GC, // tp_flags
68 0,
69 (traverseproc)ia_traverse, // tp_traverse
70 0, 0, 0,
71 PyObject_SelfIter, // tp_iter
72 (iternextfunc)ia_iternext, // tp_iternext
73 0, 0, ia_getset, 0, 0, 0, 0,
74 0, // tp_getset
75 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
76#if PY_VERSION_HEX >= 0x02030000
77 , 0 // tp_del
78#endif
79#if PY_VERSION_HEX >= 0x02060000
80 , 0 // tp_version_tag
81#endif
82#if PY_VERSION_HEX >= 0x03040000
83 , 0 // tp_finalize
84#endif
85};
86
87
88//= support for C-style arrays of objects ====================================
90 Cppyy::TCppObject_t address, Cppyy::TCppType_t klass, dim_t ndims, dims_t dims)
91{
92// recursively set up tuples of instances on all dimensions
93 if (ndims == -1 /* unknown shape */ || dims[0] == -1 /* unknown size */) {
94 // no known length ... return an iterable object and let the user figure it out
95 ia_iterobject* ia = PyObject_GC_New(ia_iterobject, &InstanceArrayIter_Type);
96 if (!ia) return nullptr;
97
98 ia->ia_klass = klass;
99 ia->ia_array_start = address;
100 ia->ia_pos = 0;
101 ia->ia_len = -1;
102 ia->ia_stride = Cppyy::SizeOf(klass);
103
104 PyObject_GC_Track(ia);
105 return (PyObject*)ia;
106 } else if (1 < ndims) {
107 // not the innermost dimension, descend one level
108 int nelems = (int)dims[0];
109 size_t block_size = 0;
110 for (int i = 1; i < (int)ndims; ++i) block_size += (size_t)dims[i];
111 block_size *= Cppyy::SizeOf(klass);
112
113 PyObject* tup = PyTuple_New(nelems);
114 for (int i = 0; i < nelems; ++i) {
115 PyTuple_SetItem(tup, i, TupleOfInstances_New(
116 (char*)address + i*block_size, klass, ndims-1, dims+1));
117 }
118 return tup;
119 } else {
120 // innermost dimension: construct tuple
121 int nelems = (int)dims[0];
122 size_t block_size = Cppyy::SizeOf(klass);
123 if (block_size == 0) {
124 PyErr_Format(PyExc_TypeError,
125 "can not determine size of type \"%s\" for array indexing",
126 Cppyy::GetScopedFinalName(klass).c_str());
127 return nullptr;
128 }
129
130 // TODO: the extra copy is inefficient, but it appears that the only way to
131 // initialize a subclass of a tuple is through a sequence
132 PyObject* tup = PyTuple_New(nelems);
133 for (int i = 0; i < nelems; ++i) {
134 // TODO: there's an assumption here that there is no padding, which is bound
135 // to be incorrect in certain cases
136 PyTuple_SetItem(tup, i,
137 BindCppObjectNoCast((char*)address + i*block_size, klass));
138 // Note: objects are bound as pointers, yet since the pointer value stays in
139 // place, updates propagate just as if they were bound by-reference
140 }
141
142 PyObject* args = PyTuple_New(1);
143 Py_INCREF(tup); PyTuple_SET_ITEM(args, 0, tup);
144 PyObject* arr = PyTuple_Type.tp_new(&TupleOfInstances_Type, args, nullptr);
145 if (PyErr_Occurred()) PyErr_Print();
146
147 Py_DECREF(args);
148 // tup ref eaten by SET_ITEM on args
149
150 return arr;
151 }
152
153// never get here
154 return nullptr;
155}
156
157//= CPyCppyy custom tuple-like array type ====================================
158PyTypeObject TupleOfInstances_Type = {
159 PyVarObject_HEAD_INIT(&PyType_Type, 0)
160 (char*)"cppyy.InstancesArray", // tp_name
161 0, // tp_basicsize
162 0, // tp_itemsize
163 0, // tp_dealloc
164 0, // tp_print
165 0, // tp_getattr
166 0, // tp_setattr
167 0, // tp_compare
168 0, // tp_repr
169 0, // tp_as_number
170 0, // tp_as_sequence
171 0, // tp_as_mapping
172 0, // tp_hash
173 0, // tp_call
174 0, // tp_str
175 0, // tp_getattro
176 0, // tp_setattro
177 0, // tp_as_buffer
178 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
179 Py_TPFLAGS_BASETYPE, // tp_flags
180 (char*)"array of C++ instances", // tp_doc
181 0, // tp_traverse
182 0, // tp_clear
183 0, // tp_richcompare
184 0, // tp_weaklistoffset
185 0, // tp_iter
186 0, // tp_iternext
187 0, // tp_methods
188 0, // tp_members
189 0, // tp_getset
190 &PyTuple_Type, // tp_base
191 0, // tp_dict
192 0, // tp_descr_get
193 0, // tp_descr_set
194 0, // tp_dictoffset
195 0, // tp_init
196 0, // tp_alloc
197 0, // tp_new
198 0, // tp_free
199 0, // tp_is_gc
200 0, // tp_bases
201 0, // tp_mro
202 0, // tp_cache
203 0, // tp_subclasses
204 0 // tp_weaklist
205#if PY_VERSION_HEX >= 0x02030000
206 , 0 // tp_del
207#endif
208#if PY_VERSION_HEX >= 0x02060000
209 , 0 // tp_version_tag
210#endif
211#if PY_VERSION_HEX >= 0x03040000
212 , 0 // tp_finalize
213#endif
214};
215
216} // namespace CPyCppyy
dim_t * dims_t
Definition: CPyCppyy.h:65
#define PyInt_FromSsize_t
Definition: CPyCppyy.h:238
int Py_ssize_t
Definition: CPyCppyy.h:236
#define PyInt_AsSsize_t
Definition: CPyCppyy.h:237
Py_ssize_t dim_t
Definition: CPyCppyy.h:64
#define PyVarObject_HEAD_INIT(type, size)
Definition: CPyCppyy.h:215
_object PyObject
Definition: PyMethodBase.h:42
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
Set of helper functions that are invoked from the pythonizors, on the Python side.
PyObject * BindCppObjectNoCast(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
PyObject * TupleOfInstances_New(Cppyy::TCppObject_t address, Cppyy::TCppType_t klass, dim_t ndims, dims_t dims)
PyTypeObject InstanceArrayIter_Type
PyTypeObject TupleOfInstances_Type
Representation of C-style array of instances.
void * TCppObject_t
Definition: cpp_cppyy.h:21
TCppScope_t TCppType_t
Definition: cpp_cppyy.h:19
RPY_EXPORTED size_t SizeOf(TCppType_t klass)
RPY_EXPORTED std::string GetScopedFinalName(TCppType_t type)