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};
174
175//= instancemethod object with a more efficient call function ================
176static PyMethodObject* free_list;
177static int numfree = 0;
178#ifndef PyMethod_MAXFREELIST
179#define PyMethod_MAXFREELIST 256
180#endif
181
182//-----------------------------------------------------------------------------
184#if PY_VERSION_HEX < 0x03000000
185 pyclass
186#endif
187 )
188{
189// from instancemethod, but with custom type (at issue is that instancemethod is not
190// meant to be derived from)
191 PyMethodObject* im;
192 if (!PyCallable_Check(func)) {
193 PyErr_Format(PyExc_SystemError,
194 "%s:%d: bad argument to internal function", __FILE__, __LINE__);
195 return nullptr;
196 }
197
198 im = free_list;
199 if (im != nullptr) {
200 free_list = (PyMethodObject*)(im->im_self);
201 (void)PyObject_INIT(im, &CustomInstanceMethod_Type);
202 }
203 else {
204 im = PyObject_GC_New(PyMethodObject, &CustomInstanceMethod_Type);
205 if (im == nullptr)
206 return nullptr;
207 }
208
209 im->im_weakreflist = nullptr;
210 Py_INCREF(func);
211 im->im_func = func;
212 Py_XINCREF(self);
213 im->im_self = self;
214#if PY_VERSION_HEX < 0x03000000
215 Py_XINCREF(pyclass);
216 im->im_class = pyclass;
217#endif
218 PyObject_GC_Track(im);
219 return (PyObject*)im;
220}
221
222//-----------------------------------------------------------------------------
223static void im_dealloc(PyMethodObject* im)
224{
225// from instancemethod, but with custom type (at issue is that instancemethod is not
226// meant to be derived from)
227 PyObject_GC_UnTrack(im);
228
229 if (im->im_weakreflist != nullptr)
230 PyObject_ClearWeakRefs((PyObject*)im);
231
232 Py_DECREF(im->im_func);
233 Py_XDECREF(im->im_self);
234#if PY_VERSION_HEX < 0x03000000
235 Py_XDECREF(im->im_class);
236#endif
237
239 im->im_self = (PyObject*)free_list;
240 free_list = im;
241 numfree++;
242 } else {
243 PyObject_GC_Del(im);
244 }
245}
246
247//-----------------------------------------------------------------------------
248static PyObject* im_call(PyObject* meth, PyObject* args, PyObject* kw)
249{
250// The mapping from a method to a function involves reshuffling of self back
251// into the list of arguments. However, the pythonized methods will then have
252// to undo that shuffling, which is inefficient. This method is the same as
253// the one for the instancemethod object, except for the shuffling.
255
256 if (!self) {
257 // unbound methods must be called with an instance of the class (or a
258 // derived class) as first argument
259 Py_ssize_t argc = PyTuple_GET_SIZE(args);
261 if (1 <= argc && PyObject_IsInstance(PyTuple_GET_ITEM(args, 0), pyclass) == 1) {
262 self = PyTuple_GET_ITEM(args, 0);
263
264 PyObject* newArgs = PyTuple_New(argc-1);
265 for (int i = 1; i < argc; ++i) {
266 PyObject* v = PyTuple_GET_ITEM(args, i);
267 Py_INCREF(v);
268 PyTuple_SET_ITEM(newArgs, i-1, v);
269 }
270
271 args = newArgs;
272
273 } else
274 return PyMethod_Type.tp_call(meth, args, kw); // will set proper error msg
275
276 } else
277 Py_INCREF(args);
278
279 PyCFunctionObject* func = (PyCFunctionObject*)CustomInstanceMethod_GET_FUNCTION(meth);
280
281// the function is globally shared, so set and reset its "self" (ok, b/c of GIL)
282 Py_INCREF(self);
283 func->m_self = self;
285 func->m_self = nullptr;
286 Py_DECREF(self);
287 Py_DECREF(args);
288 return result;
289}
290
291//-----------------------------------------------------------------------------
292static PyObject* im_descr_get(PyObject* meth, PyObject* obj, PyObject* pyclass)
293{
294// from instancemethod: don't rebind an already bound method, or an unbound method
295// of a class that's not a base class of pyclass
297#if PY_VERSION_HEX < 0x03000000
299 !PyObject_IsSubclass(pyclass, CustomInstanceMethod_GET_CLASS(meth)))
300#endif
301 ) {
302 Py_INCREF(meth);
303 return meth;
304 }
305
306 if (obj == Py_None)
307 obj = nullptr;
308
310}
311
312//= CPyCppyy custom instance method type =====================================
314 PyVarObject_HEAD_INIT(&PyType_Type, 0)
315 (char*)"cppyy.InstanceMethod", // tp_name
316 0, 0,
317 (destructor)im_dealloc, // tp_dealloc
318 0, 0, 0, 0, 0, 0, 0, 0, 0,
319 im_call, // tp_call
320 0, 0, 0, 0,
321 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
322 Py_TPFLAGS_BASETYPE, // tp_flags
323 (char*)"CPyCppyy custom instance method (internal)", // tp_doc
324 0, 0, 0, 0, 0, 0, 0, 0, 0,
325 &PyMethod_Type, // tp_base
326 0,
327 im_descr_get, // tp_descr_get
328 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
329#if PY_VERSION_HEX >= 0x02030000
330 , 0 // tp_del
331#endif
332#if PY_VERSION_HEX >= 0x02060000
333 , 0 // tp_version_tag
334#endif
335#if PY_VERSION_HEX >= 0x03040000
336 , 0 // tp_finalize
337#endif
338#if PY_VERSION_HEX >= 0x03080000
339 , 0 // tp_vectorcall
340#endif
341#if PY_VERSION_HEX >= 0x030c0000
342 , 0 // tp_watched
343#endif
344};
345
346
347//= CPyCppyy custom iterator for performance =================================
349 PyObject_GC_UnTrack(ii);
350 Py_XDECREF(ii->ii_container);
351 PyObject_GC_Del(ii);
352}
353
354static int indexiter_traverse(indexiterobject* ii, visitproc visit, void* arg) {
355 Py_VISIT(ii->ii_container);
356 return 0;
357}
358
360 if (ii->ii_pos >= ii->ii_len)
361 return nullptr;
362
363 PyObject* pyindex = PyLong_FromSsize_t(ii->ii_pos);
365 Py_DECREF(pyindex);
366
367 ii->ii_pos += 1;
368 return result;
369}
370
371PyTypeObject IndexIter_Type = {
372 PyVarObject_HEAD_INIT(&PyType_Type, 0)
373 (char*)"cppyy.indexiter", // tp_name
374 sizeof(indexiterobject), // tp_basicsize
375 0,
376 (destructor)indexiter_dealloc, // tp_dealloc
377 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
378 Py_TPFLAGS_DEFAULT |
379 Py_TPFLAGS_HAVE_GC, // tp_flags
380 0,
381 (traverseproc)indexiter_traverse, // tp_traverse
382 0, 0, 0,
383 PyObject_SelfIter, // tp_iter
384 (iternextfunc)indexiter_iternext, // tp_iternext
385 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
386#if PY_VERSION_HEX >= 0x02030000
387 , 0 // tp_del
388#endif
389#if PY_VERSION_HEX >= 0x02060000
390 , 0 // tp_version_tag
391#endif
392#if PY_VERSION_HEX >= 0x03040000
393 , 0 // tp_finalize
394#endif
395#if PY_VERSION_HEX >= 0x03080000
396 , 0 // tp_vectorcall
397#endif
398#if PY_VERSION_HEX >= 0x030c0000
399 , 0 // tp_watched
400#endif
401};
402
403
405 if (vi->vi_converter && vi->vi_converter->HasState()) delete vi->vi_converter;
407}
408
410 if (vi->ii_pos >= vi->ii_len)
411 return nullptr;
412
413 PyObject* result = nullptr;
414
415 if (vi->vi_data && vi->vi_converter) {
416 void* location = (void*)((ptrdiff_t)vi->vi_data + vi->vi_stride * vi->ii_pos);
417 result = vi->vi_converter->FromMemory(location);
418 } else if (vi->vi_data && vi->vi_klass) {
419 // The CPPInstance::kNoMemReg by-passes the memory regulator; the assumption here is
420 // that objects in vectors are simple and thus do not need to maintain object identity
421 // (or at least not during the loop anyway). This gains 2x in performance.
422 Cppyy::TCppObject_t cppobj = (Cppyy::TCppObject_t)((ptrdiff_t)vi->vi_data + vi->vi_stride * vi->ii_pos);
425 else
428 PyObject_SetAttr(result, PyStrings::gLifeLine, vi->ii_container);
429 } else {
430 PyObject* pyindex = PyLong_FromSsize_t(vi->ii_pos);
432 Py_DECREF(pyindex);
433 }
434
435 vi->ii_pos += 1;
436 return result;
437}
438
439PyTypeObject VectorIter_Type = {
440 PyVarObject_HEAD_INIT(&PyType_Type, 0)
441 (char*)"cppyy.vectoriter", // tp_name
442 sizeof(vectoriterobject), // tp_basicsize
443 0,
444 (destructor)vectoriter_dealloc, // tp_dealloc
445 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
446 Py_TPFLAGS_DEFAULT |
447 Py_TPFLAGS_HAVE_GC, // tp_flags
448 0,
449 (traverseproc)indexiter_traverse, // tp_traverse
450 0, 0, 0,
451 PyObject_SelfIter, // tp_iter
452 (iternextfunc)vectoriter_iternext, // tp_iternext
453 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
454#if PY_VERSION_HEX >= 0x02030000
455 , 0 // tp_del
456#endif
457#if PY_VERSION_HEX >= 0x02060000
458 , 0 // tp_version_tag
459#endif
460#if PY_VERSION_HEX >= 0x03040000
461 , 0 // tp_finalize
462#endif
463#if PY_VERSION_HEX >= 0x03080000
464 , 0 // tp_vectorcall
465#endif
466#if PY_VERSION_HEX >= 0x030c0000
467 , 0 // tp_watched
468#endif
469};
470
471} // 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:367
#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:28
PyObject * gGetItem
Definition PyStrings.cxx:22
PyObject * gGetNoCheck
Definition PyStrings.cxx:23
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