Logo ROOT  
Reference Guide
CustomPyTypes.cxx
Go to the documentation of this file.
1 // Bindings
2 #include "CPyCppyy.h"
3 #include "CustomPyTypes.h"
4 #include "CPPInstance.h"
5 #include "Converters.h"
6 #include "ProxyWrappers.h"
7 #include "PyStrings.h"
8 
9 #if PY_VERSION_HEX >= 0x03000000
10 // TODO: this will break functionality
11 #define PyMethod_GET_CLASS(meth) Py_None
12 #endif
13 
14 
15 namespace CPyCppyy {
16 
17 //= float type allowed for reference passing =================================
18 PyTypeObject RefFloat_Type = { // python float is a C/C++ double
19  PyVarObject_HEAD_INIT(&PyType_Type, 0)
20  (char*)"cppyy.Double", // tp_name
21  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
22  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
23  Py_TPFLAGS_BASETYPE, // tp_flags
24  (char*)"CPyCppyy float object for pass by reference", // tp_doc
25  0, 0, 0, 0, 0, 0, 0, 0, 0,
26  &PyFloat_Type, // tp_base
27  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
28 #if PY_VERSION_HEX >= 0x02030000
29  , 0 // tp_del
30 #endif
31 #if PY_VERSION_HEX >= 0x02060000
32  , 0 // tp_version_tag
33 #endif
34 #if PY_VERSION_HEX >= 0x03040000
35  , 0 // tp_finalize
36 #endif
37 };
38 
39 //= long type allowed for reference passing ==================================
40 PyTypeObject RefInt_Type = { // python int is a C/C++ long
41  PyVarObject_HEAD_INIT(&PyType_Type, 0)
42  (char*)"cppyy.Long", // tp_name
43  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
44  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
45  Py_TPFLAGS_BASETYPE
46 #if PY_VERSION_HEX >= 0x03040000
47  | Py_TPFLAGS_LONG_SUBCLASS
48 #endif
49  , // tp_flags
50  (char*)"CPyCppyy long object for pass by reference", // tp_doc
51  0, 0, 0, 0, 0, 0, 0, 0, 0,
52  &PyInt_Type, // tp_base
53  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
54 #if PY_VERSION_HEX >= 0x02030000
55  , 0 // tp_del
56 #endif
57 #if PY_VERSION_HEX >= 0x02060000
58  , 0 // tp_version_tag
59 #endif
60 #if PY_VERSION_HEX >= 0x03040000
61  , 0 // tp_finalize
62 #endif
63 };
64 
65 //- custom type representing typedef to pointer of class ---------------------
67 {
68  long long addr = 0;
69  if (!PyArg_ParseTuple(args, const_cast<char*>("|L"), &addr))
70  return nullptr;
71  return BindCppObjectNoCast((Cppyy::TCppObject_t)(intptr_t)addr, self->fType);
72 }
73 
75  PyVarObject_HEAD_INIT(&PyType_Type, 0)
76  (char*)"cppyy.TypedefPointerToClass",// tp_name
77  sizeof(typedefpointertoclassobject), // tp_basicsize
78  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
79  (ternaryfunc)tpc_call, // tp_call
80  0, 0, 0, 0,
81  Py_TPFLAGS_DEFAULT |
82  Py_TPFLAGS_HAVE_GC, // tp_flags
83  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
84 #if PY_VERSION_HEX >= 0x02030000
85  , 0 // tp_del
86 #endif
87 #if PY_VERSION_HEX >= 0x02060000
88  , 0 // tp_version_tag
89 #endif
90 #if PY_VERSION_HEX >= 0x03040000
91  , 0 // tp_finalize
92 #endif
93 };
94 
95 //= instancemethod object with a more efficient call function ================
96 static PyMethodObject* free_list;
97 static int numfree = 0;
98 #ifndef PyMethod_MAXFREELIST
99 #define PyMethod_MAXFREELIST 256
100 #endif
101 
102 //-----------------------------------------------------------------------------
104 #if PY_VERSION_HEX < 0x03000000
105  pyclass
106 #endif
107  )
108 {
109 // from instancemethod, but with custom type (at issue is that instancemethod is not
110 // meant to be derived from)
111  PyMethodObject* im;
112  if (!PyCallable_Check(func)) {
113  PyErr_Format(PyExc_SystemError,
114  "%s:%d: bad argument to internal function", __FILE__, __LINE__);
115  return nullptr;
116  }
117 
118  im = free_list;
119  if (im != nullptr) {
120  free_list = (PyMethodObject*)(im->im_self);
121  (void)PyObject_INIT(im, &CustomInstanceMethod_Type);
122  }
123  else {
124  im = PyObject_GC_New(PyMethodObject, &CustomInstanceMethod_Type);
125  if (im == nullptr)
126  return nullptr;
127  }
128 
129  im->im_weakreflist = nullptr;
130  Py_INCREF(func);
131  im->im_func = func;
132  Py_XINCREF(self);
133  im->im_self = self;
134 #if PY_VERSION_HEX < 0x03000000
135  Py_XINCREF(pyclass);
136  im->im_class = pyclass;
137 #endif
138  PyObject_GC_Track(im);
139  return (PyObject*)im;
140 }
141 
142 //-----------------------------------------------------------------------------
143 static void im_dealloc(PyMethodObject* im)
144 {
145 // from instancemethod, but with custom type (at issue is that instancemethod is not
146 // meant to be derived from)
147  PyObject_GC_UnTrack(im);
148 
149  if (im->im_weakreflist != nullptr)
150  PyObject_ClearWeakRefs((PyObject*)im);
151 
152  Py_DECREF(im->im_func);
153  Py_XDECREF(im->im_self);
154 #if PY_VERSION_HEX < 0x03000000
155  Py_XDECREF(im->im_class);
156 #endif
157 
159  im->im_self = (PyObject*)free_list;
160  free_list = im;
161  numfree++;
162  } else {
163  PyObject_GC_Del(im);
164  }
165 }
166 
167 //-----------------------------------------------------------------------------
168 static PyObject* im_call(PyObject* meth, PyObject* args, PyObject* kw)
169 {
170 // The mapping from a method to a function involves reshuffling of self back
171 // into the list of arguments. However, the pythonized methods will then have
172 // to undo that shuffling, which is inefficient. This method is the same as
173 // the one for the instancemethod object, except for the shuffling.
174  PyObject* self = PyMethod_GET_SELF(meth);
175 
176  if (!self) {
177  // unbound methods must be called with an instance of the class (or a
178  // derived class) as first argument
179  Py_ssize_t argc = PyTuple_GET_SIZE(args);
180  PyObject* pyclass = PyMethod_GET_CLASS(meth);
181  if (1 <= argc && PyObject_IsInstance(PyTuple_GET_ITEM(args, 0), pyclass) == 1) {
182  self = PyTuple_GET_ITEM(args, 0);
183 
184  PyObject* newArgs = PyTuple_New(argc-1);
185  for (int i = 1; i < argc; ++i) {
186  PyObject* v = PyTuple_GET_ITEM(args, i);
187  Py_INCREF(v);
188  PyTuple_SET_ITEM(newArgs, i-1, v);
189  }
190 
191  args = newArgs;
192 
193  } else
194  return PyMethod_Type.tp_call(meth, args, kw); // will set proper error msg
195 
196  } else
197  Py_INCREF(args);
198 
199  PyCFunctionObject* func = (PyCFunctionObject*)PyMethod_GET_FUNCTION(meth);
200 
201 // the function is globally shared, so set and reset its "self" (ok, b/c of GIL)
202  Py_INCREF(self);
203  func->m_self = self;
204  PyObject* result = PyCFunction_Call((PyObject*)func, args, kw);
205  func->m_self = nullptr;
206  Py_DECREF(self);
207  Py_DECREF(args);
208  return result;
209 }
210 
211 //-----------------------------------------------------------------------------
212 static PyObject* im_descr_get(PyObject* meth, PyObject* obj, PyObject* pyclass)
213 {
214 // from instancemethod: don't rebind an already bound method, or an unbound method
215 // of a class that's not a base class of pyclass
216  if (PyMethod_GET_SELF(meth)
217 #if PY_VERSION_HEX < 0x03000000
218  || (PyMethod_GET_CLASS(meth) &&
219  !PyObject_IsSubclass(pyclass, PyMethod_GET_CLASS(meth)))
220 #endif
221  ) {
222  Py_INCREF(meth);
223  return meth;
224  }
225 
226  if (obj == Py_None)
227  obj = nullptr;
228 
229  return CustomInstanceMethod_New(PyMethod_GET_FUNCTION(meth), obj, pyclass);
230 }
231 
232 //= CPyCppyy custom instance method type =====================================
234  PyVarObject_HEAD_INIT(&PyType_Type, 0)
235  (char*)"cppyy.InstanceMethod", // tp_name
236  0, 0,
237  (destructor)im_dealloc, // tp_dealloc
238  0, 0, 0, 0, 0, 0, 0, 0, 0,
239  im_call, // tp_call
240  0, 0, 0, 0,
241  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
242  Py_TPFLAGS_BASETYPE, // tp_flags
243  (char*)"CPyCppyy custom instance method (internal)", // tp_doc
244  0, 0, 0, 0, 0, 0, 0, 0, 0,
245  &PyMethod_Type, // tp_base
246  0,
247  im_descr_get, // tp_descr_get
248  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
249 #if PY_VERSION_HEX >= 0x02030000
250  , 0 // tp_del
251 #endif
252 #if PY_VERSION_HEX >= 0x02060000
253  , 0 // tp_version_tag
254 #endif
255 #if PY_VERSION_HEX >= 0x03040000
256  , 0 // tp_finalize
257 #endif
258 };
259 
260 
261 //= CPyCppyy custom iterator for performance =================================
263  Py_XDECREF(ii->ii_container);
264  PyObject_GC_Del(ii);
265 }
266 
267 static int indexiter_traverse(indexiterobject* ii, visitproc visit, void* arg) {
268  Py_VISIT(ii->ii_container);
269  return 0;
270 }
271 
273  if (ii->ii_pos >= ii->ii_len)
274  return nullptr;
275 
276  PyObject* pyindex = PyLong_FromSsize_t(ii->ii_pos);
277  PyObject* result = PyObject_CallMethodObjArgs((PyObject*)ii->ii_container, PyStrings::gGetItem, pyindex, nullptr);
278  Py_DECREF(pyindex);
279 
280  ii->ii_pos += 1;
281  return result;
282 }
283 
284 PyTypeObject IndexIter_Type = {
285  PyVarObject_HEAD_INIT(&PyType_Type, 0)
286  (char*)"cppyy.indexiter", // tp_name
287  sizeof(indexiterobject), // tp_basicsize
288  0,
289  (destructor)indexiter_dealloc, // tp_dealloc
290  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
291  Py_TPFLAGS_DEFAULT |
292  Py_TPFLAGS_HAVE_GC, // tp_flags
293  0,
294  (traverseproc)indexiter_traverse, // tp_traverse
295  0, 0, 0,
296  PyObject_SelfIter, // tp_iter
297  (iternextfunc)indexiter_iternext, // tp_iternext
298  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
299 #if PY_VERSION_HEX >= 0x02030000
300  , 0 // tp_del
301 #endif
302 #if PY_VERSION_HEX >= 0x02060000
303  , 0 // tp_version_tag
304 #endif
305 #if PY_VERSION_HEX >= 0x03040000
306  , 0 // tp_finalize
307 #endif
308 };
309 
310 
312  if (vi->vi_converter && vi->vi_converter->HasState()) delete vi->vi_converter;
313  indexiter_dealloc(vi);
314 }
315 
317  if (vi->ii_pos >= vi->ii_len)
318  return nullptr;
319 
320  PyObject* result = nullptr;
321 
322  if (vi->vi_data && vi->vi_converter) {
323  void* location = (void*)((ptrdiff_t)vi->vi_data + vi->vi_stride * vi->ii_pos);
324  result = vi->vi_converter->FromMemory(location);
325  } else if (vi->vi_data && vi->vi_klass) {
326  // The CPPInstance::kNoMemReg by-passes the memory regulator; the assumption here is
327  // that objects in vectors are simple and thus do not need to maintain object identity
328  // (or at least not during the loop anyway). This gains 2x in performance.
329  Cppyy::TCppObject_t cppobj = (Cppyy::TCppObject_t)((ptrdiff_t)vi->vi_data + vi->vi_stride * vi->ii_pos);
331  if (vi->vi_flags && CPPInstance_Check(result))
332  PyObject_SetAttr(result, PyStrings::gLifeLine, vi->ii_container);
333  } else {
334  PyObject* pyindex = PyLong_FromSsize_t(vi->ii_pos);
335  result = PyObject_CallMethodObjArgs((PyObject*)vi->ii_container, PyStrings::gGetNoCheck, pyindex, nullptr);
336  Py_DECREF(pyindex);
337  }
338 
339  vi->ii_pos += 1;
340  return result;
341 }
342 
343 PyTypeObject VectorIter_Type = {
344  PyVarObject_HEAD_INIT(&PyType_Type, 0)
345  (char*)"cppyy.vectoriter", // tp_name
346  sizeof(vectoriterobject), // tp_basicsize
347  0,
348  (destructor)vectoriter_dealloc, // tp_dealloc
349  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
350  Py_TPFLAGS_DEFAULT |
351  Py_TPFLAGS_HAVE_GC, // tp_flags
352  0,
353  (traverseproc)indexiter_traverse, // tp_traverse
354  0, 0, 0,
355  PyObject_SelfIter, // tp_iter
356  (iternextfunc)vectoriter_iternext, // tp_iternext
357  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
358 #if PY_VERSION_HEX >= 0x02030000
359  , 0 // tp_del
360 #endif
361 #if PY_VERSION_HEX >= 0x02060000
362  , 0 // tp_version_tag
363 #endif
364 #if PY_VERSION_HEX >= 0x03040000
365  , 0 // tp_finalize
366 #endif
367 };
368 
369 } // namespace CPyCppyy
PyTypeObject IndexIter_Type
static PyObject * indexiter_iternext(indexiterobject *ii)
CPyCppyy::Converter * vi_converter
Definition: CustomPyTypes.h:91
bool CPPInstance_Check(T *object)
Definition: CPPInstance.h:118
Cppyy::TCppType_t vi_klass
Definition: CustomPyTypes.h:92
PyTypeObject VectorIter_Type
PyObject * CustomInstanceMethod_New(PyObject *func, PyObject *self, PyObject *pyclass)
PyTypeObject TypedefPointerToClass_Type
PyObject * gLifeLine
Definition: PyStrings.cxx:23
PyObject * gGetItem
Definition: PyStrings.cxx:18
#define PyVarObject_HEAD_INIT(type, size)
Definition: CPyCppyy.h:207
static void im_dealloc(PyMethodObject *im)
PyObject * BindCppObjectNoCast(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
static void vectoriter_dealloc(vectoriterobject *vi)
virtual PyObject * FromMemory(void *address)
Definition: Converters.cxx:422
#define PyMethod_MAXFREELIST
PyTypeObject RefInt_Type
PyTypeObject CustomInstanceMethod_Type
_object PyObject
Definition: PyMethodBase.h:41
static PyMethodObject * free_list
int Py_ssize_t
Definition: CPyCppyy.h:228
static PyObject * im_call(PyObject *meth, PyObject *args, PyObject *kw)
static int numfree
static PyObject * im_descr_get(PyObject *meth, PyObject *obj, PyObject *pyclass)
PyTypeObject RefFloat_Type
Custom "builtins," detectable by type, for pass by ref and improved performance.
static int indexiter_traverse(indexiterobject *ii, visitproc visit, void *arg)
virtual bool HasState()
Definition: API.h:105
static PyObject * vectoriter_iternext(vectoriterobject *vi)
void * TCppObject_t
Definition: cpp_cppyy.h:21
typedef void((*Func_t)())
static void indexiter_dealloc(indexiterobject *ii)
PyObject * gGetNoCheck
Definition: PyStrings.cxx:19
static PyObject * tpc_call(typedefpointertoclassobject *self, PyObject *args, PyObject *)
PyObject_HEAD PyObject * ii_container
Definition: CustomPyTypes.h:80