Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
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
25namespace CPyCppyy {
26
27namespace {
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.
31static CPPOverload* free_list;
32static 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
39class TPythonCallback : public PyCallable {
40public:
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
116static inline bool IsPseudoFunc(CPPOverload* pymeth)
117{
118 return pymeth->fMethodInfo->fFlags & CallContext::kIsPseudoFunc;
119}
120
121// helper to sort on method priority
122static int PriorityCmp(PyCallable* left, PyCallable* right)
123{
124 return left->GetPriority() > right->GetPriority();
125}
126
127// return helper
128static 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
140static 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 ===================================
205static PyObject* mp_name(CPPOverload* pymeth, void*)
206{
207 return CPyCppyy_PyText_FromString(pymeth->GetName().c_str());
208}
209
210//----------------------------------------------------------------------------
211static PyObject* mp_module(CPPOverload* /* pymeth */, void*)
212{
213 Py_INCREF(PyStrings::gThisModule);
215}
216
217//----------------------------------------------------------------------------
218static 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
234 PyObject* separator = CPyCppyy_PyText_FromString("\n");
235 for (CPPOverload::Methods_t::size_type i = 1; i < nMethods; ++i) {
236 CPyCppyy_PyText_Append(&doc, separator);
237 CPyCppyy_PyText_AppendAndDel(&doc, methods[i]->GetDocString());
238 }
239 Py_DECREF(separator);
240
241 return doc;
242}
243
244//----------------------------------------------------------------------------
245static 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//----------------------------------------------------------------------------
262static 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//----------------------------------------------------------------------------
279static 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//----------------------------------------------------------------------------
295static PyObject* mp_func_closure(CPPOverload* /* pymeth */, void*)
296{
297// Stub only, to fill out the python function interface.
299}
300
301//----------------------------------------------------------------------------
302static 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//----------------------------------------------------------------------------
375static 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//----------------------------------------------------------------------------
400static 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//----------------------------------------------------------------------------
410static 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//----------------------------------------------------------------------------
433static 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//----------------------------------------------------------------------------
440static 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//----------------------------------------------------------------------------
447static 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//----------------------------------------------------------------------------
460static 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) \
482static PyObject* mp_get##name(CPPOverload* pymeth, void*) { \
483 if (pymeth->fMethodInfo->fFlags & flag) { \
484 Py_RETURN_TRUE; \
485 } \
486 Py_RETURN_FALSE; \
487} \
488 \
489static int mp_set##name(CPPOverload* pymeth, PyObject* value, void*) { \
490 return set_flag(pymeth, value, flag, label); \
491}
492
493CPPYY_BOOLEAN_PROPERTY(lifeline, CallContext::kSetLifeline, "__set_lifeline__")
494CPPYY_BOOLEAN_PROPERTY(threaded, CallContext::kReleaseGIL, "__release_gil__")
495CPPYY_BOOLEAN_PROPERTY(useffi, CallContext::kUseFFI, "__useffi__")
496CPPYY_BOOLEAN_PROPERTY(sig2exc, CallContext::kProtected, "__sig2exc__")
497
498//----------------------------------------------------------------------------
499static 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 ==================================
534static 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//----------------------------------------------------------------------------
670static 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//----------------------------------------------------------------------------
679static 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 ===========================
714static 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//----------------------------------------------------------------------------
726static 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//----------------------------------------------------------------------------
748static 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//----------------------------------------------------------------------------
756static 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//----------------------------------------------------------------------------
766static 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//----------------------------------------------------------------------------
775static 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 ================================
792static 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 ================================
844static PyObject* mp_add_overload(CPPOverload* pymeth, PyObject* new_overload)
845{
846 TPythonCallback* cb = new TPythonCallback(new_overload);
847 pymeth->AdoptMethod(cb);
849}
850
851static 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 ===============================================
863PyTypeObject 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 -----------------------------------------------------------
924void 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
#define CPPOverload_MAXFREELIST
#define CO_NOFREE
#define CPPYY_BOOLEAN_PROPERTY(name, flag, label)
PyObject * fCallable
#define Py_TYPE(ob)
Definition CPyCppyy.h:217
#define Py_RETURN_TRUE
Definition CPyCppyy.h:293
#define Py_RETURN_FALSE
Definition CPyCppyy.h:297
int Py_ssize_t
Definition CPyCppyy.h:236
#define CPyCppyy_PyText_Append
Definition CPyCppyy.h:104
#define CPyCppyy_PyText_AsString
Definition CPyCppyy.h:97
#define CPyCppyy_PyText_AppendAndDel
Definition CPyCppyy.h:105
#define CPyCppyy_PyText_FromFormat
Definition CPyCppyy.h:101
#define Py_RETURN_NONE
Definition CPyCppyy.h:289
#define CPyCppyy_PyText_FromString
Definition CPyCppyy.h:102
#define CPyCppyy_PyText_Check
Definition CPyCppyy.h:95
#define PyVarObject_HEAD_INIT(type, size)
Definition CPyCppyy.h:215
PyInt_FromLong
unsigned int fFlags
_object PyObject
char name[80]
Definition TGX11.cxx:110
typedef void((*Func_t)())
void MergeOverload(CPPOverload *meth)
void AdoptMethod(PyCallable *pc)
MethodInfo_t * fMethodInfo
Definition CPPOverload.h:68
std::vector< PyCallable * > Methods_t
Definition CPPOverload.h:39
void Set(const std::string &name, std::vector< PyCallable * > &methods)
PyObject * gLifeLine
Definition PyStrings.cxx:23
PyObject * gThisModule
Definition PyStrings.cxx:53
PyObject * gNoImplicit
Definition PyStrings.cxx:55
size_t FetchError(std::vector< PyError_t > &)
Definition Utility.cxx:879
void SetDetailedException(std::vector< PyError_t > &errors, PyObject *topmsg, PyObject *defexc)
Definition Utility.cxx:891
Set of helper functions that are invoked from the pythonizors, on the Python side.
bool HaveImplicit(CallContext *ctxt)
PyObject * GetScopeProxy(Cppyy::TCppScope_t)
static PyMethodObject * free_list
bool NoImplicit(CallContext *ctxt)
static int numfree
bool IsCreator(uint64_t flags)
bool CPPInstance_Check(T *object)
PyTypeObject CPPOverload_Type
uint64_t HashSignature(PyObject *args)
Definition CPPOverload.h:16
bool IsConstructor(uint64_t flags)
bool IsSorted(uint64_t flags)
RPY_EXPORTED TCppFuncAddr_t GetFunctionAddress(TCppMethod_t method, bool check_enabled=true)
RPY_EXPORTED size_t SizeOf(TCppType_t klass)
void * TCppFuncAddr_t
Definition cpp_cppyy.h:25
CPPOverload::DispatchMap_t fDispatchMap
Definition CPPOverload.h:46
CPPOverload::Methods_t fMethods
Definition CPPOverload.h:47
static ECallFlags sMemoryPolicy
Definition CallContext.h:70
static void Clear(PyError_t &e)
Definition Utility.h:82