Logo ROOT  
Reference Guide
CPPOverload.cxx
Go to the documentation of this file.
1 // Bindings
2 #include "CPyCppyy.h"
3 #include "structmember.h" // from Python
4 #if PY_VERSION_HEX >= 0x02050000
5 #include "code.h" // from Python
6 #else
7 #include "compile.h" // from Python
8 #endif
9 #ifndef CO_NOFREE
10 // python2.2 does not have CO_NOFREE defined
11 #define CO_NOFREE 0x0040
12 #endif
13 #include "CPPOverload.h"
14 #include "CPPInstance.h"
15 #include "CallContext.h"
16 #include "PyStrings.h"
17 #include "Utility.h"
18 
19 // Standard
20 #include <algorithm>
21 #include <sstream>
22 #include <vector>
23 
24 
25 namespace CPyCppyy {
26 
27 namespace {
28 
29 // from CPython's instancemethod: Free list for method objects to safe malloc/free overhead
30 // The im_self element is used to chain the elements.
31 static CPPOverload* free_list;
32 static int numfree = 0;
33 #ifndef CPPOverload_MAXFREELIST
34 #define CPPOverload_MAXFREELIST 32
35 #endif
36 
37 
38 // TODO: only used here, but may be better off integrated with Pythonize.cxx callbacks
39 class TPythonCallback : public PyCallable {
40 public:
42 
43  TPythonCallback(PyObject* callable) : fCallable(nullptr)
44  {
45  if (!PyCallable_Check(callable)) {
46  PyErr_SetString(PyExc_TypeError, "parameter must be callable");
47  return;
48  }
49  Py_INCREF(callable);
50  fCallable = callable;
51  }
52 
53  virtual ~TPythonCallback() {
54  Py_DECREF(fCallable);
55  fCallable = nullptr;
56  }
57 
58  virtual PyObject* GetSignature(bool /*show_formalargs*/ = true) {
59  return CPyCppyy_PyText_FromString("*args, **kwargs");
60  }
61  virtual PyObject* GetPrototype(bool /*show_formalargs*/ = true) {
62  return CPyCppyy_PyText_FromString("<callback>");
63  }
64  virtual PyObject* GetDocString() {
65  if (PyObject_HasAttrString(fCallable, "__doc__")) {
66  return PyObject_GetAttrString(fCallable, "__doc__");
67  } else {
68  return GetPrototype();
69  }
70  }
71 
72  virtual int GetPriority() { return 100; };
73  virtual bool IsGreedy() { return false; };
74 
75  virtual int GetMaxArgs() { return 100; };
76  virtual PyObject* GetCoVarNames() { // TODO: pick these up from the callable
78  }
79  virtual PyObject* GetArgDefault(int /* iarg */) { // TODO: pick these up from the callable
81  }
82 
83  virtual PyObject* GetScopeProxy() { // should this be the module ??
85  }
86 
88  return (Cppyy::TCppFuncAddr_t)nullptr;
89  }
90 
91  virtual PyCallable* Clone() { return new TPythonCallback(*this); }
92 
93  virtual PyObject* Call(
94  CPPInstance*& self, PyObject* args, PyObject* kwds, CallContext* /* ctxt = 0 */) {
95 
96  PyObject* newArgs = nullptr;
97  if (self) {
98  Py_ssize_t nargs = PyTuple_Size(args);
99  newArgs = PyTuple_New(nargs+1);
100  Py_INCREF(self);
101  PyTuple_SET_ITEM(newArgs, 0, (PyObject*)self);
102  for (Py_ssize_t iarg = 0; iarg < nargs; ++iarg) {
103  PyObject* pyarg = PyTuple_GET_ITEM(args, iarg);
104  Py_INCREF(pyarg);
105  PyTuple_SET_ITEM(newArgs, iarg+1, pyarg);
106  }
107  } else {
108  Py_INCREF(args);
109  newArgs = args;
110  }
111  return PyObject_Call(fCallable, newArgs, kwds);
112  }
113 };
114 
115 // helper to test whether a method is used in a pseudo-function modus
116 static inline bool IsPseudoFunc(CPPOverload* pymeth)
117 {
118  return pymeth->fMethodInfo->fFlags & CallContext::kIsPseudoFunc;
119 }
120 
121 // helper to sort on method priority
122 static int PriorityCmp(PyCallable* left, PyCallable* right)
123 {
124  return left->GetPriority() > right->GetPriority();
125 }
126 
127 // return helper
128 static inline void ResetCallState(CPPInstance*& selfnew, CPPInstance* selfold, bool clear)
129 {
130  if (selfnew != selfold) {
131  Py_XDECREF(selfnew);
132  selfnew = selfold;
133  }
134 
135  if (clear)
136  PyErr_Clear();
137 }
138 
139 // helper to factor out return logic of mp_call
140 static inline PyObject* HandleReturn(
141  CPPOverload* pymeth, CPPInstance* oldSelf, PyObject* result)
142 {
143 // special case for python exceptions, propagated through C++ layer
144  int ll_action = 0;
145  if (result) {
146 
147  // if this method creates new objects, always take ownership
148  if (IsCreator(pymeth->fMethodInfo->fFlags)) {
149 
150  // either be a constructor with a fresh object proxy self ...
151  if (IsConstructor(pymeth->fMethodInfo->fFlags)) {
152  if (pymeth->fSelf)
153  pymeth->fSelf->PythonOwns();
154  }
155 
156  // ... or be a method with an object proxy return value
157  else if (CPPInstance_Check(result))
158  ((CPPInstance*)result)->PythonOwns();
159  }
160 
161  // if this new object falls inside self, make sure its lifetime is proper
162  if (pymeth->fMethodInfo->fFlags & CallContext::kSetLifeline)
163  ll_action = 1;
164  else if (!(pymeth->fMethodInfo->fFlags & CallContext::kNeverLifeLine) && \
165  CPPInstance_Check(pymeth->fSelf) && CPPInstance_Check(result)) {
166  // if self was a by-value return and result is not, pro-actively protect result;
167  // else if the return value falls within the memory of 'this', force a lifeline
168  CPPInstance* cppself = (CPPInstance*)pymeth->fSelf;
169  CPPInstance* cppres = (CPPInstance*)result;
170  if (!(cppres->fFlags & CPPInstance::kIsValue)) { // no need if the result is a full copy
171  if (cppself->fFlags & CPPInstance::kIsValue)
172  ll_action = 2;
173  else if (cppself->fFlags & CPPInstance::kHasLifeline)
174  ll_action = 3;
175  else {
176  ptrdiff_t offset = (ptrdiff_t)cppres->GetObject() - (ptrdiff_t)cppself->GetObject();
177  if (0 <= offset && offset < (ptrdiff_t)Cppyy::SizeOf(cppself->ObjectIsA()))
178  ll_action = 4;
179  }
180  }
181  if (ll_action) cppres->fFlags |= CPPInstance::kHasLifeline; // for chaining
182  }
183 
184  if (!ll_action)
185  pymeth->fMethodInfo->fFlags |= CallContext::kNeverLifeLine; // assume invariant semantics
186  }
187 
188  if (ll_action) {
189  if (PyObject_SetAttr(result, PyStrings::gLifeLine, (PyObject*)pymeth->fSelf) == -1)
190  PyErr_Clear(); // ignored
191  if (ll_action == 1 /* directly set */ && CPPInstance_Check(result))
192  ((CPPInstance*)result)->fFlags |= CPPInstance::kHasLifeline; // for chaining
193  else
194  pymeth->fMethodInfo->fFlags |= CallContext::kSetLifeline; // for next time
195  }
196 
197 // reset self as necessary to allow re-use of the CPPOverload
198  ResetCallState(pymeth->fSelf, oldSelf, false);
199 
200  return result;
201 }
202 
203 
204 //= CPyCppyy method proxy object behaviour ===================================
205 static PyObject* mp_name(CPPOverload* pymeth, void*)
206 {
207  return CPyCppyy_PyText_FromString(pymeth->GetName().c_str());
208 }
209 
210 //----------------------------------------------------------------------------
211 static PyObject* mp_module(CPPOverload* /* pymeth */, void*)
212 {
213  Py_INCREF(PyStrings::gThisModule);
214  return PyStrings::gThisModule;
215 }
216 
217 //----------------------------------------------------------------------------
218 static PyObject* mp_doc(CPPOverload* pymeth, void*)
219 {
220 // Build python document string ('__doc__') from all C++-side overloads.
221  CPPOverload::Methods_t& methods = pymeth->fMethodInfo->fMethods;
222 
223 // collect doc strings
224  CPPOverload::Methods_t::size_type nMethods = methods.size();
225  if (nMethods == 0) // from template proxy with no instantiations
226  return nullptr;
227  PyObject* doc = methods[0]->GetDocString();
228 
229 // simple case
230  if (nMethods == 1)
231  return doc;
232 
233 // overloaded method
235  for (CPPOverload::Methods_t::size_type i = 1; i < nMethods; ++i) {
237  CPyCppyy_PyText_AppendAndDel(&doc, methods[i]->GetDocString());
238  }
239  Py_DECREF(separator);
240 
241  return doc;
242 }
243 
244 //----------------------------------------------------------------------------
245 static PyObject* mp_meth_func(CPPOverload* pymeth, void*)
246 {
247 // Create a new method proxy to be returned.
248  CPPOverload* newPyMeth = (CPPOverload*)CPPOverload_Type.tp_alloc(&CPPOverload_Type, 0);
249 
250 // method info is shared, as it contains the collected overload knowledge
251  *pymeth->fMethodInfo->fRefCount += 1;
252  newPyMeth->fMethodInfo = pymeth->fMethodInfo;
253 
254 // new method is unbound, track whether this proxy is used in the capacity of a
255 // method or a function (which normally is a CPPFunction)
256  newPyMeth->fMethodInfo->fFlags |= CallContext::kIsPseudoFunc;
257 
258  return (PyObject*)newPyMeth;
259 }
260 
261 //----------------------------------------------------------------------------
262 static PyObject* mp_meth_self(CPPOverload* pymeth, void*)
263 {
264 // Return the bound self, if any; in case of pseudo-function role, pretend
265 // that the data member im_self does not exist.
266  if (IsPseudoFunc(pymeth)) {
267  PyErr_Format(PyExc_AttributeError,
268  "function %s has no attribute \'im_self\'", pymeth->fMethodInfo->fName.c_str());
269  return nullptr;
270  } else if (pymeth->fSelf != 0) {
271  Py_INCREF((PyObject*)pymeth->fSelf);
272  return (PyObject*)pymeth->fSelf;
273  }
274 
276 }
277 
278 //----------------------------------------------------------------------------
279 static PyObject* mp_meth_class(CPPOverload* pymeth, void*)
280 {
281 // Return scoping class; in case of pseudo-function role, pretend that there
282 // is no encompassing class (i.e. global scope).
283  if (!IsPseudoFunc(pymeth) && pymeth->fMethodInfo->fMethods.size()) {
284  PyObject* pyclass = pymeth->fMethodInfo->fMethods[0]->GetScopeProxy();
285  if (!pyclass)
286  PyErr_Format(PyExc_AttributeError,
287  "function %s has no attribute \'im_class\'", pymeth->fMethodInfo->fName.c_str());
288  return pyclass;
289  }
290 
292 }
293 
294 //----------------------------------------------------------------------------
295 static PyObject* mp_func_closure(CPPOverload* /* pymeth */, void*)
296 {
297 // Stub only, to fill out the python function interface.
299 }
300 
301 //----------------------------------------------------------------------------
302 static PyObject* mp_func_code(CPPOverload* pymeth, void*)
303 {
304 // Code details are used in module inspect to fill out interactive help()
305 #if PY_VERSION_HEX < 0x03000000
306  CPPOverload::Methods_t& methods = pymeth->fMethodInfo->fMethods;
307 
308 // collect arguments only if there is just 1 overload, otherwise put in a
309 // fake *args (see below for co_varnames)
310  PyObject* co_varnames = methods.size() == 1 ? methods[0]->GetCoVarNames() : nullptr;
311  if (!co_varnames) {
312  // TODO: static methods need no 'self' (but is harmless otherwise)
313  co_varnames = PyTuple_New(1 /* self */ + 1 /* fake */);
314  PyTuple_SET_ITEM(co_varnames, 0, CPyCppyy_PyText_FromString("self"));
315  PyTuple_SET_ITEM(co_varnames, 1, CPyCppyy_PyText_FromString("*args"));
316  }
317 
318  int co_argcount = (int)PyTuple_Size(co_varnames);
319 
320 // for now, code object representing the statement 'pass'
321  PyObject* co_code = PyString_FromStringAndSize("d\x00\x00S", 4);
322 
323 // tuples with all the const literals used in the function
324  PyObject* co_consts = PyTuple_New(0);
325  PyObject* co_names = PyTuple_New(0);
326 
327 // names, freevars, and cellvars go unused
328  PyObject* co_unused = PyTuple_New(0);
329 
330 // filename is made-up
331  PyObject* co_filename = PyString_FromString("cppyy.py");
332 
333 // name is the function name, also through __name__ on the function itself
334  PyObject* co_name = PyString_FromString(pymeth->GetName().c_str());
335 
336 // firstlineno is the line number of first function code in the containing scope
337 
338 // lnotab is a packed table that maps instruction count and line number
339  PyObject* co_lnotab = PyString_FromString("\x00\x01\x0c\x01");
340 
341  PyObject* code = (PyObject*)PyCode_New(
342  co_argcount, // argcount
343  co_argcount+1, // nlocals
344  2, // stacksize
345  CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE, // flags
346  co_code, // code
347  co_consts, // consts
348  co_names, // names
349  co_varnames, // varnames
350  co_unused, // freevars
351  co_unused, // cellvars
352  co_filename, // filename
353  co_name, // name
354  1, // firstlineno
355  co_lnotab); // lnotab
356 
357  Py_DECREF(co_lnotab);
358  Py_DECREF(co_name);
359  Py_DECREF(co_unused);
360  Py_DECREF(co_filename);
361  Py_DECREF(co_varnames);
362  Py_DECREF(co_names);
363  Py_DECREF(co_consts);
364  Py_DECREF(co_code);
365 
366  return code;
367 #else
368 // not important for functioning of most code, so not implemented for p3 for now (TODO)
369  pymeth = 0;
371 #endif
372 }
373 
374 //----------------------------------------------------------------------------
375 static PyObject* mp_func_defaults(CPPOverload* pymeth, void*)
376 {
377 // Create a tuple of default values, if there is only one method (otherwise
378 // leave undefined: this is only used by inspect for interactive help())
379  CPPOverload::Methods_t& methods = pymeth->fMethodInfo->fMethods;
380 
381  if (methods.size() != 1)
382  return PyTuple_New(0);
383 
384  int maxarg = methods[0]->GetMaxArgs();
385 
386  PyObject* defaults = PyTuple_New(maxarg);
387 
388  int itup = 0;
389  for (int iarg = 0; iarg < maxarg; ++iarg) {
390  PyObject* defvalue = methods[0]->GetArgDefault(iarg);
391  if (defvalue)
392  PyTuple_SET_ITEM(defaults, itup++, defvalue);
393  }
394  _PyTuple_Resize(&defaults, itup);
395 
396  return defaults;
397 }
398 
399 //----------------------------------------------------------------------------
400 static PyObject* mp_func_globals(CPPOverload* /* pymeth */, void*)
401 {
402 // Return this function's global dict (hard-wired to be the cppyy module); used
403 // for lookup of names from co_code indexing into co_names.
404  PyObject* pyglobal = PyModule_GetDict(PyImport_AddModule((char*)"cppyy"));
405  Py_XINCREF(pyglobal);
406  return pyglobal;
407 }
408 
409 //----------------------------------------------------------------------------
410 static inline int set_flag(CPPOverload* pymeth, PyObject* value, CallContext::ECallFlags flag, const char* name)
411 {
412 // Generic setter of a (boolean) flag.
413  if (!value) { // accept as false (delete)
414  pymeth->fMethodInfo->fFlags &= ~flag;
415  return 0;
416  }
417 
418  long istrue = PyLong_AsLong(value);
419  if (istrue == -1 && PyErr_Occurred()) {
420  PyErr_Format(PyExc_ValueError, "a boolean 1 or 0 is required for %s", name);
421  return -1;
422  }
423 
424  if (istrue)
425  pymeth->fMethodInfo->fFlags |= flag;
426  else
427  pymeth->fMethodInfo->fFlags &= ~flag;
428 
429  return 0;
430 }
431 
432 //----------------------------------------------------------------------------
433 static PyObject* mp_getcreates(CPPOverload* pymeth, void*)
434 {
435 // Get '__creates__' boolean, which determines ownership of return values.
436  return PyInt_FromLong((long)IsCreator(pymeth->fMethodInfo->fFlags));
437 }
438 
439 //----------------------------------------------------------------------------
440 static int mp_setcreates(CPPOverload* pymeth, PyObject* value, void*)
441 {
442 // Set '__creates__' boolean, which determines ownership of return values.
443  return set_flag(pymeth, value, CallContext::kIsCreator, "__creates__");
444 }
445 
446 //----------------------------------------------------------------------------
447 static PyObject* mp_getmempolicy(CPPOverload* pymeth, void*)
448 {
449 // Get '_mempolicy' enum, which determines ownership of call arguments.
450  if (pymeth->fMethodInfo->fFlags & CallContext::kUseHeuristics)
452 
453  if (pymeth->fMethodInfo->fFlags & CallContext::kUseStrict)
455 
456  return PyInt_FromLong(-1);
457 }
458 
459 //----------------------------------------------------------------------------
460 static int mp_setmempolicy(CPPOverload* pymeth, PyObject* value, void*)
461 {
462 // Set '_mempolicy' enum, which determines ownership of call arguments.
463  long mempolicy = PyLong_AsLong(value);
464  if (mempolicy == CallContext::kUseHeuristics) {
465  pymeth->fMethodInfo->fFlags |= CallContext::kUseHeuristics;
466  pymeth->fMethodInfo->fFlags &= ~CallContext::kUseStrict;
467  } else if (mempolicy == CallContext::kUseStrict) {
468  pymeth->fMethodInfo->fFlags |= CallContext::kUseStrict;
469  pymeth->fMethodInfo->fFlags &= ~CallContext::kUseHeuristics;
470  } else {
471  PyErr_SetString(PyExc_ValueError,
472  "expected kMemoryStrict or kMemoryHeuristics as value for __mempolicy__");
473  return -1;
474  }
475 
476  return 0;
477 }
478 
479 
480 //----------------------------------------------------------------------------
481 #define CPPYY_BOOLEAN_PROPERTY(name, flag, label) \
482 static PyObject* mp_get##name(CPPOverload* pymeth, void*) { \
483  if (pymeth->fMethodInfo->fFlags & flag) { \
484  Py_RETURN_TRUE; \
485  } \
486  Py_RETURN_FALSE; \
487 } \
488  \
489 static int mp_set##name(CPPOverload* pymeth, PyObject* value, void*) { \
490  return set_flag(pymeth, value, flag, label); \
491 }
492 
493 CPPYY_BOOLEAN_PROPERTY(lifeline, CallContext::kSetLifeline, "__set_lifeline__")
494 CPPYY_BOOLEAN_PROPERTY(threaded, CallContext::kReleaseGIL, "__release_gil__")
495 CPPYY_BOOLEAN_PROPERTY(useffi, CallContext::kUseFFI, "__useffi__")
496 CPPYY_BOOLEAN_PROPERTY(sig2exc, CallContext::kProtected, "__sig2exc__")
497 
498 //----------------------------------------------------------------------------
499 static PyGetSetDef mp_getset[] = {
500  {(char*)"__name__", (getter)mp_name, nullptr, nullptr, nullptr},
501  {(char*)"__module__", (getter)mp_module, nullptr, nullptr, nullptr},
502  {(char*)"__doc__", (getter)mp_doc, nullptr, nullptr, nullptr},
503 
504 // to be more python-like, where these are duplicated as well; to actually
505 // derive from the python method or function type is too memory-expensive,
506 // given that most of the members of those types would not be used
507  {(char*)"im_func", (getter)mp_meth_func, nullptr, nullptr, nullptr},
508  {(char*)"im_self", (getter)mp_meth_self, nullptr, nullptr, nullptr},
509  {(char*)"im_class", (getter)mp_meth_class, nullptr, nullptr, nullptr},
510 
511  {(char*)"func_closure", (getter)mp_func_closure, nullptr, nullptr, nullptr},
512  {(char*)"func_code", (getter)mp_func_code, nullptr, nullptr, nullptr},
513  {(char*)"func_defaults", (getter)mp_func_defaults, nullptr, nullptr, nullptr},
514  {(char*)"func_globals", (getter)mp_func_globals, nullptr, nullptr, nullptr},
515  {(char*)"func_doc", (getter)mp_doc, nullptr, nullptr, nullptr},
516  {(char*)"func_name", (getter)mp_name, nullptr, nullptr, nullptr},
517 
518  {(char*)"__creates__", (getter)mp_getcreates, (setter)mp_setcreates,
519  (char*)"For ownership rules of result: if true, objects are python-owned", nullptr},
520  {(char*)"__mempolicy__", (getter)mp_getmempolicy, (setter)mp_setmempolicy,
521  (char*)"For argument ownership rules: like global, either heuristic or strict", nullptr},
522  {(char*)"__set_lifeline__", (getter)mp_getlifeline, (setter)mp_setlifeline,
523  (char*)"If true, set a lifeline from the return value onto self", nullptr},
524  {(char*)"__release_gil__", (getter)mp_getthreaded, (setter)mp_setthreaded,
525  (char*)"If true, releases GIL on call into C++", nullptr},
526  {(char*)"__useffi__", (getter)mp_getuseffi, (setter)mp_setuseffi,
527  (char*)"not implemented", nullptr},
528  {(char*)"__sig2exc__", (getter)mp_getsig2exc, (setter)mp_setsig2exc,
529  (char*)"If true, turn signals into Python exceptions", nullptr},
530  {(char*)nullptr, nullptr, nullptr, nullptr, nullptr}
531 };
532 
533 //= CPyCppyy method proxy function behavior ==================================
534 static PyObject* mp_call(CPPOverload* pymeth, PyObject* args, PyObject* kwds)
535 {
536 // Call the appropriate overload of this method.
537 
538  CPPInstance* oldSelf = pymeth->fSelf;
539 
540 // get local handles to proxy internals
541  auto& methods = pymeth->fMethodInfo->fMethods;
542 
543  CPPOverload::Methods_t::size_type nMethods = methods.size();
544 
545  CallContext ctxt{};
546  const auto mflags = pymeth->fMethodInfo->fFlags;
547  const auto mempolicy = (mflags & (CallContext::kUseHeuristics | CallContext::kUseStrict));
548  ctxt.fFlags |= mempolicy ? mempolicy : (uint64_t)CallContext::sMemoryPolicy;
549  ctxt.fFlags |= (mflags & CallContext::kReleaseGIL);
550  ctxt.fFlags |= (mflags & CallContext::kProtected);
551  if (IsConstructor(pymeth->fMethodInfo->fFlags)) ctxt.fFlags |= CallContext::kIsConstructor;
552 
553 // magic variable to prevent recursion passed by keyword?
554  if (kwds && PyDict_CheckExact(kwds) && PyDict_Size(kwds) != 0) {
555  if (PyDict_DelItem(kwds, PyStrings::gNoImplicit) == 0) {
556  ctxt.fFlags |= CallContext::kNoImplicit;
557  if (!PyDict_Size(kwds)) kwds = nullptr;
558  } else
559  PyErr_Clear();
560  }
561 
562 // simple case
563  if (nMethods == 1) {
564  if (!NoImplicit(&ctxt)) ctxt.fFlags |= CallContext::kAllowImplicit; // no two rounds needed
565  PyObject* result = methods[0]->Call(pymeth->fSelf, args, kwds, &ctxt);
566  return HandleReturn(pymeth, oldSelf, result);
567  }
568 
569 // otherwise, handle overloading
570  uint64_t sighash = HashSignature(args);
571 
572 // look for known signatures ...
573  auto& dispatchMap = pymeth->fMethodInfo->fDispatchMap;
574  PyCallable* memoized_pc = nullptr;
575  for (const auto& p : dispatchMap) {
576  if (p.first == sighash) {
577  memoized_pc = p.second;
578  break;
579  }
580  }
581  if (memoized_pc) {
582  PyObject* result = memoized_pc->Call(pymeth->fSelf, args, kwds, &ctxt);
583  result = HandleReturn(pymeth, oldSelf, result);
584 
585  if (result)
586  return result;
587 
588  // fall through: python is dynamic, and so, the hashing isn't infallible
589  PyErr_Clear();
590  }
591 
592 // ... otherwise loop over all methods and find the one that does not fail
593  if (!IsSorted(mflags)) {
594  std::stable_sort(methods.begin(), methods.end(), PriorityCmp);
595  pymeth->fMethodInfo->fFlags |= CallContext::kIsSorted;
596  }
597 
598  std::vector<Utility::PyError_t> errors;
599  std::vector<bool> implicit_possible(methods.size());
600  for (int stage = 0; stage < 2; ++stage) {
601  bool bHaveImplicit = false;
602  for (CPPOverload::Methods_t::size_type i = 0; i < nMethods; ++i) {
603  if (stage && !implicit_possible[i])
604  continue; // did not set implicit conversion, so don't try again
605 
606  PyObject* result = methods[i]->Call(pymeth->fSelf, args, kwds, &ctxt);
607  if (result != 0) {
608  // success: update the dispatch map for subsequent calls
609  if (!memoized_pc)
610  dispatchMap.push_back(std::make_pair(sighash, methods[i]));
611  else {
612  // debatable: apparently there are two methods that map onto the same sighash
613  // and preferring the latest may result in "ping pong."
614  for (auto& p : dispatchMap) {
615  if (p.first == sighash) {
616  p.second = methods[i];
617  break;
618  }
619  }
620  }
621 
622  // clear collected errors
623  if (!errors.empty())
624  std::for_each(errors.begin(), errors.end(), Utility::PyError_t::Clear);
625  return HandleReturn(pymeth, oldSelf, result);
626  }
627 
628  // else failure ..
629  if (stage != 0) {
630  PyErr_Clear(); // first stage errors should be more informative
631  continue;
632  }
633 
634  // collect error message/trace (automatically clears exception, too)
635  if (!PyErr_Occurred()) {
636  // this should not happen; set an error to prevent core dump and report
637  PyObject* sig = methods[i]->GetPrototype();
638  PyErr_Format(PyExc_SystemError, "%s =>\n %s",
639  CPyCppyy_PyText_AsString(sig), (char*)"nullptr result without error in mp_call");
640  Py_DECREF(sig);
641  }
642  Utility::FetchError(errors);
643 
644  if (HaveImplicit(&ctxt)) {
645  bHaveImplicit = true;
646  implicit_possible[i] = true;
647  ctxt.fFlags &= ~CallContext::kHaveImplicit;
648  } else
649  implicit_possible[i] = false;
650  ResetCallState(pymeth->fSelf, oldSelf, false);
651  }
652 
653  // only move forward if implicit conversions are available
654  if (!bHaveImplicit)
655  break;
656 
657  ctxt.fFlags |= CallContext::kAllowImplicit;
658  }
659 
660 // first summarize, then add details
662  "none of the %d overloaded methods succeeded. Full details:", (int)nMethods);
663  SetDetailedException(errors, topmsg /* steals */, PyExc_TypeError /* default error */);
664 
665 // report failure
666  return nullptr;
667 }
668 
669 //----------------------------------------------------------------------------
670 static PyObject* mp_str(CPPOverload* cppinst)
671 {
672 // Print a description that includes the C++ name
673  std::ostringstream s;
674  s << "<C++ overload \"" << cppinst->fMethodInfo->fName << "\" at " << (void*)cppinst << ">";
675  return CPyCppyy_PyText_FromString(s.str().c_str());
676 }
677 
678 //----------------------------------------------------------------------------
679 static CPPOverload* mp_descrget(CPPOverload* pymeth, CPPInstance* pyobj, PyObject*)
680 {
681 // Descriptor; create and return a new bound method proxy (language requirement) if self
682  if (!pyobj) {
683  Py_INCREF(pymeth);
684  return pymeth; // unbound, e.g. free functions
685  }
686 
687 // else: bound
688  CPPOverload* newPyMeth = free_list;
689  if (newPyMeth != NULL) {
690  free_list = (CPPOverload*)(newPyMeth->fSelf);
691  (void)PyObject_INIT(newPyMeth, &CPPOverload_Type);
692  numfree--;
693  }
694  else {
695  newPyMeth = PyObject_GC_New(CPPOverload, &CPPOverload_Type);
696  if (!newPyMeth)
697  return nullptr;
698  }
699 
700 // method info is shared, as it contains the collected overload knowledge
701  *pymeth->fMethodInfo->fRefCount += 1;
702  newPyMeth->fMethodInfo = pymeth->fMethodInfo;
703 
704 // new method is to be bound to current object
705  Py_INCREF((PyObject*)pyobj);
706  newPyMeth->fSelf = pyobj;
707 
708  PyObject_GC_Track(newPyMeth);
709  return newPyMeth;
710 }
711 
712 
713 //= CPyCppyy method proxy construction/destruction ===========================
714 static CPPOverload* mp_new(PyTypeObject*, PyObject*, PyObject*)
715 {
716 // Create a new method proxy object.
717  CPPOverload* pymeth = PyObject_GC_New(CPPOverload, &CPPOverload_Type);
718  pymeth->fSelf = nullptr;
719  pymeth->fMethodInfo = new CPPOverload::MethodInfo_t;
720 
721  PyObject_GC_Track(pymeth);
722  return pymeth;
723 }
724 
725 //----------------------------------------------------------------------------
726 static void mp_dealloc(CPPOverload* pymeth)
727 {
728 // Deallocate memory held by method proxy object.
729  PyObject_GC_UnTrack(pymeth);
730 
731  Py_CLEAR(pymeth->fSelf);
732 
733  if (--(*pymeth->fMethodInfo->fRefCount) <= 0) {
734  delete pymeth->fMethodInfo;
735  }
736 
738  pymeth->fSelf = (CPyCppyy::CPPInstance*)free_list;
739  free_list = pymeth;
740  numfree++;
741  }
742  else {
743  PyObject_GC_Del(pymeth);
744  }
745 }
746 
747 //----------------------------------------------------------------------------
748 static Py_ssize_t mp_hash(CPPOverload* pymeth)
749 {
750 // Hash of method proxy object for insertion into dictionaries; with actual
751 // method (fMethodInfo) shared, its address is best suited.
752  return _Py_HashPointer(pymeth->fMethodInfo);
753 }
754 
755 //----------------------------------------------------------------------------
756 static int mp_traverse(CPPOverload* pymeth, visitproc visit, void* args)
757 {
758 // Garbage collector traverse of held python member objects.
759  if (pymeth->fSelf)
760  return visit((PyObject*)pymeth->fSelf, args);
761 
762  return 0;
763 }
764 
765 //----------------------------------------------------------------------------
766 static int mp_clear(CPPOverload* pymeth)
767 {
768 // Garbage collector clear of held python member objects.
769  Py_CLEAR(pymeth->fSelf);
770 
771  return 0;
772 }
773 
774 //----------------------------------------------------------------------------
775 static PyObject* mp_richcompare(CPPOverload* self, CPPOverload* other, int op)
776 {
777 // Rich set of comparison objects; only equals is defined.
778  if (op != Py_EQ)
779  return PyType_Type.tp_richcompare((PyObject*)self, (PyObject*)other, op);
780 
781 // defined by type + (shared) MethodInfo + bound self, with special case for
782 // fSelf (i.e. pseudo-function)
783  if ((Py_TYPE(self) == Py_TYPE(other) && self->fMethodInfo == other->fMethodInfo) && \
784  ((IsPseudoFunc(self) && IsPseudoFunc(other)) || self->fSelf == other->fSelf)) {
786  }
788 }
789 
790 
791 //= CPyCppyy method proxy access to internals ================================
792 static PyObject* mp_overload(CPPOverload* pymeth, PyObject* sigarg)
793 {
794 // Select and call a specific C++ overload, based on its signature.
795  if (!CPyCppyy_PyText_Check(sigarg)) {
796  PyErr_Format(PyExc_TypeError, "__overload__() argument 1 must be string, not %.50s",
797  sigarg == Py_None ? "None" : Py_TYPE(sigarg)->tp_name);
798  return nullptr;
799  }
800 
801  std::string sig1{"("}; sig1.append(CPyCppyy_PyText_AsString(sigarg)); sig1.append(")");
802  sig1.erase(std::remove(sig1.begin(), sig1.end(), ' '), std::end(sig1));
803 
804  CPPOverload::Methods_t& methods = pymeth->fMethodInfo->fMethods;
805  for (auto& meth : methods) {
806 
807  bool found = false;
808 
809  PyObject* pysig2 = meth->GetSignature(false);
810  std::string sig2(CPyCppyy_PyText_AsString(pysig2));
811  sig2.erase(std::remove(sig2.begin(), sig2.end(), ' '), std::end(sig2));
812  Py_DECREF(pysig2);
813  if (sig1 == sig2) found = true;
814 
815  if (!found) {
816  pysig2 = meth->GetSignature(true);
817  std::string sig3(CPyCppyy_PyText_AsString(pysig2));
818  sig3.erase(std::remove(sig3.begin(), sig3.end(), ' '), std::end(sig3));
819  Py_DECREF(pysig2);
820  if (sig1 == sig3) found = true;
821  }
822 
823  if (found) {
824  CPPOverload* newmeth = mp_new(nullptr, nullptr, nullptr);
825  CPPOverload::Methods_t vec; vec.push_back(meth->Clone());
826  newmeth->Set(pymeth->fMethodInfo->fName, vec);
827 
828  if (pymeth->fSelf) {
829  Py_INCREF(pymeth->fSelf);
830  newmeth->fSelf = pymeth->fSelf;
831  }
832  newmeth->fMethodInfo->fFlags = pymeth->fMethodInfo->fFlags;
833 
834  return (PyObject*)newmeth;
835  }
836  }
837 
838  PyErr_Format(PyExc_LookupError,
839  "signature \"%s\" not found", CPyCppyy_PyText_AsString(sigarg));
840  return nullptr;
841 }
842 
843 //= CPyCppyy method proxy access to internals ================================
844 static PyObject* mp_add_overload(CPPOverload* pymeth, PyObject* new_overload)
845 {
846  TPythonCallback* cb = new TPythonCallback(new_overload);
847  pymeth->AdoptMethod(cb);
849 }
850 
851 static PyMethodDef mp_methods[] = {
852  {(char*)"__overload__", (PyCFunction)mp_overload, METH_O,
853  (char*)"select overload for dispatch" },
854  {(char*)"__add_overload__", (PyCFunction)mp_add_overload, METH_O,
855  (char*)"add a new overload" },
856  {(char*)nullptr, nullptr, 0, nullptr }
857 };
858 
859 } // unnamed namespace
860 
861 
862 //= CPyCppyy method proxy type ===============================================
863 PyTypeObject CPPOverload_Type = {
864  PyVarObject_HEAD_INIT(&PyType_Type, 0)
865  (char*)"cppyy.CPPOverload", // tp_name
866  sizeof(CPPOverload), // tp_basicsize
867  0, // tp_itemsize
868  (destructor)mp_dealloc, // tp_dealloc
869  0, // tp_print
870  0, // tp_getattr
871  0, // tp_setattr
872  0, // tp_compare
873  0, // tp_repr
874  0, // tp_as_number
875  0, // tp_as_sequence
876  0, // tp_as_mapping
877  (hashfunc)mp_hash, // tp_hash
878  (ternaryfunc)mp_call, // tp_call
879  (reprfunc)mp_str, // tp_str
880  0, // tp_getattro
881  0, // tp_setattro
882  0, // tp_as_buffer
883  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, // tp_flags
884  (char*)"cppyy method proxy (internal)", // tp_doc
885  (traverseproc)mp_traverse, // tp_traverse
886  (inquiry)mp_clear, // tp_clear
887  (richcmpfunc)mp_richcompare, // tp_richcompare
888  0, // tp_weaklistoffset
889  0, // tp_iter
890  0, // tp_iternext
891  mp_methods, // tp_methods
892  0, // tp_members
893  mp_getset, // tp_getset
894  0, // tp_base
895  0, // tp_dict
896  (descrgetfunc)mp_descrget, // tp_descr_get
897  0, // tp_descr_set
898  0, // tp_dictoffset
899  0, // tp_init
900  0, // tp_alloc
901  (newfunc)mp_new, // tp_new
902  0, // tp_free
903  0, // tp_is_gc
904  0, // tp_bases
905  0, // tp_mro
906  0, // tp_cache
907  0, // tp_subclasses
908  0 // tp_weaklist
909 #if PY_VERSION_HEX >= 0x02030000
910  , 0 // tp_del
911 #endif
912 #if PY_VERSION_HEX >= 0x02060000
913  , 0 // tp_version_tag
914 #endif
915 #if PY_VERSION_HEX >= 0x03040000
916  , 0 // tp_finalize
917 #endif
918 };
919 
920 } // namespace CPyCppyy
921 
922 
923 //- public members -----------------------------------------------------------
924 void CPyCppyy::CPPOverload::Set(const std::string& name, std::vector<PyCallable*>& methods)
925 {
926 // Fill in the data of a freshly created method proxy.
928  fMethodInfo->fMethods.swap(methods);
929  fMethodInfo->fFlags &= ~CallContext::kIsSorted;
930 
931 // special case: all constructors are considered creators by default
932  if (name == "__init__")
934 
935 // special case, in heuristics mode also tag *Clone* methods as creators
937  name.find("Clone") != std::string::npos)
939 }
940 
941 //----------------------------------------------------------------------------
943 {
944 // Fill in the data of a freshly created method proxy.
945  fMethodInfo->fMethods.push_back(pc);
946  fMethodInfo->fFlags &= ~CallContext::kIsSorted;
947 }
948 
949 //----------------------------------------------------------------------------
951 {
952  if (!HasMethods()) // if fresh method being filled: also copy flags
953  fMethodInfo->fFlags = meth->fMethodInfo->fFlags;
954  fMethodInfo->fMethods.insert(fMethodInfo->fMethods.end(),
955  meth->fMethodInfo->fMethods.begin(), meth->fMethodInfo->fMethods.end());
956  fMethodInfo->fFlags &= ~CallContext::kIsSorted;
957  meth->fMethodInfo->fDispatchMap.clear();
958  meth->fMethodInfo->fMethods.clear();
959 }
960 
961 //----------------------------------------------------------------------------
963 {
964 // Destructor (this object is reference counted).
965  for (Methods_t::iterator it = fMethods.begin(); it != fMethods.end(); ++it) {
966  delete *it;
967  }
968  fMethods.clear();
969  delete fRefCount;
970 }
971 
972 // TODO: something like PyMethod_Fini to clear up the free_list
PyObject * fCallable
Definition: CPPOverload.cxx:41
PyTypeObject CPPOverload_Type
bool HaveImplicit(CallContext *ctxt)
Definition: CallContext.h:121
RPY_EXPORTED size_t SizeOf(TCppType_t klass)
static ECallFlags sMemoryPolicy
Definition: CallContext.h:70
PyInt_FromLong
Definition: Converters.cxx:858
bool CPPInstance_Check(T *object)
Definition: CPPInstance.h:118
PyObject * GetScopeProxy(Cppyy::TCppScope_t)
void SetDetailedException(std::vector< PyError_t > &errors, PyObject *topmsg, PyObject *defexc)
Definition: Utility.cxx:891
#define Py_RETURN_TRUE
Definition: CPyCppyy.h:285
PyObject * gLifeLine
Definition: PyStrings.cxx:23
#define Py_TYPE(ob)
Definition: CPyCppyy.h:209
void MergeOverload(CPPOverload *meth)
PyObject * gNoImplicit
Definition: PyStrings.cxx:55
#define PyVarObject_HEAD_INIT(type, size)
Definition: CPyCppyy.h:207
static const std::string separator("@@@")
static constexpr double s
bool IsCreator(uint64_t flags)
Definition: CallContext.h:113
#define CPPYY_BOOLEAN_PROPERTY(name, flag, label)
#define Py_RETURN_NONE
Definition: CPyCppyy.h:281
void AdoptMethod(PyCallable *pc)
CPPOverload::DispatchMap_t fDispatchMap
Definition: CPPOverload.h:46
bool IsConstructor(uint64_t flags)
Definition: CallContext.h:117
#define CPPOverload_MAXFREELIST
Definition: CPPOverload.cxx:34
bool NoImplicit(CallContext *ctxt)
Definition: CallContext.h:129
static constexpr double pc
PyObject * gThisModule
Definition: PyStrings.cxx:53
_object PyObject
Definition: PyMethodBase.h:41
void * TCppFuncAddr_t
Definition: cpp_cppyy.h:25
static PyMethodObject * free_list
uint64_t HashSignature(PyObject *args)
Definition: CPPOverload.h:16
#define CPyCppyy_PyText_Append
Definition: CPyCppyy.h:104
int Py_ssize_t
Definition: CPyCppyy.h:228
static int numfree
static void Clear(PyError_t &e)
Definition: Utility.h:82
#define CO_NOFREE
Definition: CPPOverload.cxx:11
#define CPyCppyy_PyText_FromString
Definition: CPyCppyy.h:102
#define CPyCppyy_PyText_FromFormat
Definition: CPyCppyy.h:101
bool IsSorted(uint64_t flags)
Definition: CallContext.h:109
#define CPyCppyy_PyText_AppendAndDel
Definition: CPyCppyy.h:105
RPY_EXPORTED TCppFuncAddr_t GetFunctionAddress(TCppMethod_t method, bool check_enabled=true)
void Set(const std::string &name, std::vector< PyCallable *> &methods)
CPPOverload::Methods_t fMethods
Definition: CPPOverload.h:47
#define Py_RETURN_FALSE
Definition: CPyCppyy.h:289
std::vector< PyCallable * > Methods_t
Definition: CPPOverload.h:39
typedef void((*Func_t)())
MethodInfo_t * fMethodInfo
Definition: CPPOverload.h:68
size_t FetchError(std::vector< PyError_t > &)
Definition: Utility.cxx:879
char name[80]
Definition: TGX11.cxx:109
#define CPyCppyy_PyText_Check
Definition: CPyCppyy.h:95
#define CPyCppyy_PyText_AsString
Definition: CPyCppyy.h:97