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// As of Python 3.12, we can't use the PyMethod_GET_FUNCTION and
10// PyMethod_GET_SELF macros anymore, as the contain asserts that check if the
11// Python type is actually PyMethod_Type. If the Python type is
12// CustomInstanceMethod_Type, we need our own macros. Technically they do they
13// same, because the actual C++ type of the PyObject is PyMethodObject anyway.
14#define CustomInstanceMethod_GET_SELF(meth) reinterpret_cast<PyMethodObject *>(meth)->im_self
15#define CustomInstanceMethod_GET_FUNCTION(meth) reinterpret_cast<PyMethodObject *>(meth)->im_func
16#if PY_VERSION_HEX >= 0x03000000
17// TODO: this will break functionality
18#define CustomInstanceMethod_GET_CLASS(meth) Py_None
19#else
20#define CustomInstanceMethod_GET_CLASS(meth) PyMethod_GET_CLASS(meth)
21#endif
22
23namespace CPyCppyy {
24
25#if PY_VERSION_HEX < 0x03000000
26//= float type allowed for reference passing =================================
27PyTypeObject RefFloat_Type = { // python float is a C/C++ double
28 PyVarObject_HEAD_INIT(&PyType_Type, 0)
29 (char*)"cppyy.Double", // tp_name
30 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
31 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
32 Py_TPFLAGS_BASETYPE, // tp_flags
33 (char*)"CPyCppyy float object for pass by reference", // tp_doc
34 0, 0, 0, 0, 0, 0, 0, 0, 0,
35 &PyFloat_Type, // tp_base
36 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
37#if PY_VERSION_HEX >= 0x02030000
38 , 0 // tp_del
39#endif
40#if PY_VERSION_HEX >= 0x02060000
41 , 0 // tp_version_tag
42#endif
43#if PY_VERSION_HEX >= 0x03040000
44 , 0 // tp_finalize
45#endif
46};
47
48//= long type allowed for reference passing ==================================
49PyTypeObject RefInt_Type = { // python int is a C/C++ long
50 PyVarObject_HEAD_INIT(&PyType_Type, 0)
51 (char*)"cppyy.Long", // tp_name
52 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
53 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
54 Py_TPFLAGS_BASETYPE
55#if PY_VERSION_HEX >= 0x03040000
56 | Py_TPFLAGS_LONG_SUBCLASS
57#endif
58 , // tp_flags
59 (char*)"CPyCppyy long object for pass by reference", // tp_doc
60 0, 0, 0, 0, 0, 0, 0, 0, 0,
61 &PyInt_Type, // tp_base
62 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
63#if PY_VERSION_HEX >= 0x02030000
64 , 0 // tp_del
65#endif
66#if PY_VERSION_HEX >= 0x02060000
67 , 0 // tp_version_tag
68#endif
69#if PY_VERSION_HEX >= 0x03040000
70 , 0 // tp_finalize
71#endif
72};
73#endif
74
75//- custom type representing typedef to pointer of class ---------------------
77{
78 long long addr = 0;
79 if (!PyArg_ParseTuple(args, const_cast<char*>("|L"), &addr))
80 return nullptr;
81 return BindCppObjectNoCast((Cppyy::TCppObject_t)(intptr_t)addr, self->fCppType);
82}
83
84//-----------------------------------------------------------------------------
86{
88 (Cppyy::GetScopedFinalName(self->fCppType)+"*").c_str());
89}
90
91//-----------------------------------------------------------------------------
93{
95 if (pyclass) {
96 PyObject* pyname = PyObject_GetAttr(pyclass, PyStrings::gName);
97 Py_DECREF(pyclass);
98 return pyname;
99 }
100
101 return CPyCppyy_PyText_FromString("<unknown>*");
102}
103
104//-----------------------------------------------------------------------------
105static PyGetSetDef tptc_getset[] = {
106 {(char*)"__name__", (getter)tptc_name, nullptr, nullptr, nullptr},
107 {(char*)"__cpp_name__", (getter)tptc_getcppname, nullptr, nullptr, nullptr},
108 {(char*)nullptr, nullptr, nullptr, nullptr, nullptr}
109};
110
111
113 PyVarObject_HEAD_INIT(&PyType_Type, 0)
114 (char*)"cppyy.TypedefPointerToClass",// tp_name
115 sizeof(typedefpointertoclassobject), // tp_basicsize
116 0, // tp_itemsize
117 0, // tp_dealloc
118 0, // tp_vectorcall_offset
119 0, // tp_getattr
120 0, // tp_setattr
121 0, // tp_as_async
122 0, // tp_repr
123 0, // tp_as_number
124 0, // tp_as_sequence
125 0, // tp_as_mapping
126 0, // tp_hash
127 (ternaryfunc)tptc_call, // tp_call
128 0, // tp_str
129 PyObject_GenericGetAttr, // tp_getattro
130 PyObject_GenericSetAttr, // tp_setattro
131 0, // tp_as_buffer
132 Py_TPFLAGS_DEFAULT, // tp_flags
133 0, // tp_doc
134 0, // tp_traverse
135 0, // tp_clear
136 0, // tp_richcompare
137 0, // tp_weaklistoffset
138 0, // tp_iter
139 0, // tp_iternext
140 0, // tp_methods
141 0, // tp_members
142 tptc_getset, // tp_getset
143 0, // tp_base
144 0, // tp_dict
145 0, // tp_descr_get
146 0, // tp_descr_set
147 offsetof(typedefpointertoclassobject, fDict), // tp_dictoffset
148 0, // tp_init
149 0, // tp_alloc
150 0, // tp_new
151 0, // tp_free
152 0, // tp_is_gc
153 0, // tp_bases
154 0, // tp_mro
155 0, // tp_cache
156 0, // tp_subclasses
157 0 // tp_weaklist
158#if PY_VERSION_HEX >= 0x02030000
159 , 0 // tp_del
160#endif
161#if PY_VERSION_HEX >= 0x02060000
162 , 0 // tp_version_tag
163#endif
164#if PY_VERSION_HEX >= 0x03040000
165 , 0 // tp_finalize
166#endif
167#if PY_VERSION_HEX >= 0x03080000
168 , 0 // tp_vectorcall
169#endif
170#if PY_VERSION_HEX >= 0x030c0000
171 , 0 // tp_watched
172#endif
173#if PY_VERSION_HEX >= 0x030d0000
174 , 0 // tp_versions_used
175#endif
176};
177
178//= instancemethod object with a more efficient call function ================
179static PyMethodObject* free_list;
180static int numfree = 0;
181#ifndef PyMethod_MAXFREELIST
182#define PyMethod_MAXFREELIST 256
183#endif
184
185//-----------------------------------------------------------------------------
187#if PY_VERSION_HEX < 0x03000000
188 pyclass
189#endif
190 )
191{
192// from instancemethod, but with custom type (at issue is that instancemethod is not
193// meant to be derived from)
194 PyMethodObject* im;
195 if (!PyCallable_Check(func)) {
196 PyErr_Format(PyExc_SystemError,
197 "%s:%d: bad argument to internal function", __FILE__, __LINE__);
198 return nullptr;
199 }
200
201 im = free_list;
202 if (im != nullptr) {
203 free_list = (PyMethodObject*)(im->im_self);
204 (void)PyObject_INIT(im, &CustomInstanceMethod_Type);
205 }
206 else {
207 im = PyObject_GC_New(PyMethodObject, &CustomInstanceMethod_Type);
208 if (im == nullptr)
209 return nullptr;
210 }
211
212 im->im_weakreflist = nullptr;
213 Py_INCREF(func);
214 im->im_func = func;
215 Py_XINCREF(self);
216 im->im_self = self;
217#if PY_VERSION_HEX < 0x03000000
218 Py_XINCREF(pyclass);
219 im->im_class = pyclass;
220#endif
221 PyObject_GC_Track(im);
222 return (PyObject*)im;
223}
224
225//-----------------------------------------------------------------------------
226static void im_dealloc(PyMethodObject* im)
227{
228// from instancemethod, but with custom type (at issue is that instancemethod is not
229// meant to be derived from)
230 PyObject_GC_UnTrack(im);
231
232 if (im->im_weakreflist != nullptr)
233 PyObject_ClearWeakRefs((PyObject*)im);
234
235 Py_DECREF(im->im_func);
236 Py_XDECREF(im->im_self);
237#if PY_VERSION_HEX < 0x03000000
238 Py_XDECREF(im->im_class);
239#endif
240
242 im->im_self = (PyObject*)free_list;
243 free_list = im;
244 numfree++;
245 } else {
246 PyObject_GC_Del(im);
247 }
248}
249
250//-----------------------------------------------------------------------------
251static PyObject* im_call(PyObject* meth, PyObject* args, PyObject* kw)
252{
253// The mapping from a method to a function involves reshuffling of self back
254// into the list of arguments. However, the pythonized methods will then have
255// to undo that shuffling, which is inefficient. This method is the same as
256// the one for the instancemethod object, except for the shuffling.
258
259 if (!self) {
260 // unbound methods must be called with an instance of the class (or a
261 // derived class) as first argument
262 Py_ssize_t argc = PyTuple_GET_SIZE(args);
264 if (1 <= argc && PyObject_IsInstance(PyTuple_GET_ITEM(args, 0), pyclass) == 1) {
265 self = PyTuple_GET_ITEM(args, 0);
266
267 PyObject* newArgs = PyTuple_New(argc-1);
268 for (int i = 1; i < argc; ++i) {
269 PyObject* v = PyTuple_GET_ITEM(args, i);
270 Py_INCREF(v);
271 PyTuple_SET_ITEM(newArgs, i-1, v);
272 }
273
274 args = newArgs;
275
276 } else
277 return PyMethod_Type.tp_call(meth, args, kw); // will set proper error msg
278
279 } else
280 Py_INCREF(args);
281
282 PyCFunctionObject* func = (PyCFunctionObject*)CustomInstanceMethod_GET_FUNCTION(meth);
283
284// the function is globally shared, so set and reset its "self" (ok, b/c of GIL)
285 Py_INCREF(self);
286 func->m_self = self;
288 func->m_self = nullptr;
289 Py_DECREF(self);
290 Py_DECREF(args);
291 return result;
292}
293
294//-----------------------------------------------------------------------------
295static PyObject* im_descr_get(PyObject* meth, PyObject* obj, PyObject* pyclass)
296{
297// from instancemethod: don't rebind an already bound method, or an unbound method
298// of a class that's not a base class of pyclass
300#if PY_VERSION_HEX < 0x03000000
302 !PyObject_IsSubclass(pyclass, CustomInstanceMethod_GET_CLASS(meth)))
303#endif
304 ) {
305 Py_INCREF(meth);
306 return meth;
307 }
308
309 if (obj == Py_None)
310 obj = nullptr;
311
313}
314
315//= CPyCppyy custom instance method type =====================================
317 PyVarObject_HEAD_INIT(&PyType_Type, 0)
318 (char*)"cppyy.InstanceMethod", // tp_name
319 0, 0,
320 (destructor)im_dealloc, // tp_dealloc
321 0, 0, 0, 0, 0, 0, 0, 0, 0,
322 im_call, // tp_call
323 0, 0, 0, 0,
324 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
325 Py_TPFLAGS_BASETYPE, // tp_flags
326 (char*)"CPyCppyy custom instance method (internal)", // tp_doc
327 0, 0, 0, 0, 0, 0, 0, 0, 0,
328 &PyMethod_Type, // tp_base
329 0,
330 im_descr_get, // tp_descr_get
331 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
332#if PY_VERSION_HEX >= 0x02030000
333 , 0 // tp_del
334#endif
335#if PY_VERSION_HEX >= 0x02060000
336 , 0 // tp_version_tag
337#endif
338#if PY_VERSION_HEX >= 0x03040000
339 , 0 // tp_finalize
340#endif
341#if PY_VERSION_HEX >= 0x03080000
342 , 0 // tp_vectorcall
343#endif
344#if PY_VERSION_HEX >= 0x030c0000
345 , 0 // tp_watched
346#endif
347#if PY_VERSION_HEX >= 0x030d0000
348 , 0 // tp_versions_used
349#endif
350};
351
352
353//= CPyCppyy custom iterator for performance =================================
355 PyObject_GC_UnTrack(ii);
356 Py_XDECREF(ii->ii_container);
357 PyObject_GC_Del(ii);
358}
359
360static int indexiter_traverse(indexiterobject* ii, visitproc visit, void* arg) {
361 Py_VISIT(ii->ii_container);
362 return 0;
363}
364
366 if (ii->ii_pos >= ii->ii_len)
367 return nullptr;
368
369 PyObject* pyindex = PyLong_FromSsize_t(ii->ii_pos);
371 Py_DECREF(pyindex);
372
373 ii->ii_pos += 1;
374 return result;
375}
376
377PyTypeObject IndexIter_Type = {
378 PyVarObject_HEAD_INIT(&PyType_Type, 0)
379 (char*)"cppyy.indexiter", // tp_name
380 sizeof(indexiterobject), // tp_basicsize
381 0,
382 (destructor)indexiter_dealloc, // tp_dealloc
383 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
384 Py_TPFLAGS_DEFAULT |
385 Py_TPFLAGS_HAVE_GC, // tp_flags
386 0,
387 (traverseproc)indexiter_traverse, // tp_traverse
388 0, 0, 0,
389 PyObject_SelfIter, // tp_iter
390 (iternextfunc)indexiter_iternext, // tp_iternext
391 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
392#if PY_VERSION_HEX >= 0x02030000
393 , 0 // tp_del
394#endif
395#if PY_VERSION_HEX >= 0x02060000
396 , 0 // tp_version_tag
397#endif
398#if PY_VERSION_HEX >= 0x03040000
399 , 0 // tp_finalize
400#endif
401#if PY_VERSION_HEX >= 0x03080000
402 , 0 // tp_vectorcall
403#endif
404#if PY_VERSION_HEX >= 0x030c0000
405 , 0 // tp_watched
406#endif
407#if PY_VERSION_HEX >= 0x030d0000
408 , 0 // tp_versions_used
409#endif
410};
411
412
414 if (vi->vi_converter && vi->vi_converter->HasState()) delete vi->vi_converter;
416}
417
419 if (vi->ii_pos >= vi->ii_len)
420 return nullptr;
421
422 PyObject* result = nullptr;
423
424 if (vi->vi_data && vi->vi_converter) {
425 void* location = (void*)((ptrdiff_t)vi->vi_data + vi->vi_stride * vi->ii_pos);
426 result = vi->vi_converter->FromMemory(location);
427 } else if (vi->vi_data && vi->vi_klass) {
428 // The CPPInstance::kNoMemReg by-passes the memory regulator; the assumption here is
429 // that objects in vectors are simple and thus do not need to maintain object identity
430 // (or at least not during the loop anyway). This gains 2x in performance.
431 Cppyy::TCppObject_t cppobj = (Cppyy::TCppObject_t)((ptrdiff_t)vi->vi_data + vi->vi_stride * vi->ii_pos);
434 else
437 PyObject_SetAttr(result, PyStrings::gLifeLine, vi->ii_container);
438 } else {
439 PyObject* pyindex = PyLong_FromSsize_t(vi->ii_pos);
441 Py_DECREF(pyindex);
442 }
443
444 vi->ii_pos += 1;
445 return result;
446}
447
448PyTypeObject VectorIter_Type = {
449 PyVarObject_HEAD_INIT(&PyType_Type, 0)
450 (char*)"cppyy.vectoriter", // tp_name
451 sizeof(vectoriterobject), // tp_basicsize
452 0,
453 (destructor)vectoriter_dealloc, // tp_dealloc
454 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
455 Py_TPFLAGS_DEFAULT |
456 Py_TPFLAGS_HAVE_GC, // tp_flags
457 0,
458 (traverseproc)indexiter_traverse, // tp_traverse
459 0, 0, 0,
460 PyObject_SelfIter, // tp_iter
461 (iternextfunc)vectoriter_iternext, // tp_iternext
462 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
463#if PY_VERSION_HEX >= 0x02030000
464 , 0 // tp_del
465#endif
466#if PY_VERSION_HEX >= 0x02060000
467 , 0 // tp_version_tag
468#endif
469#if PY_VERSION_HEX >= 0x03040000
470 , 0 // tp_finalize
471#endif
472#if PY_VERSION_HEX >= 0x03080000
473 , 0 // tp_vectorcall
474#endif
475#if PY_VERSION_HEX >= 0x030c0000
476 , 0 // tp_watched
477#endif
478#if PY_VERSION_HEX >= 0x030d0000
479 , 0 // tp_versions_used
480#endif
481};
482
483} // namespace CPyCppyy
int Py_ssize_t
Definition CPyCppyy.h:215
#define CPyCppyy_PyCFunction_Call
Definition CPyCppyy.h:291
static PyObject * PyObject_CallMethodOneArg(PyObject *obj, PyObject *name, PyObject *arg)
Definition CPyCppyy.h:385
#define CPyCppyy_PyText_FromString
Definition CPyCppyy.h:81
#define PyVarObject_HEAD_INIT(type, size)
Definition CPyCppyy.h:194
#define PyMethod_MAXFREELIST
#define CustomInstanceMethod_GET_SELF(meth)
#define CustomInstanceMethod_GET_FUNCTION(meth)
#define CustomInstanceMethod_GET_CLASS(meth)
_object PyObject
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
virtual bool HasState()
Definition API.h:122
virtual PyObject * FromMemory(void *address)
PyObject * gLifeLine
Definition PyStrings.cxx:29
PyObject * gGetItem
Definition PyStrings.cxx:23
PyObject * gGetNoCheck
Definition PyStrings.cxx:24
static PyObject * indexiter_iternext(indexiterobject *ii)
PyObject * CustomInstanceMethod_New(PyObject *func, PyObject *self, PyObject *pyclass)
PyTypeObject VectorIter_Type
PyObject * GetScopeProxy(Cppyy::TCppScope_t)
PyTypeObject CustomInstanceMethod_Type
static PyMethodObject * free_list
PyTypeObject RefFloat_Type
Custom "builtins," detectable by type, for pass by ref and improved performance.
static PyGetSetDef tptc_getset[]
static int numfree
PyTypeObject TypedefPointerToClass_Type
static PyObject * tptc_call(typedefpointertoclassobject *self, PyObject *args, PyObject *)
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)
static PyObject * tptc_name(typedefpointertoclassobject *self, void *)
PyTypeObject IndexIter_Type
static PyObject * tptc_getcppname(typedefpointertoclassobject *self, void *)
static void indexiter_dealloc(indexiterobject *ii)
PyObject * BindCppObject(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
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
RPY_EXPORTED std::string GetScopedFinalName(TCppType_t type)
PyObject_HEAD PyObject * ii_container
PyObject_HEAD Cppyy::TCppType_t fCppType
Cppyy::TCppType_t vi_klass
CPyCppyy::Converter * vi_converter