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