Logo ROOT  
Reference Guide
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 
12 #include "CPyCppyy/DispatchPtr.h"
13 
14 // Standard
15 #include <algorithm>
16 #include <sstream>
17 
18 
19 //______________________________________________________________________________
20 // Python-side proxy objects
21 // =========================
22 //
23 // C++ objects are represented in Python by CPPInstances, which encapsulate
24 // them using either a pointer (normal), pointer-to-pointer (kIsReference set),
25 // or as an owned value (kIsValue set). Objects held as reference are never
26 // owned, otherwise the object is owned if kIsOwner is set.
27 //
28 // In addition to encapsulation, CPPInstance offers rudimentary comparison
29 // operators (based on pointer value and class comparisons); stubs (with lazy
30 // lookups) for numeric operators; and a representation that prints the C++
31 // pointer values, rather than the PyObject* ones as is the default.
32 //
33 // Smart pointers have the underlying type as the Python type, but store the
34 // pointer to the smart pointer. They carry a pointer to the Python-sode smart
35 // class for dereferencing to get to the actual instance pointer.
36 
37 
38 //- private helpers ----------------------------------------------------------
39 namespace {
40 
41 // Several specific use cases require extra data in a CPPInstance, but can not
42 // be a new type. E.g. cross-inheritance derived types are by definition added
43 // a posterio, and caching of datamembers is up to the datamember, not the
44 // instance type. To not have normal use of CPPInstance take extra memory, this
45 // extended data can slot in place of fObject for those use cases.
46 
47 struct ExtendedData {
48  ExtendedData() : fObject(nullptr), fSmartClass(nullptr), fTypeSize(0), fLastState(nullptr), fDispatchPtr(nullptr) {}
49  ~ExtendedData() {
50  for (auto& pc : fDatamemberCache)
51  Py_XDECREF(pc.second);
52  fDatamemberCache.clear();
53  }
54 
55 // the original object reference it replaces (Note: has to be first data member, see usage
56 // in GetObjectRaw(), e.g. for ptr-ptr passing)
57  void* fObject;
58 
59 // for smart pointer types
60  CPyCppyy::CPPSmartClass* fSmartClass;
61  size_t fTypeSize;
62  void* fLastState;
63 
64 // for caching expensive-to-create data member representations
65  CPyCppyy::CI_DatamemberCache_t fDatamemberCache;
66 
67 // for back-referencing from Python-derived instances
68  CPyCppyy::DispatchPtr* fDispatchPtr;
69 };
70 
71 } // unnamed namespace
72 
73 #define EXT_OBJECT(pyobj) ((ExtendedData*)((pyobj)->fObject))->fObject
74 #define SMART_CLS(pyobj) ((ExtendedData*)((pyobj)->fObject))->fSmartClass
75 #define SMART_TYPE(pyobj) SMART_CLS(pyobj)->fCppType
76 #define DISPATCHPTR(pyobj) ((ExtendedData*)((pyobj)->fObject))->fDispatchPtr
77 #define DATA_CACHE(pyobj) ((ExtendedData*)((pyobj)->fObject))->fDatamemberCache
78 
80  if (fFlags & kIsExtended)
81  return;
82  void* obj = fObject;
83  fObject = (void*)new ExtendedData{};
84  EXT_OBJECT(this) = obj;
86 }
87 
89 {
90  if (IsSmart()) {
91  // We get the raw pointer from the smart pointer each time, in case it has
92  // changed or has been freed.
93  return Cppyy::CallR(SMART_CLS(this)->fDereferencer, EXT_OBJECT(this), 0, nullptr);
94  }
95  return EXT_OBJECT(this);
96 }
97 
98 
99 //- public methods -----------------------------------------------------------
101 {
102 // create a fresh instance; args and kwds are not used by op_new (see below)
103  PyObject* self = (PyObject*)this;
104  PyTypeObject* pytype = Py_TYPE(self);
105  PyObject* newinst = pytype->tp_new(pytype, nullptr, nullptr);
106 
107 // set the C++ instance as given
108  ((CPPInstance*)newinst)->fObject = cppinst;
109 
110 // look for user-provided __cpp_copy__ (not reusing __copy__ b/c of differences
111 // in semantics: need to pass in the new instance) ...
112  PyObject* cpy = PyObject_GetAttrString(self, (char*)"__cpp_copy__");
113  if (cpy && PyCallable_Check(cpy)) {
114  PyObject* args = PyTuple_New(1);
115  Py_INCREF(newinst);
116  PyTuple_SET_ITEM(args, 0, newinst);
117  PyObject* res = PyObject_CallObject(cpy, args);
118  Py_DECREF(args);
119  Py_DECREF(cpy);
120  if (res) {
121  Py_DECREF(res);
122  return (CPPInstance*)newinst;
123  }
124 
125  // error already set, but need to return nullptr
126  Py_DECREF(newinst);
127  return nullptr;
128  } else if (cpy)
129  Py_DECREF(cpy);
130  else
131  PyErr_Clear();
132 
133 // ... otherwise, shallow copy any Python-side dictionary items
134  PyObject* selfdct = PyObject_GetAttr(self, PyStrings::gDict);
135  PyObject* newdct = PyObject_GetAttr(newinst, PyStrings::gDict);
136  bool bMergeOk = PyDict_Merge(newdct, selfdct, 1) == 0;
137  Py_DECREF(newdct);
138  Py_DECREF(selfdct);
139 
140  if (!bMergeOk) {
141  // presume error already set
142  Py_DECREF(newinst);
143  return nullptr;
144  }
145 
147  return (CPPInstance*)newinst;
148 }
149 
150 
151 //----------------------------------------------------------------------------
153 {
154  fFlags |= kIsOwner;
155  if ((fFlags & kIsExtended) && DISPATCHPTR(this))
156  DISPATCHPTR(this)->PythonOwns();
157 }
158 
159 //----------------------------------------------------------------------------
161 {
162  fFlags &= ~kIsOwner;
163  if ((fFlags & kIsExtended) && DISPATCHPTR(this))
164  DISPATCHPTR(this)->CppOwns();
165 }
166 
167 //----------------------------------------------------------------------------
169 {
170  CreateExtension();
171  Py_INCREF(smart_type);
172  SMART_CLS(this) = (CPPSmartClass*)smart_type;
173  fFlags |= kIsSmartPtr;
174 }
175 
176 //----------------------------------------------------------------------------
178 {
179  if (!IsSmart()) return (Cppyy::TCppType_t)0;
180  return SMART_TYPE(this);
181 }
182 
183 //----------------------------------------------------------------------------
185 {
186 // Return the cache for expensive data objects (and make extended as necessary)
187  CreateExtension();
188  return DATA_CACHE(this);
189 }
190 
191 //----------------------------------------------------------------------------
193 {
194 // Set up the dispatch pointer for memory management
195  CreateExtension();
196  DISPATCHPTR(this) = (DispatchPtr*)ptr;
197 }
198 
199 
200 //----------------------------------------------------------------------------
202 // Destroy the held C++ object, if owned; does not deallocate the proxy.
203 
204  Cppyy::TCppType_t klass = pyobj->ObjectIsA(false /* check_smart */);
205  void*& cppobj = pyobj->GetObjectRaw();
206 
207  if (pyobj->fFlags & CPPInstance::kIsRegulated)
209 
210  if (pyobj->fFlags & CPPInstance::kIsOwner) {
211  if (pyobj->fFlags & CPPInstance::kIsValue) {
212  Cppyy::CallDestructor(klass, cppobj);
213  Cppyy::Deallocate(klass, cppobj);
214  } else {
215  if (cppobj) Cppyy::Destruct(klass, cppobj);
216  }
217  }
218  cppobj = nullptr;
219 
220  if (pyobj->IsExtended()) delete (ExtendedData*)pyobj->fObject;
221  pyobj->fFlags = CPPInstance::kNoWrapConv;
222 }
223 
224 
225 namespace CPyCppyy {
226 
227 //= CPyCppyy object proxy null-ness checking =================================
228 static int op_nonzero(CPPInstance* self)
229 {
230 // Null of the proxy is determined by null-ness of the held C++ object.
231  if (!self->GetObject())
232  return 0;
233 
234 // If the object is valid, then the normal Python behavior is to allow __len__
235 // to determine truth. However, that function is defined in typeobject.c and only
236 // installed if tp_as_number exists w/o the nb_nonzero/nb_bool slot filled in, so
237 // it can not be called directly. Instead, since we're only ever dealing with
238 // CPPInstance derived objects, ignore length from sequence or mapping and call
239 // the __len__ method, if any, directly.
240 
241  PyObject* pylen = PyObject_CallMethodObjArgs((PyObject*)self, PyStrings::gLen, NULL);
242  if (!pylen) {
243  PyErr_Clear();
244  return 1; // since it's still a valid object
245  }
246 
247  int result = PyObject_IsTrue(pylen);
248  Py_DECREF(pylen);
249  return result;
250 }
251 
252 //= CPyCppyy object explicit destruction =====================================
254 {
255 // User access to force deletion of the object. Needed in case of a true
256 // garbage collector (like in PyPy), to allow the user control over when
257 // the C++ destructor is called. This method requires that the C++ object
258 // is owned (no-op otherwise).
259  op_dealloc_nofree(self);
261 }
262 
263 //= CPyCppyy object dispatch support =========================================
264 static PyObject* op_dispatch(PyObject* self, PyObject* args, PyObject* /* kdws */)
265 {
266 // User-side __dispatch__ method to allow selection of a specific overloaded
267 // method. The actual selection is in the __overload__() method of CPPOverload.
268  PyObject *mname = nullptr, *sigarg = nullptr;
269  if (!PyArg_ParseTuple(args, const_cast<char*>("O!O!:__dispatch__"),
270  &CPyCppyy_PyText_Type, &mname, &CPyCppyy_PyText_Type, &sigarg))
271  return nullptr;
272 
273 // get the named overload
274  PyObject* pymeth = PyObject_GetAttr(self, mname);
275  if (!pymeth)
276  return nullptr;
277 
278 // get the '__overload__' method to allow overload selection
279  PyObject* pydisp = PyObject_GetAttrString(pymeth, const_cast<char*>("__overload__"));
280  if (!pydisp) {
281  Py_DECREF(pymeth);
282  return nullptr;
283  }
284 
285 // finally, call dispatch to get the specific overload
286  PyObject* oload = PyObject_CallFunctionObjArgs(pydisp, sigarg, nullptr);
287  Py_DECREF(pydisp);
288  Py_DECREF(pymeth);
289  return oload;
290 }
291 
292 //= CPyCppyy smart pointer support ===========================================
294 {
295  if (!self->IsSmart()) {
296  // TODO: more likely should raise
298  }
299 
300  return CPyCppyy::BindCppObjectNoCast(self->GetSmartObject(), SMART_TYPE(self), CPPInstance::kNoWrapConv);
301 }
302 
303 
304 //----------------------------------------------------------------------------
305 static PyMethodDef op_methods[] = {
306  {(char*)"__destruct__", (PyCFunction)op_destruct, METH_NOARGS, nullptr},
307  {(char*)"__dispatch__", (PyCFunction)op_dispatch, METH_VARARGS,
308  (char*)"dispatch to selected overload"},
309  {(char*)"__smartptr__", (PyCFunction)op_get_smartptr, METH_NOARGS,
310  (char*)"get associated smart pointer, if any"},
311  {(char*)nullptr, nullptr, 0, nullptr}
312 };
313 
314 
315 //= CPyCppyy object proxy construction/destruction ===========================
316 static CPPInstance* op_new(PyTypeObject* subtype, PyObject*, PyObject*)
317 {
318 // Create a new object proxy (holder only).
319  CPPInstance* pyobj = (CPPInstance*)subtype->tp_alloc(subtype, 0);
320  pyobj->fObject = nullptr;
322 
323  return pyobj;
324 }
325 
326 //----------------------------------------------------------------------------
327 static void op_dealloc(CPPInstance* pyobj)
328 {
329 // Remove (Python-side) memory held by the object proxy.
330  PyObject_GC_UnTrack((PyObject*)pyobj);
331  op_dealloc_nofree(pyobj);
332  PyObject_GC_Del((PyObject*)pyobj);
333 }
334 
335 //----------------------------------------------------------------------------
336 static int op_clear(CPPInstance* pyobj)
337 {
338 // Garbage collector clear of held python member objects; this is a good time
339 // to safely remove this object from the memory regulator.
340  if (pyobj->fFlags & CPPInstance::kIsRegulated)
342 
343  return 0;
344 }
345 
346 //----------------------------------------------------------------------------
347 static inline PyObject* eqneq_binop(CPPClass* klass, PyObject* self, PyObject* obj, int op)
348 {
349  using namespace Utility;
350 
351  if (!klass->fOperators)
352  klass->fOperators = new PyOperators{};
353 
354  bool flipit = false;
355  PyObject* binop = op == Py_EQ ? klass->fOperators->fEq : klass->fOperators->fNe;
356  if (!binop) {
357  const char* cppop = op == Py_EQ ? "==" : "!=";
358  PyCallable* pyfunc = FindBinaryOperator(self, obj, cppop);
359  if (pyfunc) binop = (PyObject*)CPPOverload_New(cppop, pyfunc);
360  else {
361  Py_INCREF(Py_None);
362  binop = Py_None;
363  }
364  // sets the operator to Py_None if not found, indicating that search was done
365  if (op == Py_EQ) klass->fOperators->fEq = binop;
366  else klass->fOperators->fNe = binop;
367  }
368 
369  if (binop == Py_None) { // can try !== or !!= as alternatives
370  binop = op == Py_EQ ? klass->fOperators->fNe : klass->fOperators->fEq;
371  if (binop && binop != Py_None) flipit = true;
372  }
373 
374  if (!binop || binop == Py_None) return nullptr;
375 
376  PyObject* args = PyTuple_New(1);
377  Py_INCREF(obj); PyTuple_SET_ITEM(args, 0, obj);
378 // since this overload is "ours", don't have to worry about rebinding
379  ((CPPOverload*)binop)->fSelf = (CPPInstance*)self;
380  PyObject* result = CPPOverload_Type.tp_call(binop, args, nullptr);
381  ((CPPOverload*)binop)->fSelf = nullptr;
382  Py_DECREF(args);
383 
384  if (!result) {
385  PyErr_Clear();
386  return nullptr;
387  }
388 
389 // successful result, but may need to reverse the outcome
390  if (!flipit) return result;
391 
392  int istrue = PyObject_IsTrue(result);
393  Py_DECREF(result);
394  if (istrue) {
396  }
398 }
399 
400 static PyObject* op_richcompare(CPPInstance* self, PyObject* other, int op)
401 {
402 // Rich set of comparison objects; only equals and not-equals are defined.
403  if (op != Py_EQ && op != Py_NE) {
404  Py_INCREF(Py_NotImplemented);
405  return Py_NotImplemented;
406  }
407 
408 // special case for None to compare True to a null-pointer
409  if ((PyObject*)other == Py_None && !self->fObject) {
410  if (op == Py_EQ) { Py_RETURN_TRUE; }
412  }
413 
414 // use C++-side operators if available
415  PyObject* result = eqneq_binop((CPPClass*)Py_TYPE(self), (PyObject*)self, other, op);
416  if (!result && CPPInstance_Check(other))
417  result = eqneq_binop((CPPClass*)Py_TYPE(other), other, (PyObject*)self, op);
418  if (result) return result;
419 
420 // default behavior: type + held pointer value defines identity (covers if
421 // other is not actually an CPPInstance, as ob_type will be unequal)
422  bool bIsEq = false;
423  if (Py_TYPE(self) == Py_TYPE(other) && \
424  self->GetObject() == ((CPPInstance*)other)->GetObject())
425  bIsEq = true;
426 
427  if ((op == Py_EQ && bIsEq) || (op == Py_NE && !bIsEq)) {
429  }
431 }
432 
433 //----------------------------------------------------------------------------
435 {
436 // Build a representation string of the object proxy that shows the address
437 // of the C++ object that is held, as well as its type.
438  PyObject* pyclass = (PyObject*)Py_TYPE(self);
439  PyObject* modname = PyObject_GetAttr(pyclass, PyStrings::gModule);
440 
441  Cppyy::TCppType_t klass = self->ObjectIsA();
442  std::string clName = klass ? Cppyy::GetFinalName(klass) : "<unknown>";
443  if (self->fFlags & CPPInstance::kIsReference)
444  clName.append("*");
445 
446  PyObject* repr = nullptr;
447  if (self->IsSmart()) {
448  std::string smartPtrName = Cppyy::GetScopedFinalName(SMART_TYPE(self));
450  const_cast<char*>("<%s.%s object at %p held by %s at %p>"),
451  CPyCppyy_PyText_AsString(modname), clName.c_str(),
452  self->GetObject(), smartPtrName.c_str(), self->GetObjectRaw());
453  } else {
454  repr = CPyCppyy_PyText_FromFormat(const_cast<char*>("<%s.%s object at %p>"),
455  CPyCppyy_PyText_AsString(modname), clName.c_str(), self->GetObject());
456  }
457 
458  Py_DECREF(modname);
459  return repr;
460 }
461 
462 //----------------------------------------------------------------------------
464 {
465 // Cannot use PyLong_AsSize_t here, as it cuts of at PY_SSIZE_T_MAX, which is
466 // only half of the max of std::size_t returned by the hash.
467  if (sizeof(unsigned long) >= sizeof(size_t))
468  return (Py_hash_t)PyLong_AsUnsignedLong(obj);
469  return (Py_hash_t)PyLong_AsUnsignedLongLong(obj);
470 }
471 
473 {
474 // Try to locate an std::hash for this type and use that if it exists
475  CPPClass* klass = (CPPClass*)Py_TYPE(self);
476  if (klass->fOperators && klass->fOperators->fHash) {
477  Py_hash_t h = 0;
478  PyObject* hashval = PyObject_CallFunctionObjArgs(klass->fOperators->fHash, (PyObject*)self, nullptr);
479  if (hashval) {
480  h = CPyCppyy_PyLong_AsHash_t(hashval);
481  Py_DECREF(hashval);
482  }
483  return h;
484  }
485 
486  Cppyy::TCppScope_t stdhash = Cppyy::GetScope("std::hash<"+Cppyy::GetScopedFinalName(self->ObjectIsA())+">");
487  if (stdhash) {
488  PyObject* hashcls = CreateScopeProxy(stdhash);
489  PyObject* dct = PyObject_GetAttr(hashcls, PyStrings::gDict);
490  bool isValid = PyMapping_HasKeyString(dct, (char*)"__call__");
491  Py_DECREF(dct);
492  if (isValid) {
493  PyObject* hashobj = PyObject_CallObject(hashcls, nullptr);
494  if (!klass->fOperators) klass->fOperators = new Utility::PyOperators{};
495  klass->fOperators->fHash = hashobj;
496  Py_DECREF(hashcls);
497 
498  Py_hash_t h = 0;
499  PyObject* hashval = PyObject_CallFunctionObjArgs(hashobj, (PyObject*)self, nullptr);
500  if (hashval) {
501  h = CPyCppyy_PyLong_AsHash_t(hashval);
502  Py_DECREF(hashval);
503  }
504  return h;
505  }
506  Py_DECREF(hashcls);
507  }
508 
509 // if not valid, simply reset the hash function so as to not kill performance
510  ((PyTypeObject*)Py_TYPE(self))->tp_hash = PyBaseObject_Type.tp_hash;
511  return PyBaseObject_Type.tp_hash((PyObject*)self);
512 }
513 
514 //----------------------------------------------------------------------------
515 static PyObject* op_str_internal(PyObject* pyobj, PyObject* lshift, bool isBound)
516 {
517  static Cppyy::TCppScope_t sOStringStreamID = Cppyy::GetScope("std::ostringstream");
518  std::ostringstream s;
519  PyObject* pys = BindCppObjectNoCast(&s, sOStringStreamID);
520  PyObject* res;
521  if (isBound) res = PyObject_CallFunctionObjArgs(lshift, pys, NULL);
522  else res = PyObject_CallFunctionObjArgs(lshift, pys, pyobj, NULL);
523  Py_DECREF(pys);
524  Py_DECREF(lshift);
525  if (res) {
526  Py_DECREF(res);
527  return CPyCppyy_PyText_FromString(s.str().c_str());
528  }
529  PyErr_Clear();
530  return nullptr;
531 }
532 
534 {
535 #ifndef _WIN64
536 // Forward to C++ insertion operator if available, otherwise forward to repr.
537  PyObject* result = nullptr;
538  PyObject* pyobj = (PyObject*)self;
539  PyObject* lshift = PyObject_GetAttr(pyobj, PyStrings::gLShift);
540  if (lshift) result = op_str_internal(pyobj, lshift, true);
541 
542  if (!result) {
543  PyErr_Clear();
544  PyObject* pyclass = (PyObject*)Py_TYPE(pyobj);
545  lshift = PyObject_GetAttr(pyclass, PyStrings::gLShiftC);
546  if (!lshift) {
547  PyErr_Clear();
548  // attempt lazy install of global operator<<(ostream&)
549  std::string rcname = Utility::ClassName(pyobj);
551  PyCallable* pyfunc = Utility::FindBinaryOperator("std::ostream", rcname, "<<", rnsID);
552  if (pyfunc) {
553  Utility::AddToClass(pyclass, "__lshiftc__", pyfunc);
554  lshift = PyObject_GetAttr(pyclass, PyStrings::gLShiftC);
555  } else
556  PyType_Type.tp_setattro(pyclass, PyStrings::gLShiftC, Py_None);
557  } else if (lshift == Py_None) {
558  Py_DECREF(lshift);
559  lshift = nullptr;
560  }
561  if (lshift) result = op_str_internal(pyobj, lshift, false);
562  }
563 
564  if (result)
565  return result;
566 #endif //!_WIN64
567 
568  return op_repr(self);
569 }
570 
571 //-----------------------------------------------------------------------------
572 static PyObject* op_getownership(CPPInstance* pyobj, void*)
573 {
574  return PyBool_FromLong((long)(pyobj->fFlags & CPPInstance::kIsOwner));
575 }
576 
577 //-----------------------------------------------------------------------------
578 static int op_setownership(CPPInstance* pyobj, PyObject* value, void*)
579 {
580 // Set the ownership (True is python-owns) for the given object.
581  long shouldown = PyLong_AsLong(value);
582  if (shouldown == -1 && PyErr_Occurred()) {
583  PyErr_SetString(PyExc_ValueError, "__python_owns__ should be either True or False");
584  return -1;
585  }
586 
587  (bool)shouldown ? pyobj->PythonOwns() : pyobj->CppOwns();
588 
589  return 0;
590 }
591 
592 
593 //-----------------------------------------------------------------------------
594 static PyGetSetDef op_getset[] = {
595  {(char*)"__python_owns__", (getter)op_getownership, (setter)op_setownership,
596  (char*)"If true, python manages the life time of this object", nullptr},
597  {(char*)nullptr, nullptr, nullptr, nullptr, nullptr}
598 };
599 
600 
601 //= CPyCppyy type number stubs to allow dynamic overrides =====================
602 #define CPYCPPYY_STUB_BODY(name, op) \
603  if (!meth) { \
604  PyErr_Clear(); \
605  PyCallable* pyfunc = Utility::FindBinaryOperator(left, right, #op); \
606  if (pyfunc) meth = (PyObject*)CPPOverload_New(#name, pyfunc); \
607  else { \
608  PyErr_SetString(PyExc_NotImplementedError, ""); \
609  return nullptr; \
610  } \
611  } \
612  PyObject* res = PyObject_CallFunctionObjArgs(meth, cppobj, other, nullptr);\
613  if (!res) { \
614  /* try again, in case there is a better overload out there */ \
615  PyErr_Clear(); \
616  PyCallable* pyfunc = Utility::FindBinaryOperator(left, right, #op); \
617  if (pyfunc) ((CPPOverload*&)meth)->AdoptMethod(pyfunc); \
618  else { \
619  PyErr_SetString(PyExc_NotImplementedError, ""); \
620  return nullptr; \
621  } \
622  /* use same overload with newly added function */ \
623  res = PyObject_CallFunctionObjArgs(meth, cppobj, other, nullptr); \
624  } \
625  return res;
626 
627 
628 #define CPYCPPYY_OPERATOR_STUB(name, op, ometh) \
629 static PyObject* op_##name##_stub(PyObject* left, PyObject* right) \
630 { \
631 /* placeholder to lazily install and forward to 'ometh' if available */ \
632  CPPClass* klass = (CPPClass*)Py_TYPE(left); \
633  if (!klass->fOperators) klass->fOperators = new Utility::PyOperators{}; \
634  PyObject*& meth = ometh; \
635  PyObject *cppobj = left, *other = right; \
636  CPYCPPYY_STUB_BODY(name, op) \
637 }
638 
639 #define CPYCPPYY_ASSOCIATIVE_OPERATOR_STUB(name, op, lmeth, rmeth) \
640 static PyObject* op_##name##_stub(PyObject* left, PyObject* right) \
641 { \
642 /* placeholder to lazily install and forward do '(l/r)meth' if available */ \
643  CPPClass* klass; PyObject** pmeth; \
644  PyObject *cppobj, *other; \
645  if (CPPInstance_Check(left)) { \
646  klass = (CPPClass*)Py_TYPE(left); \
647  if (!klass->fOperators) klass->fOperators = new Utility::PyOperators{};\
648  pmeth = &lmeth; cppobj = left; other = right; \
649  } else if (CPPInstance_Check(right)) { \
650  klass = (CPPClass*)Py_TYPE(right); \
651  if (!klass->fOperators) klass->fOperators = new Utility::PyOperators{};\
652  pmeth = &rmeth; cppobj = right; other = left; \
653  } else { \
654  PyErr_SetString(PyExc_NotImplementedError, ""); \
655  return nullptr; \
656  } \
657  PyObject*& meth = *pmeth; \
658  CPYCPPYY_STUB_BODY(name, op) \
659 }
660 
661 #define CPYCPPYY_UNARY_OPERATOR(name, op, label) \
662 static PyObject* op_##name##_stub(PyObject* pyobj) \
663 { \
664 /* placeholder to lazily install unary operators */ \
665  PyCallable* pyfunc = Utility::FindUnaryOperator((PyObject*)Py_TYPE(pyobj), #op);\
666  if (pyfunc && Utility::AddToClass((PyObject*)Py_TYPE(pyobj), #label, pyfunc))\
667  return PyObject_CallMethod(pyobj, (char*)#label, nullptr); \
668  PyErr_SetString(PyExc_NotImplementedError, ""); \
669  return nullptr; \
670 }
671 
672 CPYCPPYY_ASSOCIATIVE_OPERATOR_STUB(add, +, klass->fOperators->fLAdd, klass->fOperators->fRAdd)
673 CPYCPPYY_OPERATOR_STUB( sub, -, klass->fOperators->fSub)
674 CPYCPPYY_ASSOCIATIVE_OPERATOR_STUB(mul, *, klass->fOperators->fLMul, klass->fOperators->fRMul)
675 CPYCPPYY_OPERATOR_STUB( div, /, klass->fOperators->fDiv)
676 CPYCPPYY_UNARY_OPERATOR(neg, -, __neg__)
677 CPYCPPYY_UNARY_OPERATOR(pos, +, __pos__)
678 CPYCPPYY_UNARY_OPERATOR(invert, ~, __invert__)
679 
680 //-----------------------------------------------------------------------------
681 static PyNumberMethods op_as_number = {
682  (binaryfunc)op_add_stub, // nb_add
683  (binaryfunc)op_sub_stub, // nb_subtract
684  (binaryfunc)op_mul_stub, // nb_multiply
685 #if PY_VERSION_HEX < 0x03000000
686  (binaryfunc)op_div_stub, // nb_divide
687 #endif
688  0, // nb_remainder
689  0, // nb_divmod
690  0, // nb_power
691  (unaryfunc)op_neg_stub, // nb_negative
692  (unaryfunc)op_pos_stub, // nb_positive
693  0, // nb_absolute
694  (inquiry)op_nonzero, // nb_bool (nb_nonzero in p2)
695  (unaryfunc)op_invert_stub, // nb_invert
696  0, // nb_lshift
697  0, // nb_rshift
698  0, // nb_and
699  0, // nb_xor
700  0, // nb_or
701 #if PY_VERSION_HEX < 0x03000000
702  0, // nb_coerce
703 #endif
704  0, // nb_int
705  0, // nb_long (nb_reserved in p3)
706  0, // nb_float
707 #if PY_VERSION_HEX < 0x03000000
708  0, // nb_oct
709  0, // nb_hex
710 #endif
711  0, // nb_inplace_add
712  0, // nb_inplace_subtract
713  0, // nb_inplace_multiply
714 #if PY_VERSION_HEX < 0x03000000
715  0, // nb_inplace_divide
716 #endif
717  0, // nb_inplace_remainder
718  0, // nb_inplace_power
719  0, // nb_inplace_lshift
720  0, // nb_inplace_rshift
721  0, // nb_inplace_and
722  0, // nb_inplace_xor
723  0 // nb_inplace_or
724 #if PY_VERSION_HEX >= 0x02020000
725  , 0 // nb_floor_divide
726 #if PY_VERSION_HEX < 0x03000000
727  , 0 // nb_true_divide
728 #else
729  , (binaryfunc)op_div_stub // nb_true_divide
730 #endif
731  , 0 // nb_inplace_floor_divide
732  , 0 // nb_inplace_true_divide
733 #endif
734 #if PY_VERSION_HEX >= 0x02050000
735  , 0 // nb_index
736 #endif
737 #if PY_VERSION_HEX >= 0x03050000
738  , 0 // nb_matrix_multiply
739  , 0 // nb_inplace_matrix_multiply
740 #endif
741 };
742 
743 
744 //= CPyCppyy object proxy type ===============================================
745 PyTypeObject CPPInstance_Type = {
747  (char*)"cppyy.CPPInstance", // tp_name
748  sizeof(CPPInstance), // tp_basicsize
749  0, // tp_itemsize
750  (destructor)op_dealloc, // tp_dealloc
751  0, // tp_print
752  0, // tp_getattr
753  0, // tp_setattr
754  0, // tp_compare
755  (reprfunc)op_repr, // tp_repr
756  &op_as_number, // tp_as_number
757  0, // tp_as_sequence
758  0, // tp_as_mapping
759  (hashfunc)op_hash, // tp_hash
760  0, // tp_call
761  (reprfunc)op_str, // tp_str
762  0, // tp_getattro
763  0, // tp_setattro
764  0, // tp_as_buffer
765  Py_TPFLAGS_DEFAULT |
766  Py_TPFLAGS_BASETYPE |
767  Py_TPFLAGS_HAVE_GC |
768  Py_TPFLAGS_CHECKTYPES, // tp_flags
769  (char*)"cppyy object proxy (internal)", // tp_doc
770  0, // tp_traverse
771  (inquiry)op_clear, // tp_clear
772  (richcmpfunc)op_richcompare, // tp_richcompare
773  0, // tp_weaklistoffset
774  0, // tp_iter
775  0, // tp_iternext
776  op_methods, // tp_methods
777  0, // tp_members
778  op_getset, // tp_getset
779  0, // tp_base
780  0, // tp_dict
781  0, // tp_descr_get
782  0, // tp_descr_set
783  0, // tp_dictoffset
784  0, // tp_init
785  0, // tp_alloc
786  (newfunc)op_new, // tp_new
787  0, // tp_free
788  0, // tp_is_gc
789  0, // tp_bases
790  0, // tp_mro
791  0, // tp_cache
792  0, // tp_subclasses
793  0 // tp_weaklist
794 #if PY_VERSION_HEX >= 0x02030000
795  , 0 // tp_del
796 #endif
797 #if PY_VERSION_HEX >= 0x02060000
798  , 0 // tp_version_tag
799 #endif
800 #if PY_VERSION_HEX >= 0x03040000
801  , 0 // tp_finalize
802 #endif
803 };
804 
805 } // namespace CPyCppyy
CPyCppyy
Set of helper functions that are invoked from the pythonizors, on the Python side.
Definition: TPyClassGenerator.cxx:31
CPYCPPYY_OPERATOR_STUB
#define CPYCPPYY_OPERATOR_STUB(name, op, ometh)
Definition: CPPInstance.cxx:628
CPyCppyy::CPPScope_Type
PyTypeObject CPPScope_Type
Definition: CPPScope.cxx:614
CPyCppyy::CPPScope::fOperators
Utility::PyOperators * fOperators
Definition: CPPScope.h:56
CPyCppyy::CPPInstance::kIsRegulated
@ kIsRegulated
Definition: CPPInstance.h:40
CPyCppyy::Utility::ClassName
std::string ClassName(PyObject *pyobj)
Definition: Utility.cxx:825
CPyCppyy::CPPInstance::kNoWrapConv
@ kNoWrapConv
Definition: CPPInstance.h:30
CPyCppyy::op_getset
static PyGetSetDef op_getset[]
Definition: CPPInstance.cxx:594
CPPInstance.h
CPyCppyy::CPPOverload_Type
PyTypeObject CPPOverload_Type
Definition: CPPOverload.cxx:863
CPyCppyy::PyStrings::gDict
PyObject * gDict
Definition: PyStrings.cxx:14
CPyCppyy::CPPInstance::SetDispatchPtr
void SetDispatchPtr(void *)
Definition: CPPInstance.cxx:192
PyVarObject_HEAD_INIT
#define PyVarObject_HEAD_INIT(type, size)
Definition: CPyCppyy.h:207
Py_hash_t
long Py_hash_t
Definition: CPyCppyy.h:133
CPyCppyy::CPPInstance::CreateExtension
void CreateExtension()
Definition: CPPInstance.cxx:79
CPyCppyy::PyStrings::gModule
R__EXTERN PyObject * gModule
Definition: TPython.cxx:104
CPyCppyy::TypeManip::extract_namespace
std::string extract_namespace(const std::string &name)
Definition: TypeManip.cxx:159
PyObject
_object PyObject
Definition: PyMethodBase.h:42
CPyCppyy::CPPInstance::fFlags
int fFlags
Definition: CPPInstance.h:45
CPyCppyy::CI_DatamemberCache_t
std::vector< std::pair< ptrdiff_t, PyObject * > > CI_DatamemberCache_t
Definition: CPPInstance.h:24
CPyCppyy::op_hash
static Py_hash_t op_hash(CPPInstance *self)
Definition: CPPInstance.cxx:472
CPyCppyy::PyStrings::gLen
PyObject * gLen
Definition: PyStrings.cxx:22
Utility.h
CPyCppyy::CPPInstance::GetObjectRaw
void *& GetObjectRaw()
Definition: CPPInstance.h:60
Cppyy::TCppScope_t
size_t TCppScope_t
Definition: cpp_cppyy.h:18
CPyCppyy::CPPInstance::kIsExtended
@ kIsExtended
Definition: CPPInstance.h:32
CPyCppyy::CPPInstance
Definition: CPPInstance.h:26
TGeant4Unit::s
static constexpr double s
Definition: TGeant4SystemOfUnits.h:162
CPyCppyy::PyCallable
Definition: PyCallable.h:12
CPyCppyy::op_str_internal
static PyObject * op_str_internal(PyObject *pyobj, PyObject *lshift, bool isBound)
Definition: CPPInstance.cxx:515
TGeant4Unit::pc
static constexpr double pc
Definition: TGeant4SystemOfUnits.h:130
CPyCppyy::op_dispatch
static PyObject * op_dispatch(PyObject *self, PyObject *args, PyObject *)
Definition: CPPInstance.cxx:264
CPyCppyy::Utility::PyOperators::fNe
PyObject * fNe
Definition: Utility.h:62
CPyCppyy::Utility::PyOperators
Definition: Utility.h:56
CPyCppyy::CPPInstance::GetDatamemberCache
CI_DatamemberCache_t & GetDatamemberCache()
Definition: CPPInstance.cxx:184
CPyCppyy::CPPOverload_New
CPPOverload * CPPOverload_New(const std::string &name, std::vector< PyCallable * > &methods)
Definition: CPPOverload.h:91
CPyCppyy_PyText_FromString
#define CPyCppyy_PyText_FromString
Definition: CPyCppyy.h:102
MemoryRegulator.h
CPyCppyy::CPPInstance::PythonOwns
void PythonOwns()
Definition: CPPInstance.cxx:152
Py_RETURN_FALSE
#define Py_RETURN_FALSE
Definition: CPyCppyy.h:289
CPyCppyy::CPPInstance::GetExtendedObject
void * GetExtendedObject()
Definition: CPPInstance.cxx:88
bool
CPyCppyy::eqneq_binop
static PyObject * eqneq_binop(CPPClass *klass, PyObject *self, PyObject *obj, int op)
Definition: CPPInstance.cxx:347
CPyCppyy::Utility::PyOperators::fEq
PyObject * fEq
Definition: Utility.h:61
CPyCppyy::DispatchPtr
Definition: DispatchPtr.h:23
Cppyy::GetScope
RPY_EXPORTED TCppScope_t GetScope(const std::string &scope_name)
Definition: clingwrapper.cxx:497
CPyCppyy::CPPInstance::fObject
PyObject_HEAD void * fObject
Definition: CPPInstance.h:44
CPyCppyy::op_methods
static PyMethodDef op_methods[]
Definition: CPPInstance.cxx:305
DATA_CACHE
#define DATA_CACHE(pyobj)
Definition: CPPInstance.cxx:77
CPyCppyy::PyStrings::gLShiftC
PyObject * gLShiftC
Definition: PyStrings.cxx:39
ProxyWrappers.h
CPyCppyy::op_str
static PyObject * op_str(CPPInstance *self)
Definition: CPPInstance.cxx:533
h
#define h(i)
Definition: RSha256.hxx:106
CPyCppyy::CPyCppyy_PyLong_AsHash_t
static Py_hash_t CPyCppyy_PyLong_AsHash_t(PyObject *obj)
Definition: CPPInstance.cxx:463
Cppyy::CallR
RPY_EXPORTED void * CallR(TCppMethod_t method, TCppObject_t self, size_t nargs, void *args)
Definition: clingwrapper.cxx:843
CPyCppyy::CreateScopeProxy
PyObject * CreateScopeProxy(Cppyy::TCppScope_t)
Definition: ProxyWrappers.cxx:490
Py_RETURN_TRUE
#define Py_RETURN_TRUE
Definition: CPyCppyy.h:285
CPyCppyy::PyStrings::gLShift
PyObject * gLShift
Definition: PyStrings.cxx:38
CPyCppyy::op_dealloc
static void op_dealloc(CPPInstance *pyobj)
Definition: CPPInstance.cxx:327
Cppyy::CallDestructor
RPY_EXPORTED void CallDestructor(TCppType_t type, TCppObject_t self)
Definition: clingwrapper.cxx:876
CPyCppyy::CPPSmartClass
Definition: CPPScope.h:65
CPYCPPYY_UNARY_OPERATOR
#define CPYCPPYY_UNARY_OPERATOR(name, op, label)
Definition: CPPInstance.cxx:661
CPyCppyy::op_nonzero
static int op_nonzero(CPPInstance *self)
Definition: CPPInstance.cxx:228
CPyCppyy::CPPInstance::kIsOwner
@ kIsOwner
Definition: CPPInstance.h:31
CPyCppyy::op_as_number
static PyNumberMethods op_as_number
Definition: CPPInstance.cxx:681
CPyCppyy.h
CPyCppyy::op_new
static CPPInstance * op_new(PyTypeObject *subtype, PyObject *, PyObject *)
Definition: CPPInstance.cxx:316
DispatchPtr.h
CPyCppyy::op_get_smartptr
static PyObject * op_get_smartptr(CPPInstance *self)
Definition: CPPInstance.cxx:293
CPYCPPYY_ASSOCIATIVE_OPERATOR_STUB
#define CPYCPPYY_ASSOCIATIVE_OPERATOR_STUB(name, op, lmeth, rmeth)
Definition: CPPInstance.cxx:639
Cppyy::Destruct
RPY_EXPORTED void Destruct(TCppType_t type, TCppObject_t instance)
Definition: clingwrapper.cxx:680
Cppyy::GetScopedFinalName
RPY_EXPORTED std::string GetScopedFinalName(TCppType_t type)
Definition: clingwrapper.cxx:1161
CPyCppyy_PyText_FromFormat
#define CPyCppyy_PyText_FromFormat
Definition: CPyCppyy.h:101
CPyCppyy_PyText_AsString
#define CPyCppyy_PyText_AsString
Definition: CPyCppyy.h:97
CPyCppyy_PyText_Type
#define CPyCppyy_PyText_Type
Definition: CPyCppyy.h:115
CPyCppyy::CPPScope
Definition: CPPScope.h:37
Cppyy::TCppType_t
TCppScope_t TCppType_t
Definition: cpp_cppyy.h:19
TypeManip.h
Cppyy::Deallocate
RPY_EXPORTED void Deallocate(TCppType_t type, TCppObject_t instance)
Definition: clingwrapper.cxx:668
CPyCppyy::CPPInstance::Copy
CPPInstance * Copy(void *cppinst)
Definition: CPPInstance.cxx:100
Cppyy::GetFinalName
RPY_EXPORTED std::string GetFinalName(TCppType_t type)
Definition: clingwrapper.cxx:1148
CPyCppyy::op_destruct
static PyObject * op_destruct(CPPInstance *self)
Definition: CPPInstance.cxx:253
CPPScope.h
CPyCppyy::MemoryRegulator::UnregisterPyObject
static bool UnregisterPyObject(CPPInstance *pyobj, PyObject *pyclass)
Definition: MemoryRegulator.cxx:200
CPyCppyy::CPPInstance::kIsValue
@ kIsValue
Definition: CPPInstance.h:35
DISPATCHPTR
#define DISPATCHPTR(pyobj)
Definition: CPPInstance.cxx:76
fFlags
unsigned int fFlags
Definition: DeclareExecutors.h:98
CPPOverload.h
PyBool_FromLong
#define PyBool_FromLong
Definition: CPyCppyy.h:264
CPyCppyy::Utility::AddToClass
bool AddToClass(PyObject *pyclass, const char *label, PyCFunction cfunc, int flags=METH_VARARGS)
Definition: Utility.cxx:169
CPyCppyy::op_setownership
static int op_setownership(CPPInstance *pyobj, PyObject *value, void *)
Definition: CPPInstance.cxx:578
CPyCppyy::op_clear
static int op_clear(CPPInstance *pyobj)
Definition: CPPInstance.cxx:336
CPyCppyy::MemoryRegulator::RegisterPyObject
static bool RegisterPyObject(CPPInstance *pyobj, void *cppobj)
Definition: MemoryRegulator.cxx:173
CPyCppyy::CPPOverload
Definition: CPPOverload.h:36
CPyCppyy::CPPInstance_Type
PyTypeObject CPPInstance_Type
Definition: CPPInstance.cxx:745
CPyCppyy::CPPInstance::SetSmart
void SetSmart(PyObject *smart_type)
Definition: CPPInstance.cxx:168
CPyCppyy::op_repr
static PyObject * op_repr(CPPInstance *self)
Definition: CPPInstance.cxx:434
CPyCppyy::CPPInstance::GetSmartIsA
Cppyy::TCppType_t GetSmartIsA() const
Definition: CPPInstance.cxx:177
CPyCppyy::CPPInstance::ObjectIsA
Cppyy::TCppType_t ObjectIsA(bool check_smart=true) const
Definition: CPPInstance.h:106
CPyCppyy::CPPInstance::CppOwns
void CppOwns()
Definition: CPPInstance.cxx:160
Py_TYPE
#define Py_TYPE(ob)
Definition: CPyCppyy.h:209
CPyCppyy::CPPInstance::kIsReference
@ kIsReference
Definition: CPPInstance.h:33
CPyCppyy::op_getownership
static PyObject * op_getownership(CPPInstance *pyobj, void *)
Definition: CPPInstance.cxx:572
CPyCppyy::CPPInstance_Check
bool CPPInstance_Check(T *object)
Definition: CPPInstance.h:118
SMART_TYPE
#define SMART_TYPE(pyobj)
Definition: CPPInstance.cxx:75
CPyCppyy::Utility::PyOperators::fHash
PyObject * fHash
Definition: Utility.h:67
EXT_OBJECT
#define EXT_OBJECT(pyobj)
Definition: CPPInstance.cxx:73
CPyCppyy::BindCppObjectNoCast
PyObject * BindCppObjectNoCast(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
Definition: ProxyWrappers.cxx:802
CPyCppyy::Utility::FindBinaryOperator
PyCallable * FindBinaryOperator(PyObject *left, PyObject *right, const char *op, Cppyy::TCppScope_t scope=0)
Definition: Utility.cxx:280
CPyCppyy::op_dealloc_nofree
void op_dealloc_nofree(CPPInstance *)
Definition: CPPInstance.cxx:201
SMART_CLS
#define SMART_CLS(pyobj)
Definition: CPPInstance.cxx:74
PyStrings.h
Py_RETURN_NONE
#define Py_RETURN_NONE
Definition: CPyCppyy.h:281
CPyCppyy::op_richcompare
static PyObject * op_richcompare(CPPInstance *self, PyObject *other, int op)
Definition: CPPInstance.cxx:400