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
15namespace CPyCppyy {
16
17//= float type allowed for reference passing =================================
18PyTypeObject 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 ==================================
40PyTypeObject 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 ================
96static PyMethodObject* free_list;
97static 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//-----------------------------------------------------------------------------
143static 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//-----------------------------------------------------------------------------
168static 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//-----------------------------------------------------------------------------
212static 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
267static 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
284PyTypeObject 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;
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
343PyTypeObject 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
int Py_ssize_t
Definition: CPyCppyy.h:228
#define PyVarObject_HEAD_INIT(type, size)
Definition: CPyCppyy.h:207
#define PyMethod_MAXFREELIST
_object PyObject
Definition: PyMethodBase.h:41
typedef void((*Func_t)())
virtual bool HasState()
Definition: API.h:105
virtual PyObject * FromMemory(void *address)
Definition: Converters.cxx:422
PyObject * gLifeLine
Definition: PyStrings.cxx:23
PyObject * gGetItem
Definition: PyStrings.cxx:18
PyObject * gGetNoCheck
Definition: PyStrings.cxx:19
static PyObject * indexiter_iternext(indexiterobject *ii)
PyObject * CustomInstanceMethod_New(PyObject *func, PyObject *self, PyObject *pyclass)
PyTypeObject VectorIter_Type
PyTypeObject CustomInstanceMethod_Type
static PyMethodObject * free_list
PyTypeObject RefFloat_Type
Custom "builtins," detectable by type, for pass by ref and improved performance.
static int numfree
PyTypeObject TypedefPointerToClass_Type
PyObject * BindCppObjectNoCast(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
static void vectoriter_dealloc(vectoriterobject *vi)
static int indexiter_traverse(indexiterobject *ii, visitproc visit, void *arg)
static PyObject * im_descr_get(PyObject *meth, PyObject *obj, PyObject *pyclass)
static PyObject * vectoriter_iternext(vectoriterobject *vi)
bool CPPInstance_Check(T *object)
Definition: CPPInstance.h:118
PyTypeObject IndexIter_Type
static void indexiter_dealloc(indexiterobject *ii)
static PyObject * tpc_call(typedefpointertoclassobject *self, PyObject *args, PyObject *)
static void im_dealloc(PyMethodObject *im)
static PyObject * im_call(PyObject *meth, PyObject *args, PyObject *kw)
PyTypeObject RefInt_Type
void * TCppObject_t
Definition: cpp_cppyy.h:21
PyObject_HEAD PyObject * ii_container
Definition: CustomPyTypes.h:80
PyObject_HEAD Cppyy::TCppType_t fType
Definition: CustomPyTypes.h:43
Cppyy::TCppType_t vi_klass
Definition: CustomPyTypes.h:92
CPyCppyy::Converter * vi_converter
Definition: CustomPyTypes.h:91