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