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 
7 namespace {
8 
9 typedef 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 
18 static 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 
32 static int ia_traverse(ia_iterobject*, visitproc, void*) {
33  return 0;
34 }
35 
36 static PyObject* ia_getsize(ia_iterobject* ia, void*) {
37  return PyInt_FromSsize_t(ia->ia_len);
38 }
39 
40 static int ia_setsize(ia_iterobject* ia, PyObject* pysize, void*) {
41  Py_ssize_t size = PyInt_AsSsize_t(pysize);
42  if (size == (Py_ssize_t)-1 && PyErr_Occurred())
43  return -1;
44  ia->ia_len = size;
45  return 0;
46 }
47 
48 static 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 
57 namespace CPyCppyy {
58 
59 PyTypeObject 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 ====================================
158 PyTypeObject 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
Py_ssize_t dim_t
Definition: CPyCppyy.h:64
PyTypeObject InstanceArrayIter_Type
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)
#define PyVarObject_HEAD_INIT(type, size)
Definition: CPyCppyy.h:207
PyObject * BindCppObjectNoCast(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
dim_t * dims_t
Definition: CPyCppyy.h:65
#define PyInt_FromSsize_t
Definition: CPyCppyy.h:230
PyObject * TupleOfInstances_New(Cppyy::TCppObject_t address, Cppyy::TCppType_t klass, dim_t ndims, dims_t dims)
#define PyInt_AsSsize_t
Definition: CPyCppyy.h:229
_object PyObject
Definition: PyMethodBase.h:41
int Py_ssize_t
Definition: CPyCppyy.h:228
void * TCppObject_t
Definition: cpp_cppyy.h:21
PyTypeObject TupleOfInstances_Type
Representation of C-style array of instances.