Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
CPPInstance.cxx
Go to the documentation of this file.
1// Bindings
2#include "cpyrt.h"
3
4using namespace cppjit;
5#include "CPPInstance.h"
6#include "CPPOverload.h"
7#include "CPPScope.h"
8#include "Compatibility.h"
9#include "MemoryRegulator.h"
10#include "ProxyWrappers.h"
11#include "PyStrings.h"
12#include "TypeManip.h"
13#include "Utility.h"
14#include "cppjit_interop.h"
15
16#include "cpyrt/DispatchPtr.h"
17
18// Standard
19#include <algorithm>
20#include <sstream>
21
22//- data _____________________________________________________________________
23namespace cppjit::cpyrt {
25}
26
27//______________________________________________________________________________
28// Python-side proxy objects
29// =========================
30//
31// C++ objects are represented in Python by CPPInstances, which encapsulate
32// them using either a pointer (normal), pointer-to-pointer (kIsReference set),
33// or as an owned value (kIsValue set). Objects held as reference are never
34// owned, otherwise the object is owned if kIsOwner is set.
35//
36// In addition to encapsulation, CPPInstance offers rudimentary comparison
37// operators (based on pointer value and class comparisons); stubs (with lazy
38// lookups) for numeric operators; and a representation that prints the C++
39// pointer values, rather than the PyObject* ones as is the default.
40//
41// Smart pointers have the underlying type as the Python type, but store the
42// pointer to the smart pointer. They carry a pointer to the Python-sode smart
43// class for dereferencing to get to the actual instance pointer.
44
45//- private helpers ----------------------------------------------------------
46namespace {
47
48// Several specific use cases require extra data in a CPPInstance, but can not
49// be a new type. E.g. cross-inheritance derived types are by definition added
50// a posterio, and caching of datamembers is up to the datamember, not the
51// instance type. To not have normal use of CPPInstance take extra memory, this
52// extended data can slot in place of fObject for those use cases.
53
54struct ExtendedData {
55 ExtendedData()
56 : fObject(nullptr), fSmartClass(nullptr), fDispatchPtr(nullptr),
57 fArraySize(0) {}
58 ~ExtendedData() {
59 for (auto& pc : fDatamemberCache)
60 Py_XDECREF(pc.second);
61 fDatamemberCache.clear();
62 }
63
64 // the original object reference it replaces (Note: has to be first data
65 // member, see usage in GetObjectRaw(), e.g. for ptr-ptr passing)
66 void* fObject;
67
68 // for caching expensive-to-create data member representations
69 cpyrt::CI_DatamemberCache_t fDatamemberCache;
70
71 // for smart pointer types
72 cpyrt::CPPSmartClass* fSmartClass;
73
74 // for back-referencing from Python-derived instances
75 cpyrt::DispatchPtr* fDispatchPtr;
76
77 // for representing T* as a low-level array
78 Py_ssize_t fArraySize;
79};
80
81} // unnamed namespace
82
83#define EXT_OBJECT(pyobj) ((ExtendedData*)((pyobj)->fObject))->fObject
84#define DATA_CACHE(pyobj) ((ExtendedData*)((pyobj)->fObject))->fDatamemberCache
85#define SMART_CLS(pyobj) ((ExtendedData*)((pyobj)->fObject))->fSmartClass
86#define SMART_TYPE(pyobj) SMART_CLS(pyobj)->fCppType
87#define DISPATCHPTR(pyobj) ((ExtendedData*)((pyobj)->fObject))->fDispatchPtr
88#define ARRAY_SIZE(pyobj) ((ExtendedData*)((pyobj)->fObject))->fArraySize
89
91 if (fFlags & kIsExtended)
92 return;
93 void* obj = fObject;
94 fObject = (void*)new ExtendedData{};
95 EXT_OBJECT(this) = obj;
97}
98
100 if (IsSmart()) {
101 // We get the raw pointer from the smart pointer each time, in case it has
102 // changed or has been freed.
103 return interop::CallR(SMART_CLS(this)->fDereferencer, EXT_OBJECT(this), 0,
104 nullptr);
105 }
106 return EXT_OBJECT(this);
107}
108
109//- public methods -----------------------------------------------------------
111 PyTypeObject* target) {
112 // create a fresh instance; args and kwds are not used by op_new (see below)
113 PyObject* self = (PyObject*)this;
114 if (!target)
115 target = Py_TYPE(self);
116 PyObject* newinst = target->tp_new(target, nullptr, nullptr);
117
118 // set the C++ instance as given
119 ((CPPInstance*)newinst)->fObject = cppinst;
120
121 // look for user-provided __cpp_copy__ (not reusing __copy__ b/c of
122 // differences in semantics: need to pass in the new instance) ...
123 PyObject* cpy = PyObject_GetAttrString(self, (char*)"__cpp_copy__");
124 if (cpy && PyCallable_Check(cpy)) {
125 PyObject* args = PyTuple_New(1);
126 Py_INCREF(newinst);
127 PyTuple_SET_ITEM(args, 0, newinst);
128 PyObject* res = PyObject_CallObject(cpy, args);
129 Py_DECREF(args);
130 Py_DECREF(cpy);
131 if (res) {
132 Py_DECREF(res);
133 return (CPPInstance*)newinst;
134 }
135
136 // error already set, but need to return nullptr
137 Py_DECREF(newinst);
138 return nullptr;
139 } else if (cpy)
140 Py_DECREF(cpy);
141 else
142 PyErr_Clear();
143
144 // ... otherwise, shallow copy any Python-side dictionary items
145 PyObject* selfdct = PyObject_GetAttr(self, PyStrings::gDict);
146 PyObject* newdct = PyObject_GetAttr(newinst, PyStrings::gDict);
147 bool bMergeOk = PyDict_Merge(newdct, selfdct, 1) == 0;
148 Py_DECREF(newdct);
149 Py_DECREF(selfdct);
150
151 if (!bMergeOk) {
152 // presume error already set
153 Py_DECREF(newinst);
154 return nullptr;
155 }
156
158 return (CPPInstance*)newinst;
159}
160
161//----------------------------------------------------------------------------
163 fFlags |= kIsOwner;
164 if ((fFlags & kIsExtended) && DISPATCHPTR(this))
165 DISPATCHPTR(this)->PythonOwns();
166}
167
168//----------------------------------------------------------------------------
170 fFlags &= ~kIsOwner;
171 if ((fFlags & kIsExtended) && DISPATCHPTR(this))
172 DISPATCHPTR(this)->CppOwns();
173}
174
175//----------------------------------------------------------------------------
177 CreateExtension();
178 Py_INCREF(smart_type);
179 SMART_CLS(this) = (CPPSmartClass*)smart_type;
180 fFlags |= kIsSmartPtr;
181}
182
183//----------------------------------------------------------------------------
185 if (!IsSmart())
186 return interop::TCppScope_t{};
187 return SMART_TYPE(this);
188}
189
190//----------------------------------------------------------------------------
192 // Declared underlying type of the embedded smart pointer ('Base' for a
193 // std::unique_ptr<Base>), independent of any downcast of the dereferenced
194 // object.
195 if (!IsSmart())
196 return interop::TCppScope_t{};
197 return SMART_CLS(this)->fUnderlyingType;
198}
199
200//----------------------------------------------------------------------------
202 // Return the cache for expensive data objects (and make extended as
203 // necessary)
204 CreateExtension();
205 return DATA_CACHE(this);
206}
207
208//----------------------------------------------------------------------------
210 // Set up the dispatch pointer for memory management
211 CreateExtension();
212 DISPATCHPTR(this) = (DispatchPtr*)ptr;
213}
214
215//----------------------------------------------------------------------------
217 // Destroy the held C++ object, if owned; does not deallocate the proxy.
218
219 interop::TCppScope_t klass = pyobj->ObjectIsA(false /* check_smart */);
220 void*& cppobj = pyobj->GetObjectRaw();
221
224 (PyObject*)Py_TYPE((PyObject*)pyobj));
225
226 if (cppobj && (pyobj->fFlags & CPPInstance::kIsOwner)) {
227 if (pyobj->fFlags & CPPInstance::kIsValue) {
228 interop::CallDestructor(klass, cppobj);
229 interop::Deallocate(klass, cppobj);
230 } else
231 interop::Destruct(klass, cppobj);
232 }
233 cppobj = nullptr;
234
235 if (pyobj->IsExtended())
236 delete (ExtendedData*)pyobj->fObject;
238}
239
240namespace cppjit::cpyrt {
241
242//----------------------------------------------------------------------------
243static int op_traverse(CPPInstance* /*pyobj*/, visitproc /*visit*/,
244 void* /*arg*/) {
245 return 0;
246}
247
248//= cpyrt object proxy null-ness checking =================================
249static int op_nonzero(CPPInstance* self) {
250 // Null of the proxy is determined by null-ness of the held C++ object.
251 if (!self->GetObject())
252 return 0;
253
254 // If the object is valid, then the normal Python behavior is to allow __len__
255 // to determine truth. However, that function is defined in typeobject.c and
256 // only installed if tp_as_number exists w/o the nb_nonzero/nb_bool slot
257 // filled in, so it can not be called directly. Instead, since we're only ever
258 // dealing with CPPInstance derived objects, ignore length from sequence or
259 // mapping and call the __len__ method, if any, directly.
260
261 PyObject* pylen =
262 PyObject_CallMethodObjArgs((PyObject*)self, PyStrings::gLen, NULL);
263 if (!pylen) {
264 PyErr_Clear();
265 return 1; // since it's still a valid object
266 }
267
268 int result = PyObject_IsTrue(pylen);
269 Py_DECREF(pylen);
270 return result;
271}
272
273//= cpyrt object explicit destruction =====================================
275 // User access to force deletion of the object. Needed in case of a true
276 // garbage collector (like in PyPy), to allow the user control over when
277 // the C++ destructor is called. This method requires that the C++ object
278 // is owned (no-op otherwise).
279 op_dealloc_nofree(self);
281}
282
283//= cpyrt object dispatch support =========================================
285 PyObject* /* kwds */) {
286 // User-side __dispatch__ method to allow selection of a specific overloaded
287 // method. The actual selection is in the __overload__() method of
288 // CPPOverload.
289 PyObject *mname = nullptr, *sigarg = nullptr;
290 if (!PyArg_ParseTuple(args, const_cast<char*>("O!O!:__dispatch__"),
291 &PyUnicode_Type, &mname, &PyUnicode_Type, &sigarg))
292 return nullptr;
293
294 // get the named overload
295 PyObject* pymeth = PyObject_GetAttr(self, mname);
296 if (!pymeth)
297 return nullptr;
298
299 // get the '__overload__' method to allow overload selection
300 PyObject* pydisp =
301 PyObject_GetAttrString(pymeth, const_cast<char*>("__overload__"));
302 if (!pydisp) {
303 Py_DECREF(pymeth);
304 return nullptr;
305 }
306
307 // finally, call dispatch to get the specific overload
308 PyObject* oload = PyObject_CallFunctionObjArgs(pydisp, sigarg, nullptr);
309 Py_DECREF(pydisp);
310 Py_DECREF(pymeth);
311 return oload;
312}
313
314//= cpyrt smart pointer support ===========================================
316 if (!self->IsSmart()) {
317 // TODO: more likely should raise
319 }
320
323}
324
325//= pointer-as-array support for legacy C code ===============================
327 CreateExtension();
328 fFlags |= kIsArray;
329 ARRAY_SIZE(this) = sz;
330}
331
333 if (!(fFlags & kIsArray))
334 return -1;
335 return (Py_ssize_t)ARRAY_SIZE(this);
336}
337
338static PyObject* op_reshape(CPPInstance* self, PyObject* shape) {
339 // Allow the user to fix up the actual (type-strided) size of the buffer.
340 if (!PyTuple_Check(shape) || PyTuple_GET_SIZE(shape) != 1) {
341 PyErr_SetString(PyExc_TypeError, "tuple object of size 1 expected");
342 return nullptr;
343 }
344
345 long sz = PyLong_AsLong(PyTuple_GET_ITEM(shape, 0));
346 if (sz <= 0) {
347 PyErr_SetString(PyExc_ValueError, "array length must be positive");
348 return nullptr;
349 }
350
351 self->CastToArray(sz);
352
354}
355
356static PyObject* op_item(CPPInstance* self, Py_ssize_t idx) {
357 // In C, it is common to represent an array of structs as a pointer to the
358 // first object in the array. If the caller indexes a pointer to an object
359 // that does not define indexing, then highly likely such C-style indexing is
360 // the goal. Just like C, this is potentially unsafe, so caveat emptor.
361
363 PyErr_Format(PyExc_TypeError, "%s object does not support indexing",
364 Py_TYPE(self)->tp_name);
365 return nullptr;
366 }
367
368 if (idx < 0) {
369 // this is debatable, and probably should not care, but the use case is
370 // pretty circumscribed anyway, so might as well keep the functionality
371 // simple
372 PyErr_SetString(PyExc_IndexError,
373 "negative indices not supported for array of structs");
374 return nullptr;
375 }
376
377 if (self->fFlags & CPPInstance::kIsArray) {
378 Py_ssize_t maxidx = ARRAY_SIZE(self);
379 if (0 <= maxidx && maxidx <= idx) {
380 PyErr_SetString(PyExc_IndexError, "index out of range");
381 return nullptr;
382 }
383 }
384
385 unsigned flags = 0;
386 size_t sz = sizeof(void*);
387 if (self->fFlags & CPPInstance::kIsPtrPtr) {
389 } else {
390 sz = interop::SizeOf(((CPPClass*)Py_TYPE(self))->fCppType);
391 }
392
393 uintptr_t address =
394 (uintptr_t)(flags ? self->GetObjectRaw() : self->GetObject());
395 void* indexed_obj = (void*)(address + (uintptr_t)(idx * sz));
396
397 return BindCppObjectNoCast(indexed_obj, ((CPPClass*)Py_TYPE(self))->fCppType,
398 flags);
399}
400
401//- sequence methods --------------------------------------------------------
402static PySequenceMethods op_as_sequence = {
403 0, // sq_length
404 0, // sq_concat
405 0, // sq_repeat
406 (ssizeargfunc)op_item, // sq_item
407 0, // sq_slice
408 0, // sq_ass_item
409 0, // sq_ass_slice
410 0, // sq_contains
411 0, // sq_inplace_concat
412 0, // sq_inplace_repeat
413};
414
416 static PyCFunction reducer = nullptr;
417 return reducer;
418}
419
421 auto& reducer = CPPInstance::ReduceMethod();
422 if (!reducer) {
423 PyErr_SetString(PyExc_NotImplementedError, "");
424 return nullptr;
425 }
426 return reducer(self, args);
427}
428
429//----------------------------------------------------------------------------
430static PyMethodDef op_methods[] = {
431 {(char*)"__destruct__", (PyCFunction)op_destruct, METH_NOARGS,
432 (char*)"call the C++ destructor"},
433 {(char*)"__dispatch__", (PyCFunction)op_dispatch, METH_VARARGS,
434 (char*)"dispatch to selected overload"},
435 {(char*)"__smartptr__", (PyCFunction)op_get_smartptr, METH_NOARGS,
436 (char*)"get associated smart pointer, if any"},
437 {(char*)"__reduce__", (PyCFunction)op_reduce, METH_NOARGS,
438 (char*)"reduce method for serialization"},
439 {(char*)"__reshape__", (PyCFunction)op_reshape, METH_O,
440 (char*)"cast pointer to 1D array type"},
441 {(char*)nullptr, nullptr, 0, nullptr}};
442
443//= cpyrt object proxy construction/destruction ===========================
444static CPPInstance* op_new(PyTypeObject* subtype, PyObject*, PyObject*) {
445 // Create a new object proxy (holder only).
446 CPPInstance* pyobj = (CPPInstance*)subtype->tp_alloc(subtype, 0);
447 pyobj->fObject = nullptr;
449
450 return pyobj;
451}
452
453//----------------------------------------------------------------------------
454static void op_dealloc(CPPInstance* pyobj) {
455 // Remove (Python-side) memory held by the object proxy.
456 PyObject_GC_UnTrack((PyObject*)pyobj);
457 op_dealloc_nofree(pyobj);
458 PyObject_GC_Del((PyObject*)pyobj);
459}
460
461//----------------------------------------------------------------------------
462static int op_clear(CPPInstance* pyobj) {
463 // Garbage collector clear of held python member objects; this is a good time
464 // to safely remove this object from the memory regulator.
467 (PyObject*)Py_TYPE((PyObject*)pyobj));
468
469 return 0;
470}
471
472//----------------------------------------------------------------------------
473static inline PyObject* eqneq_binop(CPPClass* klass, PyObject* self,
474 PyObject* obj, int op) {
475 using namespace Utility;
476
477 // special case for C++11 style nullptr
478 if (obj == gNullPtrObject) {
479 void* rawcpp = ((CPPInstance*)self)->GetObjectRaw();
480 switch (op) {
481 case Py_EQ:
482 if (rawcpp == nullptr)
485 case Py_NE:
486 if (rawcpp != nullptr)
489 default:
490 return nullptr; // not implemented
491 }
492 }
493
494 if (!klass->fOperators)
495 klass->fOperators = new PyOperators{};
496
497 bool flipit = false;
498 PyObject* binop =
499 op == Py_EQ ? klass->fOperators->fEq : klass->fOperators->fNe;
500 if (!binop) {
501 binop = op == Py_EQ ? klass->fOperators->fNe : klass->fOperators->fEq;
502 if (binop)
503 flipit = true;
504 }
505 if (!binop) {
506 // an operator== defined on a base class is proxied into the base's dict
507 // only and cached on the base at its pythonization; consult the mro before
508 // doing a (much more expensive, and possibly failing) lazy lookup: the base
509 // class method is what C++ overload resolution would select anyway (e.g.
510 // MSVC's STL iterators define operator== on the const_iterator base class
511 // only)
512 PyObject* mro = ((PyTypeObject*)klass)->tp_mro;
513 for (Py_ssize_t i = 1; mro && i < PyTuple_GET_SIZE(mro); ++i) {
514 PyObject* base = PyTuple_GET_ITEM(mro, i);
515 // static types such as CPPInstance_Type pass CPPScope_Check (their
516 // metatype is CPPScope_Type), but are plain PyTypeObjects without the
517 // CPPScope layout; only heap types created through the metatype carry
518 // fOperators, so reading it from anything else is out of bounds
519 if (!CPPScope_Check(base) ||
520 !PyType_HasFeature((PyTypeObject*)base, Py_TPFLAGS_HEAPTYPE) ||
521 !((CPPClass*)base)->fOperators)
522 continue;
523 PyOperators* bops = ((CPPClass*)base)->fOperators;
524 PyObject* bop = op == Py_EQ ? bops->fEq : bops->fNe;
525 if (bop && bop != Py_None) {
526 binop = bop;
527 break;
528 }
529 bop = op == Py_EQ ? bops->fNe : bops->fEq;
530 if (bop && bop != Py_None) {
531 binop = bop;
532 flipit = true;
533 break;
534 }
535 }
536 if (binop) {
537 // cache on this class so the mro walk happens only once; a flipped
538 // operator goes into the slot of the op it actually implements, which
539 // is what the slot-based flip logic above expects on the next call
540 Py_INCREF(binop);
541 bool asEq = (op == Py_EQ) != flipit;
542 if (asEq)
543 klass->fOperators->fEq = binop;
544 else
545 klass->fOperators->fNe = binop;
546 }
547 }
548 if (!binop) {
549 const char* cppop = op == Py_EQ ? "==" : "!=";
550 PyCallable* pyfunc = FindBinaryOperator(self, obj, cppop);
551 if (pyfunc)
552 binop = (PyObject*)CPPOverload_New(cppop, pyfunc);
553 else {
554 Py_INCREF(Py_None);
555 binop = Py_None;
556 }
557 // sets the operator to Py_None if not found, indicating that search was
558 // done
559 if (op == Py_EQ)
560 klass->fOperators->fEq = binop;
561 else
562 klass->fOperators->fNe = binop;
563 }
564
565 if (!binop || binop == Py_None)
566 return nullptr;
567
568 PyObject* args = PyTuple_New(1);
569 Py_INCREF(obj);
570 PyTuple_SET_ITEM(args, 0, obj);
571 // since this overload is "ours", don't have to worry about rebinding
572 ((CPPOverload*)binop)->fSelf = (CPPInstance*)self;
573 PyObject* result = CPPOverload_Type.tp_call(binop, args, nullptr);
574 ((CPPOverload*)binop)->fSelf = nullptr;
575 Py_DECREF(args);
576
577 if (!result) {
578 PyErr_Clear();
579 return nullptr;
580 }
581
582 // successful result, but may need to reverse the outcome
583 if (!flipit)
584 return result;
585
586 int istrue = PyObject_IsTrue(result);
587 Py_DECREF(result);
588 if (istrue) {
590 }
592}
593
594static inline void* cast_actual(void* obj) {
595 void* address = ((CPPInstance*)obj)->GetObject();
597 return address;
598
599 interop::TCppScope_t klass = ((CPPClass*)Py_TYPE((PyObject*)obj))->fCppType;
600 interop::TCppScope_t clActual = interop::GetActualClass(klass, address);
601 if (clActual && clActual != klass) {
603 clActual, klass, address, -1 /* down-cast */, true /* report errors */);
604 if (offset != -1)
605 address = (void*)((intptr_t)address + offset);
606 }
607
608 return address;
609}
610
611#define CPYRT_ORDERED_OPERATOR_STUB(op, ometh, label) \
612 if (!ometh) { \
613 PyCallable* pyfunc = \
614 Utility::FindBinaryOperator((PyObject*)self, other, #op); \
615 if (pyfunc) \
616 ometh = (PyObject*)CPPOverload_New(#label, pyfunc); \
617 } \
618 meth = ometh;
619
620static PyObject* op_richcompare(CPPInstance* self, PyObject* other, int op) {
621 // Rich set of comparison objects; currently supported:
622 // == : Py_EQ
623 // != : Py_NE
624 //
625 // < : Py_LT
626 // <= : Py_LE
627 // > : Py_GT
628 // >= : Py_GE
629 //
630
631 // associative comparison operators
632 if (op == Py_EQ || op == Py_NE) {
633 // special case for None to compare True to a null-pointer
634 if ((PyObject*)other == Py_None && !self->fObject) {
635 const char* msg =
636 "\nComparison of C++ nullptr objects with `None` is no longer "
637 "supported."
638 "\n\nPreviously, `None` was treated as equivalent to a null C++ "
639 "pointer, "
640 "but this led to confusing behavior where `x == None` could be True "
641 "even though `x is None` was False."
642 "\n\nTo test whether a C++ object is null or not, check its truth "
643 "value instead:"
644 "\n if not x: ..."
645 "\nor use `x is None` to explicitly check for Python None."
646 "\n";
647
648 PyErr_SetString(PyExc_TypeError, msg);
649 return NULL; // stop execution, raise TypeError
650 }
651
652 // use C++-side operators if available
654 eqneq_binop((CPPClass*)Py_TYPE(self), (PyObject*)self, other, op);
655 if (!result && CPPInstance_Check(other))
656 result =
657 eqneq_binop((CPPClass*)Py_TYPE(other), other, (PyObject*)self, op);
658 if (result)
659 return result;
660
661 // default behavior: type + held pointer value defines identity; if both are
662 // CPPInstance objects, perform an additional autocast if need be
663 bool bIsEq = false;
664
665 if ((Py_TYPE(self) == Py_TYPE(other) &&
666 self->GetObject() == ((CPPInstance*)other)->GetObject())) {
667 // direct match
668 bIsEq = true;
669 } else if (CPPInstance_Check(other)) {
670 // try auto-cast match
671 void* addr1 = cast_actual(self);
672 void* addr2 = cast_actual(other);
673 bIsEq = addr1 && addr2 && (addr1 == addr2);
674 }
675
676 if ((op == Py_EQ && bIsEq) || (op == Py_NE && !bIsEq))
678
680 }
681
682 // ordered comparison operators
683 else if (op == Py_LT || op == Py_LE || op == Py_GT || op == Py_GE) {
684 CPPClass* klass = (CPPClass*)Py_TYPE(self);
685 if (!klass->fOperators)
686 klass->fOperators = new Utility::PyOperators{};
687 PyObject* meth = nullptr;
688
689 switch (op) {
690 case Py_LT:
691 CPYRT_ORDERED_OPERATOR_STUB(<, klass->fOperators->fLt, __lt__)
692 break;
693 case Py_LE:
694 CPYRT_ORDERED_OPERATOR_STUB(<=, klass->fOperators->fLe, __le__)
695 break;
696 case Py_GT:
697 CPYRT_ORDERED_OPERATOR_STUB(>, klass->fOperators->fGt, __gt__)
698 break;
699 case Py_GE:
700 CPYRT_ORDERED_OPERATOR_STUB(>=, klass->fOperators->fGe, __ge__)
701 break;
702 }
703
704 if (!meth) {
705 PyErr_SetString(PyExc_NotImplementedError, "");
706 return nullptr;
707 }
708
709 return PyObject_CallFunctionObjArgs(meth, (PyObject*)self, other, nullptr);
710 }
711
712 Py_INCREF(Py_NotImplemented);
713 return Py_NotImplemented;
714}
715
716//----------------------------------------------------------------------------
718 // Build a representation string of the object proxy that shows the address
719 // of the C++ object that is held, as well as its type.
720 PyObject* pyclass = (PyObject*)Py_TYPE(self);
721 if (CPPScope_Check(pyclass) &&
722 (((CPPScope*)pyclass)->fFlags & CPPScope::kIsPython))
723 return PyBaseObject_Type.tp_repr((PyObject*)self);
724 PyObject* modname = PyObject_GetAttr(pyclass, PyStrings::gModule);
725
726 interop::TCppScope_t klass = self->ObjectIsA();
727 std::string clName = klass ? interop::GetFinalName(klass) : "<unknown>";
728 if (self->fFlags & CPPInstance::kIsPtrPtr)
729 clName.append("**");
730 else if (self->fFlags & CPPInstance::kIsReference)
731 clName.append("*");
732
733 PyObject* repr = nullptr;
734 if (self->IsSmart()) {
735 std::string smartPtrName = interop::GetScopedFinalName(SMART_TYPE(self));
736 repr = PyUnicode_FromFormat(
737 const_cast<char*>("<%s.%s object at %p held by %s at %p>"),
738 PyUnicode_AsUTF8(modname), clName.c_str(), self->GetObject(),
739 smartPtrName.c_str(), self->GetObjectRaw());
740 } else {
741 repr = PyUnicode_FromFormat(const_cast<char*>("<%s.%s object at %p>"),
742 PyUnicode_AsUTF8(modname), clName.c_str(),
743 self->GetObject());
744 }
745
746 Py_DECREF(modname);
747 return repr;
748}
749
750//----------------------------------------------------------------------------
751static inline Py_hash_t cpyrt_PyLong_AsHash_t(PyObject* obj) {
752 // Cannot use PyLong_AsSize_t here, as it cuts of at PY_SSIZE_T_MAX, which is
753 // only half of the max of std::size_t returned by the hash.
754 if (sizeof(unsigned long) >= sizeof(size_t))
755 return (Py_hash_t)PyLong_AsUnsignedLong(obj);
756 return (Py_hash_t)PyLong_AsUnsignedLongLong(obj);
757}
758
759static Py_hash_t op_hash(CPPInstance* self) {
760 // Try to locate an std::hash for this type and use that if it exists
761 CPPClass* klass = (CPPClass*)Py_TYPE(self);
762 if (klass->fOperators && klass->fOperators->fHash) {
763 Py_hash_t h = 0;
764 PyObject* hashval = PyObject_CallFunctionObjArgs(klass->fOperators->fHash,
765 (PyObject*)self, nullptr);
766 if (hashval) {
767 h = cpyrt_PyLong_AsHash_t(hashval);
768 Py_DECREF(hashval);
769 }
770 return h;
771 }
772
774 "std::hash<" + interop::GetScopedFinalName(self->ObjectIsA()) + ">");
775 if (stdhash) {
776 PyObject* hashcls = CreateScopeProxy(stdhash);
777 PyObject* dct = PyObject_GetAttr(hashcls, PyStrings::gDict);
778 bool isValid = PyMapping_HasKeyString(dct, (char*)"__call__");
779 Py_DECREF(dct);
780 if (isValid) {
781 PyObject* hashobj = PyObject_CallObject(hashcls, nullptr);
782 if (!klass->fOperators)
783 klass->fOperators = new Utility::PyOperators{};
784 klass->fOperators->fHash = hashobj;
785 Py_DECREF(hashcls);
786
787 Py_hash_t h = 0;
788 PyObject* hashval =
789 PyObject_CallFunctionObjArgs(hashobj, (PyObject*)self, nullptr);
790 if (hashval) {
791 h = cpyrt_PyLong_AsHash_t(hashval);
792 Py_DECREF(hashval);
793 }
794 return h;
795 }
796 Py_DECREF(hashcls);
797 }
798
799 // if not valid, simply reset the hash function so as to not kill performance
800 ((PyTypeObject*)Py_TYPE(self))->tp_hash = PyBaseObject_Type.tp_hash;
801 return PyBaseObject_Type.tp_hash((PyObject*)self);
802}
803
804//----------------------------------------------------------------------------
806 bool isBound) {
807 static interop::TCppScope_t sOStringStreamID =
808 interop::GetFullScope("std::ostringstream");
809 std::ostringstream s;
810 PyObject* pys = BindCppObjectNoCast(&s, sOStringStreamID);
811 Py_INCREF(pys);
812 // for py3 and later, a ref-count of 2 is okay to consider the object
813 // temporary, but in this case, we can't lose our existing ostrinstring
814 // (otherwise, we'd have to peel it out of the return value, if moves are used
815 Py_INCREF(pys);
816
817 PyObject* res;
818 if (isBound)
819 res = PyObject_CallFunctionObjArgs(lshift, pys, NULL);
820 else
821 res = PyObject_CallFunctionObjArgs(lshift, pys, pyobj, NULL);
822
823 Py_DECREF(pys);
824 Py_DECREF(pys);
825
826 if (res) {
827 Py_DECREF(res);
828 return PyUnicode_FromString(s.str().c_str());
829 }
830
831 return nullptr;
832}
833
834static inline bool ScopeFlagCheck(CPPInstance* self, CPPScope::EFlags flag) {
835 return ((CPPScope*)Py_TYPE((PyObject*)self))->fFlags & flag;
836}
837
838static inline void ScopeFlagSet(CPPInstance* self, CPPScope::EFlags flag) {
839 ((CPPScope*)Py_TYPE((PyObject*)self))->fFlags |= flag;
840}
841
843 // There are three possible options here:
844 // 1. Available operator<< to convert through an ostringstream
845 // 2. Cling's pretty printing
846 // 3. Generic printing as done in op_repr
847 //
848 // Additionally, there may be a mapped __str__ from the C++ type defining
849 // `operator char*` or `operator const char*`. Results are memoized for
850 // performance reasons.
851
852 // 0. Protect against trying to print a typed nullptr object through an
853 // insertion operator
854 if (!self->GetObject())
855 return op_repr(self);
856
857 // 1. Available operator<< to convert through an ostringstream
860 (PyObject*)0x01, (PyObject*)0x02}) {
861 if (pyname == PyStrings::gLShift &&
863 continue;
864
865 else if (pyname == (PyObject*)0x01) {
866 // normal lookup failed; attempt lazy install of global
867 // operator<<(ostream&, type&)
868 std::string rcname = Utility::ClassName((PyObject*)self);
871 PyCallable* pyfunc =
872 Utility::FindBinaryOperator("std::ostream", rcname, "<<", rnsID);
873 if (!pyfunc)
874 continue;
875
876 Utility::AddToClass((PyObject*)Py_TYPE((PyObject*)self), "__lshiftc__",
877 pyfunc);
878
879 pyname = PyStrings::gLShiftC;
881
882 } else if (pyname == (PyObject*)0x02) {
883 // TODO: the only reason this still exists, is b/c friend functions are
884 // otherwise not found
885 // TODO: ToString() still leaks ...
886 const std::string& pretty =
887 compat::ObjToString(self->ObjectIsA(), self->GetObject());
888 if (!pretty.empty())
889 return PyUnicode_FromString(pretty.c_str());
890 continue;
891 }
892
893 PyObject* lshift = PyObject_GetAttr(
894 pyname == PyStrings::gLShift ? (PyObject*)self
895 : (PyObject*)Py_TYPE((PyObject*)self),
896 pyname);
897
898 if (lshift) {
899 PyObject* result = op_str_internal((PyObject*)self, lshift,
900 pyname == PyStrings::gLShift);
901 Py_DECREF(lshift);
902 if (result)
903 return result;
904 }
905
906 PyErr_Clear();
907 }
908
909 // failed ostream printing; don't try again
911 }
912
913 // 2. Cling's pretty printing (not done through backend for performance
914 // reasons)
916 PyObject* printValue = compat::GetClingPrintValue();
917
918 if (printValue) {
919 // as printValue only works well for templates taking pointer arguments,
920 // we'll have to force the issue by working with a by-ptr object
921 interop::TCppObject_t cppobj = self->GetObjectRaw();
922 PyObject* byref = (PyObject*)self;
923 if (!(self->fFlags & CPPInstance::kIsReference)) {
924 byref = BindCppObjectNoCast(
925 (interop::TCppObject_t)&cppobj, self->ObjectIsA(),
927 } else {
928 Py_INCREF(byref);
929 }
930
931 // explicit template lookup
932 PyObject* clName =
933 PyUnicode_FromString(Utility::ClassName((PyObject*)self).c_str());
934 PyObject* OL = PyObject_GetItem(printValue, clName);
935 Py_DECREF(clName);
936
937 PyObject* pretty =
938 OL ? PyObject_CallFunctionObjArgs(OL, byref, nullptr) : nullptr;
939 Py_XDECREF(OL);
940 Py_DECREF(byref);
941
942 PyObject* result = nullptr;
943 if (pretty) {
944 // the std::string executor may return a Python string
945 std::string pv;
946 if (PyUnicode_Check(pretty)) {
947 if (const char* s = PyUnicode_AsUTF8(pretty))
948 pv = s;
949 else
950 PyErr_Clear();
951 } else if (CPPInstance_Check(pretty) &&
952 ((CPPInstance*)pretty)->GetObject()) {
953 pv = *(std::string*)((CPPInstance*)pretty)->GetObject();
954 }
955 // an address or the generic fallback means no usable pretty printer
956 if (!pv.empty() && pv.find("@0x") == std::string::npos &&
957 pv != "{not representable}")
958 result = PyUnicode_FromString(pv.c_str());
959 Py_DECREF(pretty);
960 if (result)
961 return result;
962 }
963
964 PyErr_Clear();
965 }
966
967 // if not available/specialized, don't try again
969 }
970
971 // 3. Generic printing as done in op_repr
972 return op_repr(self);
973}
974
975//-----------------------------------------------------------------------------
976static PyObject* op_getownership(CPPInstance* pyobj, void*) {
977 return PyBool_FromLong((long)(pyobj->fFlags & CPPInstance::kIsOwner));
978}
979
980//-----------------------------------------------------------------------------
981static int op_setownership(CPPInstance* pyobj, PyObject* value, void*) {
982 // Set the ownership (True is python-owns) for the given object.
983 long shouldown = PyLong_AsLong(value);
984 if (shouldown == -1 && PyErr_Occurred()) {
985 PyErr_SetString(PyExc_ValueError,
986 "__python_owns__ should be either True or False");
987 return -1;
988 }
989
990 (bool)shouldown ? pyobj->PythonOwns() : pyobj->CppOwns();
991
992 return 0;
993}
994
995//-----------------------------------------------------------------------------
996static PyGetSetDef op_getset[] = {
997 {(char*)"__python_owns__", (getter)op_getownership, (setter)op_setownership,
998 (char*)"If true, python manages the life time of this object", nullptr},
999 {(char*)nullptr, nullptr, nullptr, nullptr, nullptr}};
1000
1001//= cpyrt type number stubs to allow dynamic overrides =====================
1002#define CPYRT_STUB_BODY(name, op) \
1003 bool previously_resolved_overload = (bool)meth; \
1004 if (!meth) { \
1005 PyErr_Clear(); \
1006 PyCallable* pyfunc = Utility::FindBinaryOperator(left, right, #op); \
1007 if (pyfunc) \
1008 meth = (PyObject*)CPPOverload_New(#name, pyfunc); \
1009 else { \
1010 PyErr_SetString(PyExc_NotImplementedError, ""); \
1011 return nullptr; \
1012 } \
1013 } \
1014 PyObject* res = PyObject_CallFunctionObjArgs(meth, cppobj, other, nullptr); \
1015 if (!res && previously_resolved_overload) { \
1016 /* try again, in case (left, right) are different types than before */ \
1017 PyErr_Clear(); \
1018 PyCallable* pyfunc = Utility::FindBinaryOperator(left, right, #op); \
1019 if (pyfunc) \
1020 ((CPPOverload*&)meth)->AdoptMethod(pyfunc); \
1021 else { \
1022 PyErr_SetString(PyExc_NotImplementedError, ""); \
1023 return nullptr; \
1024 } \
1025 /* use same overload with newly added function */ \
1026 res = PyObject_CallFunctionObjArgs(meth, cppobj, other, nullptr); \
1027 } \
1028 return res;
1029
1030#define CPYRT_OPERATOR_STUB(name, op, ometh) \
1031 static PyObject* op_##name##_stub(PyObject* left, PyObject* right) { \
1032 /* placeholder to lazily install and forward to 'ometh' if available */ \
1033 CPPClass* klass = (CPPClass*)Py_TYPE(left); \
1034 if (!klass->fOperators) \
1035 klass->fOperators = new Utility::PyOperators{}; \
1036 PyObject*& meth = ometh; \
1037 PyObject *cppobj = left, *other = right; \
1038 CPYRT_STUB_BODY(name, op) \
1039 }
1040
1041#define CPYRT_ASSOCIATIVE_OPERATOR_STUB(name, op, lmeth, rmeth) \
1042 static PyObject* op_##name##_stub(PyObject* left, PyObject* right) { \
1043 /* placeholder to lazily install and forward do '(l/r)meth' if available \
1044 */ \
1045 CPPClass* klass; \
1046 PyObject** pmeth; \
1047 PyObject *cppobj, *other; \
1048 if (CPPInstance_Check(left)) { \
1049 klass = (CPPClass*)Py_TYPE(left); \
1050 if (!klass->fOperators) \
1051 klass->fOperators = new Utility::PyOperators{}; \
1052 pmeth = &lmeth; \
1053 cppobj = left; \
1054 other = right; \
1055 } else if (CPPInstance_Check(right)) { \
1056 klass = (CPPClass*)Py_TYPE(right); \
1057 if (!klass->fOperators) \
1058 klass->fOperators = new Utility::PyOperators{}; \
1059 pmeth = &rmeth; \
1060 cppobj = right; \
1061 other = left; \
1062 } else { \
1063 PyErr_SetString(PyExc_NotImplementedError, ""); \
1064 return nullptr; \
1065 } \
1066 PyObject*& meth = *pmeth; \
1067 CPYRT_STUB_BODY(name, op) \
1068 }
1070#define CPYRT_UNARY_OPERATOR(name, op, label) \
1071 static PyObject* op_##name##_stub(PyObject* pyobj) { \
1072 /* placeholder to lazily install unary operators */ \
1073 PyCallable* pyfunc = \
1074 Utility::FindUnaryOperator((PyObject*)Py_TYPE(pyobj), #op); \
1075 if (pyfunc && \
1076 Utility::AddToClass((PyObject*)Py_TYPE(pyobj), #label, pyfunc)) \
1077 return PyObject_CallMethod(pyobj, (char*)#label, nullptr); \
1078 PyErr_SetString(PyExc_NotImplementedError, ""); \
1079 return nullptr; \
1080 }
1081
1082// clang-format off
1083CPYRT_ASSOCIATIVE_OPERATOR_STUB(add, +, klass->fOperators->fLAdd, klass->fOperators->fRAdd)
1084CPYRT_OPERATOR_STUB( sub, -, klass->fOperators->fSub)
1085CPYRT_ASSOCIATIVE_OPERATOR_STUB(mul, *, klass->fOperators->fLMul, klass->fOperators->fRMul)
1086CPYRT_OPERATOR_STUB( div, /, klass->fOperators->fDiv)
1087CPYRT_UNARY_OPERATOR(neg, -, __neg__)
1088CPYRT_UNARY_OPERATOR(pos, +, __pos__)
1089CPYRT_UNARY_OPERATOR(invert, ~, __invert__)
1090// clang-format on
1091
1092//-----------------------------------------------------------------------------
1093static PyNumberMethods op_as_number = {
1094 (binaryfunc)op_add_stub, // nb_add
1095 (binaryfunc)op_sub_stub, // nb_subtract
1096 (binaryfunc)op_mul_stub, // nb_multiply
1097 0, // nb_remainder
1098 0, // nb_divmod
1099 0, // nb_power
1100 (unaryfunc)op_neg_stub, // nb_negative
1101 (unaryfunc)op_pos_stub, // nb_positive
1102 0, // nb_absolute
1103 (inquiry)op_nonzero, // nb_bool (nb_nonzero in p2)
1104 (unaryfunc)op_invert_stub, // nb_invert
1105 0, // nb_lshift
1106 0, // nb_rshift
1107 0, // nb_and
1108 0, // nb_xor
1109 0, // nb_or
1110 0, // nb_int
1111 0, // nb_long (nb_reserved in p3)
1112 0, // nb_float
1113 0, // nb_inplace_add
1114 0, // nb_inplace_subtract
1115 0, // nb_inplace_multiply
1116 0, // nb_inplace_remainder
1117 0, // nb_inplace_power
1118 0, // nb_inplace_lshift
1119 0, // nb_inplace_rshift
1120 0, // nb_inplace_and
1121 0, // nb_inplace_xor
1122 0, // nb_inplace_or
1123 0, // nb_floor_divide
1124 (binaryfunc)op_div_stub, // nb_true_divide
1125 0, // nb_inplace_floor_divide
1126 0, // nb_inplace_true_divide
1127 0, // nb_index
1128 0, // nb_matrix_multiply
1129 0 // nb_inplace_matrix_multiply
1130};
1131
1132//= cpyrt object proxy type ===============================================
1133PyTypeObject CPPInstance_Type = {
1134 PyVarObject_HEAD_INIT(&CPPScope_Type,
1135 0)(char*) "cppjit.CPPInstance", // tp_name
1136 sizeof(CPPInstance), // tp_basicsize
1137 0, // tp_itemsize
1138 (destructor)op_dealloc, // tp_dealloc
1139 0, // tp_vectorcall_offset / tp_print
1140 0, // tp_getattr
1141 0, // tp_setattr
1142 0, // tp_as_async / tp_compare
1143 (reprfunc)op_repr, // tp_repr
1144 &op_as_number, // tp_as_number
1145 &op_as_sequence, // tp_as_sequence
1146 0, // tp_as_mapping
1147 (hashfunc)op_hash, // tp_hash
1148 0, // tp_call
1149 (reprfunc)op_str, // tp_str
1150 0, // tp_getattro
1151 0, // tp_setattro
1152 0, // tp_as_buffer
1153 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, // tp_flags
1154 (char*)"cppjit object proxy (internal)", // tp_doc
1155 (traverseproc)op_traverse, // tp_traverse
1156 (inquiry)op_clear, // tp_clear
1157 (richcmpfunc)op_richcompare, // tp_richcompare
1158 0, // tp_weaklistoffset
1159 0, // tp_iter
1160 0, // tp_iternext
1161 op_methods, // tp_methods
1162 0, // tp_members
1163 op_getset, // tp_getset
1164 0, // tp_base
1165 0, // tp_dict
1166 0, // tp_descr_get
1167 0, // tp_descr_set
1168 0, // tp_dictoffset
1169 0, // tp_init
1170 0, // tp_alloc
1171 (newfunc)op_new, // tp_new
1172 0, // tp_free
1173 0, // tp_is_gc
1174 0, // tp_bases
1175 0, // tp_mro
1176 0, // tp_cache
1177 0, // tp_subclasses
1178 0, // tp_weaklist
1179 0, // tp_del
1180 0, // tp_version_tag
1181 0, // tp_finalize
1182 0 // tp_vectorcall
1184
1185} // namespace cppjit::cpyrt
#define SMART_CLS(pyobj)
#define CPYRT_UNARY_OPERATOR(name, op, label)
#define EXT_OBJECT(pyobj)
#define CPYRT_ASSOCIATIVE_OPERATOR_STUB(name, op, lmeth, rmeth)
#define SMART_TYPE(pyobj)
#define CPYRT_ORDERED_OPERATOR_STUB(op, ometh, label)
#define DATA_CACHE(pyobj)
#define CPYRT_OPERATOR_STUB(name, op, ometh)
#define DISPATCHPTR(pyobj)
#define ARRAY_SIZE(pyobj)
uint32_t fFlags
_object PyObject
#define h(i)
Definition RSha256.hxx:106
@ kIsArray
Definition TDictionary.h:79
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 Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
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 Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t target
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
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
interop::TCppScope_t GetSmartIsA() const
void CastToArray(Py_ssize_t sz)
interop::TCppScope_t GetSmartUnderlyingType() const
PyObject_HEAD void * fObject
Definition CPPInstance.h:49
CPPInstance * Copy(void *cppinst, PyTypeObject *target=nullptr)
static PyCFunction & ReduceMethod()
void SetSmart(PyObject *smart_type)
interop::TCppScope_t ObjectIsA(bool check_smart=true) const
CI_DatamemberCache_t & GetDatamemberCache()
Utility::PyOperators * fOperators
Definition CPPScope.h:68
static bool RegisterPyObject(CPPInstance *pyobj, interop::TCppObject_t cppobj)
static bool UnregisterPyObject(CPPInstance *pyobj, PyObject *pyclass)
#define Py_RETURN_TRUE
Definition cpyrt.h:73
#define Py_RETURN_FALSE
Definition cpyrt.h:77
#define CPYRT_PYTYPE_TAIL
Definition cpyrt.h:159
#define Py_RETURN_NONE
Definition cpyrt.h:69
std::string extract_namespace(const std::string &name)
std::string ClassName(PyObject *pyobj)
Definition Utility.cxx:1402
PyCallable * FindBinaryOperator(PyObject *left, PyObject *right, const char *op, interop::TCppScope_t scope=interop::TCppScope_t{})
Definition Utility.cxx:293
bool AddToClass(PyObject *pyclass, const char *label, PyCFunction cfunc, int flags=METH_VARARGS)
Definition Utility.cxx:178
PyObject * GetClingPrintValue()
std::string ObjToString(interop::TCppScope_t, interop::TCppObject_t)
static PyObject * op_repr(CPPInstance *self)
static PyObject * op_pos_stub(PyObject *pyobj)
static void * cast_actual(void *obj)
static int op_setownership(CPPInstance *pyobj, PyObject *value, void *)
PyTypeObject CPPScope_Type
Definition CPPScope.cxx:686
PyObject * gNullPtrObject
static PyObject * op_item(CPPInstance *self, Py_ssize_t idx)
PyTypeObject CPPInstance_Type
PyTypeObject CPPOverload_Type
static PyObject * op_reshape(CPPInstance *self, PyObject *shape)
static void op_dealloc(CPPInstance *pyobj)
static PySequenceMethods op_as_sequence
static PyObject * op_richcompare(CPPInstance *self, PyObject *other, int op)
void op_dealloc_nofree(CPPInstance *)
static int op_traverse(CPPInstance *, visitproc, void *)
static PyObject * op_invert_stub(PyObject *pyobj)
static PyObject * op_destruct(CPPInstance *self)
static Py_hash_t cpyrt_PyLong_AsHash_t(PyObject *obj)
static PyMethodDef op_methods[]
static PyObject * op_get_smartptr(CPPInstance *self)
PyObject * CreateScopeProxy(PyObject *, PyObject *args)
static CPPInstance * op_new(PyTypeObject *subtype, PyObject *, PyObject *)
static PyObject * op_div_stub(PyObject *left, PyObject *right)
static PyObject * op_sub_stub(PyObject *left, PyObject *right)
static bool ScopeFlagCheck(CPPInstance *self, CPPScope::EFlags flag)
static int op_clear(CPPInstance *pyobj)
std::vector< std::pair< ptrdiff_t, PyObject * > > CI_DatamemberCache_t
Definition CPPInstance.h:26
static PyObject * op_str_internal(PyObject *pyobj, PyObject *lshift, bool isBound)
static PyNumberMethods op_as_number
static PyObject * op_dispatch(PyObject *self, PyObject *args, PyObject *)
PyObject * BindCppObjectNoCast(interop::TCppObject_t object, interop::TCppScope_t klass, const unsigned flags=0)
static PyObject * eqneq_binop(CPPClass *klass, PyObject *self, PyObject *obj, int op)
static PyGetSetDef op_getset[]
static PyObject * op_getownership(CPPInstance *pyobj, void *)
static Py_hash_t op_hash(CPPInstance *self)
static void ScopeFlagSet(CPPInstance *self, CPPScope::EFlags flag)
bool CPPScope_Check(T *object)
Definition CPPScope.h:86
static PyObject * op_add_stub(PyObject *left, PyObject *right)
bool CPPInstance_Check(T *object)
static PyObject * op_neg_stub(PyObject *pyobj)
CPPOverload * CPPOverload_New(const std::string &name, std::vector< PyCallable * > &methods)
static PyObject * op_mul_stub(PyObject *left, PyObject *right)
static int op_nonzero(CPPInstance *self)
PyObject * op_reduce(PyObject *self, PyObject *args)
static PyObject * op_str(CPPInstance *self)
TCppScope_t GetFullScope(const std::string &scope_name)
Cpp::ObjectRef TCppObject_t
Definition API.h:43
void CallDestructor(TCppScope_t type, TCppObject_t self)
TCppScope_t GetActualClass(TCppScope_t klass, TCppObject_t obj)
void Destruct(TCppScope_t scope, TCppObject_t instance)
size_t SizeOf(TCppScope_t klass)
void * CallR(TCppMethod_t method, TCppObject_t self, size_t nargs, void *args)
TCppScope_t GetScope(const std::string &scope_name, TCppScope_t parent_scope=TCppScope_t{})
std::string GetFinalName(TCppScope_t type)
ptrdiff_t GetBaseOffset(TCppScope_t derived, TCppScope_t base, TCppObject_t address, int direction, bool rerror=false)
void Deallocate(TCppScope_t scope, TCppObject_t instance)
std::string GetScopedFinalName(TCppScope_t type)
Cpp::DeclRef TCppScope_t
Definition API.h:41
Definition API.cxx:34