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