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
29 (char*)"cppyy.Double", // tp_name
30 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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
51 (char*)"cppyy.Long", // tp_name
52 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
55#if PY_VERSION_HEX >= 0x03040000
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//-----------------------------------------------------------------------------
90
91//-----------------------------------------------------------------------------
103
104//-----------------------------------------------------------------------------
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
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 0, // 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
171};
172
173//= instancemethod object with a more efficient call function ================
175static int numfree = 0;
176#ifndef PyMethod_MAXFREELIST
177#define PyMethod_MAXFREELIST 256
178#endif
179
180//-----------------------------------------------------------------------------
182#if PY_VERSION_HEX < 0x03000000
183 pyclass
184#endif
185 )
186{
187// from instancemethod, but with custom type (at issue is that instancemethod is not
188// meant to be derived from)
190 if (!PyCallable_Check(func)) {
192 "%s:%d: bad argument to internal function", __FILE__, __LINE__);
193 return nullptr;
194 }
195
196 im = free_list;
197 if (im != nullptr) {
198 free_list = (PyMethodObject*)(im->im_self);
200 }
201 else {
203 if (im == nullptr)
204 return nullptr;
205 }
206
207 im->im_weakreflist = nullptr;
208 Py_INCREF(func);
209 im->im_func = func;
211 im->im_self = self;
212#if PY_VERSION_HEX < 0x03000000
214 im->im_class = pyclass;
215#endif
217 return (PyObject*)im;
218}
219
220//-----------------------------------------------------------------------------
222{
223// from instancemethod, but with custom type (at issue is that instancemethod is not
224// meant to be derived from)
226
227 if (im->im_weakreflist != nullptr)
229
230 Py_DECREF(im->im_func);
231 Py_XDECREF(im->im_self);
232#if PY_VERSION_HEX < 0x03000000
233 Py_XDECREF(im->im_class);
234#endif
235
237 im->im_self = (PyObject*)free_list;
238 free_list = im;
239 numfree++;
240 } else {
242 }
243}
244
245//-----------------------------------------------------------------------------
247{
248// The mapping from a method to a function involves reshuffling of self back
249// into the list of arguments. However, the pythonized methods will then have
250// to undo that shuffling, which is inefficient. This method is the same as
251// the one for the instancemethod object, except for the shuffling.
253
254 if (!self) {
255 // unbound methods must be called with an instance of the class (or a
256 // derived class) as first argument
259 if (1 <= argc && PyObject_IsInstance(PyTuple_GET_ITEM(args, 0), pyclass) == 1) {
260 self = PyTuple_GET_ITEM(args, 0);
261
263 for (int i = 1; i < argc; ++i) {
264 PyObject* v = PyTuple_GET_ITEM(args, i);
265 Py_INCREF(v);
267 }
268
269 args = newArgs;
270
271 } else
272 return PyMethod_Type.tp_call(meth, args, kw); // will set proper error msg
273
274 } else
275 Py_INCREF(args);
276
278
279// the function is globally shared, so set and reset its "self" (ok, b/c of GIL)
281 func->m_self = self;
283 func->m_self = nullptr;
285 Py_DECREF(args);
286 return result;
287}
288
289//-----------------------------------------------------------------------------
291{
292// from instancemethod: don't rebind an already bound method, or an unbound method
293// of a class that's not a base class of pyclass
295#if PY_VERSION_HEX < 0x03000000
298#endif
299 ) {
301 return meth;
302 }
303
304 if (obj == Py_None)
305 obj = nullptr;
306
308}
309
310//= CPyCppyy custom instance method type =====================================
313 (char*)"cppyy.InstanceMethod", // tp_name
314 0, 0,
315 (destructor)im_dealloc, // tp_dealloc
316 0, 0, 0, 0, 0, 0, 0, 0, 0,
317 im_call, // tp_call
318 0, 0, 0, 0,
320 Py_TPFLAGS_BASETYPE, // tp_flags
321 (char*)"CPyCppyy custom instance method (internal)", // tp_doc
322 0, 0, 0, 0, 0, 0, 0, 0, 0,
323 &PyMethod_Type, // tp_base
324 0,
325 im_descr_get, // tp_descr_get
326 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
327#if PY_VERSION_HEX >= 0x02030000
328 , 0 // tp_del
329#endif
330#if PY_VERSION_HEX >= 0x02060000
331 , 0 // tp_version_tag
332#endif
333#if PY_VERSION_HEX >= 0x03040000
334 , 0 // tp_finalize
335#endif
336#if PY_VERSION_HEX >= 0x03080000
337 , 0 // tp_vectorcall
338#endif
340};
341
342
343//= CPyCppyy custom iterator for performance =================================
346 Py_XDECREF(ii->ii_container);
348}
349
351 Py_VISIT(ii->ii_container);
352 return 0;
353}
354
356 if (ii->ii_pos >= ii->ii_len)
357 return nullptr;
358
362
363 ii->ii_pos += 1;
364 return result;
365}
366
369 (char*)"cppyy.indexiter", // tp_name
370 sizeof(indexiterobject), // tp_basicsize
371 0,
372 (destructor)indexiter_dealloc, // tp_dealloc
373 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
375 Py_TPFLAGS_HAVE_GC, // tp_flags
376 0,
377 (traverseproc)indexiter_traverse, // tp_traverse
378 0, 0, 0,
379 PyObject_SelfIter, // tp_iter
380 (iternextfunc)indexiter_iternext, // tp_iternext
381 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
382#if PY_VERSION_HEX >= 0x02030000
383 , 0 // tp_del
384#endif
385#if PY_VERSION_HEX >= 0x02060000
386 , 0 // tp_version_tag
387#endif
388#if PY_VERSION_HEX >= 0x03040000
389 , 0 // tp_finalize
390#endif
391#if PY_VERSION_HEX >= 0x03080000
392 , 0 // tp_vectorcall
393#endif
395};
396
397
399 if (vi->vi_converter && vi->vi_converter->HasState()) delete vi->vi_converter;
401}
402
404 if (vi->ii_pos >= vi->ii_len)
405 return nullptr;
406
407 PyObject* result = nullptr;
408
409 if (vi->vi_data && vi->vi_converter) {
410 void* location = (void*)((ptrdiff_t)vi->vi_data + vi->vi_stride * vi->ii_pos);
411 result = vi->vi_converter->FromMemory(location);
412 } else if (vi->vi_data && vi->vi_klass) {
413 // The CPPInstance::kNoMemReg by-passes the memory regulator; the assumption here is
414 // that objects in vectors are simple and thus do not need to maintain object identity
415 // (or at least not during the loop anyway). This gains 2x in performance.
416 Cppyy::TCppObject_t cppobj = (Cppyy::TCppObject_t)((ptrdiff_t)vi->vi_data + vi->vi_stride * vi->ii_pos);
417 if (vi->vi_flags & vectoriterobject::kIsPolymorphic)
419 else
421 if ((vi->vi_flags & vectoriterobject::kNeedLifeLine) && result)
423 } else {
427 }
428
429 vi->ii_pos += 1;
430 return result;
431}
432
435 (char*)"cppyy.vectoriter", // tp_name
436 sizeof(vectoriterobject), // tp_basicsize
437 0,
438 (destructor)vectoriter_dealloc, // tp_dealloc
439 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
441 Py_TPFLAGS_HAVE_GC, // tp_flags
442 0,
443 (traverseproc)indexiter_traverse, // tp_traverse
444 0, 0, 0,
445 PyObject_SelfIter, // tp_iter
446 (iternextfunc)vectoriter_iternext, // tp_iternext
447 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
448#if PY_VERSION_HEX >= 0x02030000
449 , 0 // tp_del
450#endif
451#if PY_VERSION_HEX >= 0x02060000
452 , 0 // tp_version_tag
453#endif
454#if PY_VERSION_HEX >= 0x03040000
455 , 0 // tp_finalize
456#endif
457#if PY_VERSION_HEX >= 0x03080000
458 , 0 // tp_vectorcall
459#endif
461};
462
463} // namespace CPyCppyy
int Py_ssize_t
Definition CPyCppyy.h:215
#define CPYCPPYY_PYTYPE_TAIL
Definition CPyCppyy.h:412
#define CPyCppyy_PyCFunction_Call
Definition CPyCppyy.h:283
static PyObject * PyObject_CallMethodOneArg(PyObject *obj, PyObject *name, PyObject *arg)
Definition CPyCppyy.h:377
#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
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
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
PyObject * gLifeLine
Definition PyStrings.cxx:30
PyObject * gGetItem
Definition PyStrings.cxx:24
PyObject * gGetNoCheck
Definition PyStrings.cxx:25
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:37
RPY_EXPORTED std::string GetScopedFinalName(TCppType_t type)