Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
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, // tp_flags
82 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
83#if PY_VERSION_HEX >= 0x02030000
84 , 0 // tp_del
85#endif
86#if PY_VERSION_HEX >= 0x02060000
87 , 0 // tp_version_tag
88#endif
89#if PY_VERSION_HEX >= 0x03040000
90 , 0 // tp_finalize
91#endif
92};
93
94//= instancemethod object with a more efficient call function ================
95static PyMethodObject* free_list;
96static int numfree = 0;
97#ifndef PyMethod_MAXFREELIST
98#define PyMethod_MAXFREELIST 256
99#endif
100
101//-----------------------------------------------------------------------------
103#if PY_VERSION_HEX < 0x03000000
104 pyclass
105#endif
106 )
107{
108// from instancemethod, but with custom type (at issue is that instancemethod is not
109// meant to be derived from)
110 PyMethodObject* im;
111 if (!PyCallable_Check(func)) {
112 PyErr_Format(PyExc_SystemError,
113 "%s:%d: bad argument to internal function", __FILE__, __LINE__);
114 return nullptr;
115 }
116
117 im = free_list;
118 if (im != nullptr) {
119 free_list = (PyMethodObject*)(im->im_self);
120 (void)PyObject_INIT(im, &CustomInstanceMethod_Type);
121 }
122 else {
123 im = PyObject_GC_New(PyMethodObject, &CustomInstanceMethod_Type);
124 if (im == nullptr)
125 return nullptr;
126 }
127
128 im->im_weakreflist = nullptr;
129 Py_INCREF(func);
130 im->im_func = func;
131 Py_XINCREF(self);
132 im->im_self = self;
133#if PY_VERSION_HEX < 0x03000000
134 Py_XINCREF(pyclass);
135 im->im_class = pyclass;
136#endif
137 PyObject_GC_Track(im);
138 return (PyObject*)im;
139}
140
141//-----------------------------------------------------------------------------
142static void im_dealloc(PyMethodObject* im)
143{
144// from instancemethod, but with custom type (at issue is that instancemethod is not
145// meant to be derived from)
146 PyObject_GC_UnTrack(im);
147
148 if (im->im_weakreflist != nullptr)
149 PyObject_ClearWeakRefs((PyObject*)im);
150
151 Py_DECREF(im->im_func);
152 Py_XDECREF(im->im_self);
153#if PY_VERSION_HEX < 0x03000000
154 Py_XDECREF(im->im_class);
155#endif
156
158 im->im_self = (PyObject*)free_list;
159 free_list = im;
160 numfree++;
161 } else {
162 PyObject_GC_Del(im);
163 }
164}
165
166//-----------------------------------------------------------------------------
167static PyObject* im_call(PyObject* meth, PyObject* args, PyObject* kw)
168{
169// The mapping from a method to a function involves reshuffling of self back
170// into the list of arguments. However, the pythonized methods will then have
171// to undo that shuffling, which is inefficient. This method is the same as
172// the one for the instancemethod object, except for the shuffling.
173 PyObject* self = PyMethod_GET_SELF(meth);
174
175 if (!self) {
176 // unbound methods must be called with an instance of the class (or a
177 // derived class) as first argument
178 Py_ssize_t argc = PyTuple_GET_SIZE(args);
179 PyObject* pyclass = PyMethod_GET_CLASS(meth);
180 if (1 <= argc && PyObject_IsInstance(PyTuple_GET_ITEM(args, 0), pyclass) == 1) {
181 self = PyTuple_GET_ITEM(args, 0);
182
183 PyObject* newArgs = PyTuple_New(argc-1);
184 for (int i = 1; i < argc; ++i) {
185 PyObject* v = PyTuple_GET_ITEM(args, i);
186 Py_INCREF(v);
187 PyTuple_SET_ITEM(newArgs, i-1, v);
188 }
189
190 args = newArgs;
191
192 } else
193 return PyMethod_Type.tp_call(meth, args, kw); // will set proper error msg
194
195 } else
196 Py_INCREF(args);
197
198 PyCFunctionObject* func = (PyCFunctionObject*)PyMethod_GET_FUNCTION(meth);
199
200// the function is globally shared, so set and reset its "self" (ok, b/c of GIL)
201 Py_INCREF(self);
202 func->m_self = self;
203 PyObject* result = CPyCppyy_PyCFunction_Call((PyObject*)func, args, kw);
204 func->m_self = nullptr;
205 Py_DECREF(self);
206 Py_DECREF(args);
207 return result;
208}
209
210//-----------------------------------------------------------------------------
211static PyObject* im_descr_get(PyObject* meth, PyObject* obj, PyObject* pyclass)
212{
213// from instancemethod: don't rebind an already bound method, or an unbound method
214// of a class that's not a base class of pyclass
215 if (PyMethod_GET_SELF(meth)
216#if PY_VERSION_HEX < 0x03000000
217 || (PyMethod_GET_CLASS(meth) &&
218 !PyObject_IsSubclass(pyclass, PyMethod_GET_CLASS(meth)))
219#endif
220 ) {
221 Py_INCREF(meth);
222 return meth;
223 }
224
225 if (obj == Py_None)
226 obj = nullptr;
227
228 return CustomInstanceMethod_New(PyMethod_GET_FUNCTION(meth), obj, pyclass);
229}
230
231//= CPyCppyy custom instance method type =====================================
233 PyVarObject_HEAD_INIT(&PyType_Type, 0)
234 (char*)"cppyy.InstanceMethod", // tp_name
235 0, 0,
236 (destructor)im_dealloc, // tp_dealloc
237 0, 0, 0, 0, 0, 0, 0, 0, 0,
238 im_call, // tp_call
239 0, 0, 0, 0,
240 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
241 Py_TPFLAGS_BASETYPE, // tp_flags
242 (char*)"CPyCppyy custom instance method (internal)", // tp_doc
243 0, 0, 0, 0, 0, 0, 0, 0, 0,
244 &PyMethod_Type, // tp_base
245 0,
246 im_descr_get, // tp_descr_get
247 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
248#if PY_VERSION_HEX >= 0x02030000
249 , 0 // tp_del
250#endif
251#if PY_VERSION_HEX >= 0x02060000
252 , 0 // tp_version_tag
253#endif
254#if PY_VERSION_HEX >= 0x03040000
255 , 0 // tp_finalize
256#endif
257};
258
259
260//= CPyCppyy custom iterator for performance =================================
262 Py_XDECREF(ii->ii_container);
263 PyObject_GC_Del(ii);
264}
265
266static int indexiter_traverse(indexiterobject* ii, visitproc visit, void* arg) {
267 Py_VISIT(ii->ii_container);
268 return 0;
269}
270
272 if (ii->ii_pos >= ii->ii_len)
273 return nullptr;
274
275 PyObject* pyindex = PyLong_FromSsize_t(ii->ii_pos);
276 PyObject* result = PyObject_CallMethodObjArgs((PyObject*)ii->ii_container, PyStrings::gGetItem, pyindex, nullptr);
277 Py_DECREF(pyindex);
278
279 ii->ii_pos += 1;
280 return result;
281}
282
283PyTypeObject IndexIter_Type = {
284 PyVarObject_HEAD_INIT(&PyType_Type, 0)
285 (char*)"cppyy.indexiter", // tp_name
286 sizeof(indexiterobject), // tp_basicsize
287 0,
288 (destructor)indexiter_dealloc, // tp_dealloc
289 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
290 Py_TPFLAGS_DEFAULT |
291 Py_TPFLAGS_HAVE_GC, // tp_flags
292 0,
293 (traverseproc)indexiter_traverse, // tp_traverse
294 0, 0, 0,
295 PyObject_SelfIter, // tp_iter
296 (iternextfunc)indexiter_iternext, // tp_iternext
297 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
298#if PY_VERSION_HEX >= 0x02030000
299 , 0 // tp_del
300#endif
301#if PY_VERSION_HEX >= 0x02060000
302 , 0 // tp_version_tag
303#endif
304#if PY_VERSION_HEX >= 0x03040000
305 , 0 // tp_finalize
306#endif
307};
308
309
311 if (vi->vi_converter && vi->vi_converter->HasState()) delete vi->vi_converter;
313}
314
316 if (vi->ii_pos >= vi->ii_len)
317 return nullptr;
318
319 PyObject* result = nullptr;
320
321 if (vi->vi_data && vi->vi_converter) {
322 void* location = (void*)((ptrdiff_t)vi->vi_data + vi->vi_stride * vi->ii_pos);
323 result = vi->vi_converter->FromMemory(location);
324 } else if (vi->vi_data && vi->vi_klass) {
325 // The CPPInstance::kNoMemReg by-passes the memory regulator; the assumption here is
326 // that objects in vectors are simple and thus do not need to maintain object identity
327 // (or at least not during the loop anyway). This gains 2x in performance.
328 Cppyy::TCppObject_t cppobj = (Cppyy::TCppObject_t)((ptrdiff_t)vi->vi_data + vi->vi_stride * vi->ii_pos);
330 if (vi->vi_flags && CPPInstance_Check(result))
331 PyObject_SetAttr(result, PyStrings::gLifeLine, vi->ii_container);
332 } else {
333 PyObject* pyindex = PyLong_FromSsize_t(vi->ii_pos);
334 result = PyObject_CallMethodObjArgs((PyObject*)vi->ii_container, PyStrings::gGetNoCheck, pyindex, nullptr);
335 Py_DECREF(pyindex);
336 }
337
338 vi->ii_pos += 1;
339 return result;
340}
341
342PyTypeObject VectorIter_Type = {
343 PyVarObject_HEAD_INIT(&PyType_Type, 0)
344 (char*)"cppyy.vectoriter", // tp_name
345 sizeof(vectoriterobject), // tp_basicsize
346 0,
347 (destructor)vectoriter_dealloc, // tp_dealloc
348 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
349 Py_TPFLAGS_DEFAULT |
350 Py_TPFLAGS_HAVE_GC, // tp_flags
351 0,
352 (traverseproc)indexiter_traverse, // tp_traverse
353 0, 0, 0,
354 PyObject_SelfIter, // tp_iter
355 (iternextfunc)vectoriter_iternext, // tp_iternext
356 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
357#if PY_VERSION_HEX >= 0x02030000
358 , 0 // tp_del
359#endif
360#if PY_VERSION_HEX >= 0x02060000
361 , 0 // tp_version_tag
362#endif
363#if PY_VERSION_HEX >= 0x03040000
364 , 0 // tp_finalize
365#endif
366};
367
368} // namespace CPyCppyy
int Py_ssize_t
Definition CPyCppyy.h:236
#define CPyCppyy_PyCFunction_Call
Definition CPyCppyy.h:304
#define PyVarObject_HEAD_INIT(type, size)
Definition CPyCppyy.h:215
#define PyMethod_MAXFREELIST
typedef void(GLAPIENTRYP _GLUfuncptr)(void)
_object PyObject
virtual bool HasState()
Definition API.h:105
virtual PyObject * FromMemory(void *address)
PyObject * gLifeLine
Definition PyStrings.cxx:23
PyObject * gGetItem
Definition PyStrings.cxx:18
PyObject * gGetNoCheck
Definition PyStrings.cxx:19
Set of helper functions that are invoked from the pythonizors, on the Python side.
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)
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
PyObject_HEAD Cppyy::TCppType_t fType
Cppyy::TCppType_t vi_klass
CPyCppyy::Converter * vi_converter