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 "CPyCppyy/Reflex.h"
4#include "structmember.h" // from Python
5#if PY_VERSION_HEX >= 0x02050000
6#if PY_VERSION_HEX < 0x030b0000
7#include "code.h" // from Python
8#endif
9#else
10#include "compile.h" // from Python
11#endif
12#ifndef CO_NOFREE
13// python2.2 does not have CO_NOFREE defined
14#define CO_NOFREE 0x0040
15#endif
16#include "CPPOverload.h"
17#include "CPPInstance.h"
18#include "CallContext.h"
19#include "PyStrings.h"
20#include "Utility.h"
21
22// Standard
23#include <algorithm>
24#include <sstream>
25#include <vector>
26
27
28namespace CPyCppyy {
29
30namespace {
31
32// from CPython's instancemethod: Free list for method objects to safe malloc/free overhead
33// The fSelf field is used to chain the elements.
34static CPPOverload* free_list;
35static int numfree = 0;
36#ifndef CPPOverload_MAXFREELIST
37#define CPPOverload_MAXFREELIST 32
38#endif
39
40
41// TODO: only used in pythonizations to add Python-side overloads to existing
42// C++ overloads, but may be better off integrated with Pythonize.cxx callbacks
43class TPythonCallback : public PyCallable {
44public:
46
47 TPythonCallback(PyObject* callable) : fCallable(nullptr)
48 {
50 PyErr_SetString(PyExc_TypeError, "parameter must be callable");
51 return;
52 }
55 }
56
57 ~TPythonCallback() override {
58 Py_DECREF(fCallable);
59 fCallable = nullptr;
60 }
61
62 PyObject* GetSignature(bool /*show_formalargs*/ = true) override {
63 return CPyCppyy_PyText_FromString("*args, **kwargs");
64 }
65 PyObject* GetSignatureNames() override {
66 return PyTuple_New(0);
67 }
68 PyObject* GetSignatureTypes() override {
69 return PyTuple_New(0);
70 }
71 PyObject* GetPrototype(bool /*show_formalargs*/ = true) override {
72 return CPyCppyy_PyText_FromString("<callback>");
73 }
74 PyObject* GetDocString() override {
75 if (PyObject_HasAttrString(fCallable, "__doc__")) {
76 return PyObject_GetAttrString(fCallable, "__doc__");
77 } else {
78 return GetPrototype();
79 }
80 }
81
82 int GetPriority() override { return 100; };
83 bool IsGreedy() override { return false; };
84
85 int GetMaxArgs() override { return 100; };
86 PyObject* GetCoVarNames() override { // TODO: pick these up from the callable
88 }
89 PyObject* GetArgDefault(int /* iarg */, bool /* silent */ =true) override {
90 Py_RETURN_NONE; // TODO: pick these up from the callable
91 }
92
93 PyObject* GetScopeProxy() override { // should this be the module ??
95 }
96
98 return (Cppyy::TCppFuncAddr_t)nullptr;
99 }
100
101 PyCallable* Clone() override { return new TPythonCallback(*this); }
102
103 PyObject* Call(CPPInstance*& self,
104 CPyCppyy_PyArgs_t args, size_t nargsf, PyObject* kwds, CallContext* /* ctxt = 0 */) override {
105
106#if PY_VERSION_HEX >= 0x03080000
107 if (self) {
108 if (nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET) { // mutation allowed?
109 std::swap(((PyObject**)args-1)[0], (PyObject*&)self);
111 args = args-1;
112 } else {
116 if (!newArgs)
117 return nullptr;
118
119 newArgs[0] = (PyObject*)self;
120 if (0 < totalargs)
121 memcpy((void*)&newArgs[1], args, totalargs * sizeof(PyObject*));
122 args = newArgs;
123 }
124 nargsf += 1;
125 }
126
127 PyObject* result = CPyCppyy_PyObject_Call(fCallable, args, nargsf, kwds);
128 if (self) {
130 std::swap(((PyObject**)args-1)[0], (PyObject*&)self);
131 else PyMem_Free((void*)args);
132 }
133#else
134 PyObject* newArgs = nullptr;
135 if (self) {
140 for (Py_ssize_t iarg = 0; iarg < nargs; ++iarg) {
144 }
145 } else {
146 Py_INCREF(args);
147 newArgs = args;
148 }
149 PyObject* result = PyObject_Call(fCallable, newArgs, kwds);
151#endif
152 return result;
153 }
154};
155
156// helper to test whether a method is used in a pseudo-function modus
157static inline bool IsPseudoFunc(CPPOverload* pymeth)
158{
159 return pymeth->fMethodInfo->fFlags & CallContext::kIsPseudoFunc;
160}
161
162// helper to sort on method priority
163static int PriorityCmp(const std::pair<int, PyCallable*>& left, const std::pair<int, PyCallable*>& right)
164{
165 return left.first > right.first;
166}
167
168// return helper
169static inline void ResetCallState(CPPInstance* descr_self, CPPInstance*& im_self)
170{
171// reset self if needed, allowing simple re-use
172 if (descr_self != im_self) {
175 }
176}
177
178// helper to factor out return logic of mp_call / mp_vectorcall
179static inline PyObject* HandleReturn(
180 CPPOverload* pymeth, CPPInstance* im_self, PyObject* result)
181{
182// special case for python exceptions, propagated through C++ layer
183 if (result) {
184 CPPInstance* cppres = (CPPInstance*)(CPPInstance_Check(result) ? result : nullptr);
185
186 // if this method creates new objects, always take ownership
187 if (IsCreator(pymeth->fMethodInfo->fFlags)) {
188
189 // either be a constructor with a fresh object proxy self ...
190 if (IsConstructor(pymeth->fMethodInfo->fFlags)) {
191 if (im_self)
192 im_self->PythonOwns();
193 }
194
195 // ... or be a regular method with an object proxy return value
196 else if (cppres)
197 cppres->PythonOwns();
198 }
199
200 // if this new object falls inside self, make sure its lifetime is proper
201 if (!(pymeth->fMethodInfo->fFlags & CallContext::kNeverLifeLine)) {
202 int ll_action = 0;
203 if ((PyObject*)im_self != result) {
204 if (pymeth->fMethodInfo->fFlags & CallContext::kSetLifeLine)
205 ll_action = 1;
206 else if (cppres && CPPInstance_Check(im_self)) {
207 // if self was a by-value return and result is not, pro-actively protect result;
208 // else if the return value falls within the memory of 'this', force a lifeline
209 if (!(cppres->fFlags & CPPInstance::kIsValue)) { // no need if the result is temporary
210 if (im_self->fFlags & CPPInstance::kIsValue)
211 ll_action = 2;
212 else if (im_self->fFlags & CPPInstance::kHasLifeLine)
213 ll_action = 3;
214 else {
215 ptrdiff_t offset = (ptrdiff_t)cppres->GetObject() - (ptrdiff_t)im_self->GetObject();
216 if (0 <= offset && offset < (ptrdiff_t)Cppyy::SizeOf(im_self->ObjectIsA()))
217 ll_action = 4;
218 }
219 }
220 }
221 }
222
223 if (!ll_action)
224 pymeth->fMethodInfo->fFlags |= CallContext::kNeverLifeLine; // assume invariant semantics
225 else {
227 PyErr_Clear(); // ignored
228 if (cppres) cppres->fFlags |= CPPInstance::kHasLifeLine; // for chaining
229 pymeth->fMethodInfo->fFlags |= CallContext::kSetLifeLine; // for next time
230 }
231 }
232 }
233
234// reset self as necessary to allow re-use of the CPPOverload
236
237 return result;
238}
239
240
241//= CPyCppyy method proxy object behaviour ===================================
242static PyObject* mp_name(CPPOverload* pymeth, void*)
243{
244 return CPyCppyy_PyText_FromString(pymeth->GetName().c_str());
245}
246
247//----------------------------------------------------------------------------
248static PyObject* mp_module(CPPOverload* /* pymeth */, void*)
249{
252}
253
254//----------------------------------------------------------------------------
255static PyObject* mp_doc(CPPOverload* pymeth, void*)
256{
257 if (pymeth->fMethodInfo->fDoc) {
258 Py_INCREF(pymeth->fMethodInfo->fDoc);
259 return pymeth->fMethodInfo->fDoc;
260 }
261
262// Build python document string ('__doc__') from all C++-side overloads.
263 CPPOverload::Methods_t& methods = pymeth->fMethodInfo->fMethods;
264
265// collect doc strings
266 CPPOverload::Methods_t::size_type nMethods = methods.size();
267 if (nMethods == 0) // from template proxy with no instantiations
268 return nullptr;
269 PyObject* doc = methods[0]->GetDocString();
270
271// simple case
272 if (nMethods == 1)
273 return doc;
274
275// overloaded method
276 PyObject* separator = CPyCppyy_PyText_FromString("\n");
277 for (CPPOverload::Methods_t::size_type i = 1; i < nMethods; ++i) {
278 CPyCppyy_PyText_Append(&doc, separator);
279 CPyCppyy_PyText_AppendAndDel(&doc, methods[i]->GetDocString());
280 }
281 Py_DECREF(separator);
282
283 return doc;
284}
285
286static int mp_doc_set(CPPOverload* pymeth, PyObject *val, void *)
287{
288 Py_XDECREF(pymeth->fMethodInfo->fDoc);
289 Py_INCREF(val);
290 pymeth->fMethodInfo->fDoc = val;
291 return 0;
292}
293
294/**
295 * @brief Returns a dictionary with the input parameter names for all overloads.
296 *
297 * This dictionary may look like:
298 *
299 * {'double ::foo(int a, float b, double c)': ('a', 'b', 'c'),
300 * 'float ::foo(float b)': ('b',),
301 * 'int ::foo(int a)': ('a',),
302 * 'int ::foo(int a, float b)': ('a', 'b')}
303 */
304static PyObject *mp_func_overloads_names(CPPOverload *pymeth)
305{
306
307 const CPPOverload::Methods_t &methods = pymeth->fMethodInfo->fMethods;
308
310
311 for (PyCallable *method : methods) {
312 PyDict_SetItem(overloads_names_dict, method->GetPrototype(), method->GetSignatureNames());
313 }
314
316}
317
318/**
319 * @brief Returns a dictionary with the types of all overloads.
320 *
321 * This dictionary may look like:
322 *
323 * {'double ::foo(int a, float b, double c)': {'input_types': ('int', 'float', 'double'), 'return_type': 'double'},
324 * 'float ::foo(float b)': {'input_types': ('float',), 'return_type': 'float'},
325 * 'int ::foo(int a)': {'input_types': ('int',), 'return_type': 'int'},
326 * 'int ::foo(int a, float b)': {'input_types': ('int', 'float'), 'return_type': 'int'}}
327 */
328static PyObject *mp_func_overloads_types(CPPOverload *pymeth)
329{
330
331 const CPPOverload::Methods_t &methods = pymeth->fMethodInfo->fMethods;
332
334
335 for (PyCallable *method : methods) {
336 PyDict_SetItem(overloads_types_dict, method->GetPrototype(), method->GetSignatureTypes());
337 }
338
340}
341
342//----------------------------------------------------------------------------
343static PyObject* mp_meth_func(CPPOverload* pymeth, void*)
344{
345// Create a new method proxy to be returned.
346 CPPOverload* newPyMeth = (CPPOverload*)CPPOverload_Type.tp_alloc(&CPPOverload_Type, 0);
347
348// method info is shared, as it contains the collected overload knowledge
349 *pymeth->fMethodInfo->fRefCount += 1;
350 newPyMeth->fMethodInfo = pymeth->fMethodInfo;
351
352// new method is unbound, track whether this proxy is used in the capacity of a
353// method or a function (which normally is a CPPFunction)
354 newPyMeth->fMethodInfo->fFlags |= CallContext::kIsPseudoFunc;
355
356 return (PyObject*)newPyMeth;
357}
358
359//----------------------------------------------------------------------------
360static PyObject* mp_meth_self(CPPOverload* pymeth, void*)
361{
362// Return the bound self, if any; in case of pseudo-function role, pretend
363// that the data member im_self does not exist.
364 if (IsPseudoFunc(pymeth)) {
366 "function %s has no attribute \'im_self\'", pymeth->fMethodInfo->fName.c_str());
367 return nullptr;
368 } else if (pymeth->fSelf != 0) {
369 Py_INCREF((PyObject*)pymeth->fSelf);
370 return (PyObject*)pymeth->fSelf;
371 }
372
374}
375
376//----------------------------------------------------------------------------
377static PyObject* mp_meth_class(CPPOverload* pymeth, void*)
378{
379// Return scoping class; in case of pseudo-function role, pretend that there
380// is no encompassing class (i.e. global scope).
381 if (!IsPseudoFunc(pymeth) && pymeth->fMethodInfo->fMethods.size()) {
382 PyObject* pyclass = pymeth->fMethodInfo->fMethods[0]->GetScopeProxy();
383 if (!pyclass)
385 "function %s has no attribute \'im_class\'", pymeth->fMethodInfo->fName.c_str());
386 return pyclass;
387 }
388
390}
391
392//----------------------------------------------------------------------------
393static PyObject* mp_func_closure(CPPOverload* /* pymeth */, void*)
394{
395// Stub only, to fill out the python function interface.
397}
398
399// To declare a variable as unused only when compiling for Python 3.
400#if PY_VERSION_HEX < 0x03000000
401#define CPyCppyy_Py3_UNUSED(name) name
402#else
403#define CPyCppyy_Py3_UNUSED(name)
404#endif
405
406//----------------------------------------------------------------------------
407static PyObject* mp_func_code(CPPOverload* CPyCppyy_Py3_UNUSED(pymeth), void*)
408{
409// Code details are used in module inspect to fill out interactive help()
410#if PY_VERSION_HEX < 0x03000000
411 CPPOverload::Methods_t& methods = pymeth->fMethodInfo->fMethods;
412
413// collect arguments only if there is just 1 overload, otherwise put in a
414// fake *args (see below for co_varnames)
415 PyObject* co_varnames = methods.size() == 1 ? methods[0]->GetCoVarNames() : nullptr;
416 if (!co_varnames) {
417 // TODO: static methods need no 'self' (but is harmless otherwise)
418 co_varnames = PyTuple_New(1 /* self */ + 1 /* fake */);
421 }
422
424
425// for now, code object representing the statement 'pass'
426 PyObject* co_code = PyString_FromStringAndSize("d\x00\x00S", 4);
427
428// tuples with all the const literals used in the function
431
432// names, freevars, and cellvars go unused
434
435// filename is made-up
437
438// name is the function name, also through __name__ on the function itself
439 PyObject* co_name = PyString_FromString(pymeth->GetName().c_str());
440
441// firstlineno is the line number of first function code in the containing scope
442
443// lnotab is a packed table that maps instruction count and line number
444 PyObject* co_lnotab = PyString_FromString("\x00\x01\x0c\x01");
445
446 PyObject* code = (PyObject*)PyCode_New(
447 co_argcount, // argcount
448 co_argcount+1, // nlocals
449 2, // stacksize
451 co_code, // code
452 co_consts, // consts
453 co_names, // names
454 co_varnames, // varnames
455 co_unused, // freevars
456 co_unused, // cellvars
457 co_filename, // filename
458 co_name, // name
459 1, // firstlineno
460 co_lnotab); // lnotab
461
470
471 return code;
472#else
473// not important for functioning of most code, so not implemented for p3 for now (TODO)
475#endif
476}
477
478//----------------------------------------------------------------------------
479static PyObject* mp_func_defaults(CPPOverload* pymeth, void*)
480{
481// Create a tuple of default values, if there is only one method (otherwise
482// leave undefined: this is only used by inspect for interactive help())
483 CPPOverload::Methods_t& methods = pymeth->fMethodInfo->fMethods;
484
485 if (methods.size() != 1)
486 return PyTuple_New(0);
487
488 int maxarg = methods[0]->GetMaxArgs();
489
491
492 int itup = 0;
493 for (int iarg = 0; iarg < maxarg; ++iarg) {
494 PyObject* defvalue = methods[0]->GetArgDefault(iarg);
495 if (defvalue)
497 else
498 PyErr_Clear();
499 }
501
502 return defaults;
503}
504
505//----------------------------------------------------------------------------
506static PyObject* mp_func_globals(CPPOverload* /* pymeth */, void*)
507{
508// Return this function's global dict (hard-wired to be the cppyy module); used
509// for lookup of names from co_code indexing into co_names.
512 return pyglobal;
513}
514
515//----------------------------------------------------------------------------
516static inline int set_flag(CPPOverload* pymeth, PyObject* value, CallContext::ECallFlags flag, const char* name)
517{
518// Generic setter of a (boolean) flag.
519 if (!value) { // accept as false (delete)
520 pymeth->fMethodInfo->fFlags &= ~flag;
521 return 0;
522 }
523
524 long istrue = PyLong_AsLong(value);
525 if (istrue == -1 && PyErr_Occurred()) {
526 PyErr_Format(PyExc_ValueError, "a boolean 1 or 0 is required for %s", name);
527 return -1;
528 }
529
530 if (istrue)
531 pymeth->fMethodInfo->fFlags |= flag;
532 else
533 pymeth->fMethodInfo->fFlags &= ~flag;
534
535 return 0;
536}
537
538//----------------------------------------------------------------------------
539static PyObject* mp_getcreates(CPPOverload* pymeth, void*)
540{
541// Get '__creates__' boolean, which determines ownership of return values.
542 return PyInt_FromLong((long)IsCreator(pymeth->fMethodInfo->fFlags));
543}
544
545//----------------------------------------------------------------------------
546static int mp_setcreates(CPPOverload* pymeth, PyObject* value, void*)
547{
548// Set '__creates__' boolean, which determines ownership of return values.
549 return set_flag(pymeth, value, CallContext::kIsCreator, "__creates__");
550}
551
552constexpr const char *mempolicy_error_message =
553 "The __mempolicy__ attribute can't be used, because in the past it was reserved to manage the local memory policy. "
554 "If you want to do that now, please implement a pythonization for your class that uses SetOwnership() to manage the "
555 "ownership of arguments according to your needs.";
556
557//----------------------------------------------------------------------------
558static PyObject* mp_getmempolicy(CPPOverload*, void*)
559{
561 return nullptr;
562}
563
564//----------------------------------------------------------------------------
565static int mp_setmempolicy(CPPOverload*, PyObject*, void*)
566{
568 return -1;
569}
570
571//----------------------------------------------------------------------------
572#define CPPYY_BOOLEAN_PROPERTY(name, flag, label) \
573static PyObject* mp_get##name(CPPOverload* pymeth, void*) { \
574 if (pymeth->fMethodInfo->fFlags & flag) { \
575 Py_RETURN_TRUE; \
576 } \
577 Py_RETURN_FALSE; \
578} \
579 \
580static int mp_set##name(CPPOverload* pymeth, PyObject* value, void*) { \
581 return set_flag(pymeth, value, flag, label); \
582}
583
585CPPYY_BOOLEAN_PROPERTY(threaded, CallContext::kReleaseGIL, "__release_gil__")
586CPPYY_BOOLEAN_PROPERTY(useffi, CallContext::kUseFFI, "__useffi__")
587CPPYY_BOOLEAN_PROPERTY(sig2exc, CallContext::kProtected, "__sig2exc__")
588
589static PyObject* mp_getcppname(CPPOverload* pymeth, void*)
590{
591 if ((void*)pymeth == (void*)&CPPOverload_Type)
592 return CPyCppyy_PyText_FromString("CPPOverload_Type");
593
594 auto& methods = pymeth->fMethodInfo->fMethods;
595 if (methods.empty())
596 return CPyCppyy_PyText_FromString("void (*)()"); // debatable
597
598 if (methods.size() == 1)
599 return methods[0]->GetTypeName();
600
601 return CPyCppyy_PyText_FromString("void* (*)(...)"); // id.
602}
603
604
605//----------------------------------------------------------------------------
606static PyGetSetDef mp_getset[] = {
607 {(char*)"__name__", (getter)mp_name, nullptr, nullptr, nullptr},
608 {(char*)"__module__", (getter)mp_module, nullptr, nullptr, nullptr},
609 {(char*)"__doc__", (getter)mp_doc, (setter)mp_doc_set, nullptr, nullptr},
610
611// to be more python-like, where these are duplicated as well; to actually
612// derive from the python method or function type is too memory-expensive,
613// given that most of the members of those types would not be used
614 {(char*)"im_func", (getter)mp_meth_func, nullptr, nullptr, nullptr},
615 {(char*)"im_self", (getter)mp_meth_self, nullptr, nullptr, nullptr},
616 {(char*)"im_class", (getter)mp_meth_class, nullptr, nullptr, nullptr},
617
618 {(char*)"func_closure", (getter)mp_func_closure, nullptr, nullptr, nullptr},
619 {(char*)"func_code", (getter)mp_func_code, nullptr, nullptr, nullptr},
620 {(char*)"func_defaults", (getter)mp_func_defaults, nullptr, nullptr, nullptr},
621 {(char*)"func_globals", (getter)mp_func_globals, nullptr, nullptr, nullptr},
622 {(char*)"func_doc", (getter)mp_doc, (setter)mp_doc_set, nullptr, nullptr},
623 {(char*)"func_name", (getter)mp_name, nullptr, nullptr, nullptr},
624 {(char*)"func_overloads_types", (getter)mp_func_overloads_types, nullptr, nullptr, nullptr},
625 {(char*)"func_overloads_names", (getter)mp_func_overloads_names, nullptr, nullptr, nullptr},
626
627
628// flags to control behavior
629 {(char*)"__creates__", (getter)mp_getcreates, (setter)mp_setcreates,
630 (char*)"For ownership rules of result: if true, objects are python-owned", nullptr},
631 {(char*)"__mempolicy__", (getter)mp_getmempolicy, (setter)mp_setmempolicy,
632 (char*)"Unused", nullptr},
633 {(char*)"__set_lifeline__", (getter)mp_getlifeline, (setter)mp_setlifeline,
634 (char*)"If true, set a lifeline from the return value onto self", nullptr},
635 {(char*)"__release_gil__", (getter)mp_getthreaded, (setter)mp_setthreaded,
636 (char*)"If true, releases GIL on call into C++", nullptr},
637 {(char*)"__useffi__", (getter)mp_getuseffi, (setter)mp_setuseffi,
638 (char*)"not implemented", nullptr},
639 {(char*)"__sig2exc__", (getter)mp_getsig2exc, (setter)mp_setsig2exc,
640 (char*)"If true, turn signals into Python exceptions", nullptr},
641
642// basic reflection information
643 {(char*)"__cpp_name__", (getter)mp_getcppname, nullptr, nullptr, nullptr},
644
645 {(char*)nullptr, nullptr, nullptr, nullptr, nullptr}
646};
647
648//= CPyCppyy method proxy function behavior ==================================
649#if PY_VERSION_HEX >= 0x03080000
650static PyObject* mp_vectorcall(
651 CPPOverload* pymeth, PyObject* const *args, size_t nargsf, PyObject* kwds)
652#else
653static PyObject* mp_call(CPPOverload* pymeth, PyObject* args, PyObject* kwds)
654#endif
655{
656#if PY_VERSION_HEX < 0x03080000
657 size_t nargsf = PyTuple_GET_SIZE(args);
658#endif
659
660// Call the appropriate overload of this method.
661
662// If called from a descriptor, then this could be a bound function with
663// non-zero self; otherwise pymeth->fSelf is expected to always be nullptr.
664
665 CPPInstance* im_self = pymeth->fSelf;
666
667// get local handles to proxy internals
668 auto& methods = pymeth->fMethodInfo->fMethods;
669
670 CPPOverload::Methods_t::size_type nMethods = methods.size();
671
672 CallContext ctxt{};
673 const auto mflags = pymeth->fMethodInfo->fFlags;
675 ctxt.fFlags |= (mflags & CallContext::kProtected);
676 if (IsConstructor(pymeth->fMethodInfo->fFlags)) ctxt.fFlags |= CallContext::kIsConstructor;
678 ctxt.fPyContext = (PyObject*)im_self; // no Py_INCREF as no ownership
679
680// check implicit conversions status (may be disallowed to prevent recursion)
681 ctxt.fFlags |= (pymeth->fFlags & CallContext::kNoImplicit);
682
683// simple case
684 if (nMethods == 1) {
685 if (!NoImplicit(&ctxt)) ctxt.fFlags |= CallContext::kAllowImplicit; // no two rounds needed
686 PyObject* result = methods[0]->Call(im_self, args, nargsf, kwds, &ctxt);
687 return HandleReturn(pymeth, im_self, result);
688 }
689
690// otherwise, handle overloading
691 uint64_t sighash = HashSignature(args, nargsf);
692
693// look for known signatures ...
694 auto& dispatchMap = pymeth->fMethodInfo->fDispatchMap;
695 PyCallable* memoized_pc = nullptr;
696 for (const auto& p : dispatchMap) {
697 if (p.first == sighash) {
698 memoized_pc = p.second;
699 break;
700 }
701 }
702 if (memoized_pc) {
703 // it is necessary to enable implicit conversions as the memoized call may be from
704 // such a conversion case; if the call fails, the implicit flag is reset below
706 PyObject* result = memoized_pc->Call(im_self, args, nargsf, kwds, &ctxt);
707 if (result)
708 return HandleReturn(pymeth, im_self, result);
709
710 // fall through: python is dynamic, and so, the hashing isn't infallible
711 ctxt.fFlags &= ~CallContext::kAllowImplicit;
712 PyErr_Clear();
714 }
715
716// ... otherwise loop over all methods and find the one that does not fail
717 if (!IsSorted(mflags)) {
718 // sorting is based on priority, which is not stored on the method as it is used
719 // only once, so copy the vector of methods into one where the priority can be
720 // stored during sorting
721 std::vector<std::pair<int, PyCallable*>> pm; pm.reserve(methods.size());
722 for (auto ptr : methods)
723 pm.emplace_back(ptr->GetPriority(), ptr);
724 std::stable_sort(pm.begin(), pm.end(), PriorityCmp);
725 for (CPPOverload::Methods_t::size_type i = 0; i < methods.size(); ++i)
726 methods[i] = pm[i].second;
727 pymeth->fMethodInfo->fFlags |= CallContext::kIsSorted;
728 }
729
730 std::vector<Utility::PyError_t> errors;
731 std::vector<bool> implicit_possible(methods.size());
732 for (int stage = 0; stage < 2; ++stage) {
733 bool bHaveImplicit = false;
734 for (CPPOverload::Methods_t::size_type i = 0; i < nMethods; ++i) {
735 if (stage && !implicit_possible[i])
736 continue; // did not set implicit conversion, so don't try again
737
738 PyObject* result = methods[i]->Call(im_self, args, nargsf, kwds, &ctxt);
739 if (result) {
740 // success: update the dispatch map for subsequent calls
741 if (!memoized_pc)
742 dispatchMap.push_back(std::make_pair(sighash, methods[i]));
743 else {
744 // debatable: apparently there are two methods that map onto the same sighash
745 // and preferring the latest may result in "ping pong."
746 for (auto& p : dispatchMap) {
747 if (p.first == sighash) {
748 p.second = methods[i];
749 break;
750 }
751 }
752 }
753
754 return HandleReturn(pymeth, im_self, result);
755 }
756
757 // else failure ..
758 if (stage != 0) {
759 PyErr_Clear(); // first stage errors should be the more informative
761 continue;
762 }
763
764 // collect error message/trace (automatically clears exception, too)
765 if (!PyErr_Occurred()) {
766 // this should not happen; set an error to prevent core dump and report
767 PyObject* sig = methods[i]->GetPrototype();
768 PyErr_Format(PyExc_SystemError, "%s =>\n %s",
769 CPyCppyy_PyText_AsString(sig), (char*)"nullptr result without error in overload call");
770 Py_DECREF(sig);
771 }
772
773 // retrieve, store, and clear errors
777
778 if (HaveImplicit(&ctxt)) {
779 bHaveImplicit = true;
780 implicit_possible[i] = true;
781 ctxt.fFlags &= ~CallContext::kHaveImplicit;
782 } else
783 implicit_possible[i] = false;
785 }
786
787 // only move forward if implicit conversions are available
788 if (!bHaveImplicit)
789 break;
790
792 }
793
794// first summarize, then add details
796 "none of the %d overloaded methods succeeded. Full details:", (int)nMethods);
797 SetDetailedException(std::move(errors), topmsg /* steals */, PyExc_TypeError /* default error */);
798
799// report failure
800 return nullptr;
801}
802
803//----------------------------------------------------------------------------
804static PyObject* mp_str(CPPOverload* cppinst)
805{
806// Print a description that includes the C++ name
807 std::ostringstream s;
808 s << "<C++ overload \"" << cppinst->fMethodInfo->fName << "\" at " << (void*)cppinst << ">";
809 return CPyCppyy_PyText_FromString(s.str().c_str());
810}
811
812//----------------------------------------------------------------------------
813static CPPOverload* mp_descr_get(CPPOverload* pymeth, CPPInstance* pyobj, PyObject*)
814{
815// Descriptor; create and return a new, possibly bound, method proxy. This method
816// has evolved with versions of python as follows:
817//
818// Python version | Action
819// <- py2.7 | bound methods need to be first-class objects, so create a new
820// | method object if self is not nullptr or Py_None
821// py3.0-py3.7 | bound methods are no longer a language requirement, but
822// | still supported: for convenience, retain old behavior
823// py3.8 <= | vector calls no longer call the descriptor, so when it is
824// | called, the method is likely stored, so should be new object
825
826#if PY_VERSION_HEX < 0x03080000
827 if (!pyobj || (PyObject*)pyobj == Py_None /* from unbound TemplateProxy */) {
828 Py_XDECREF(pymeth->fSelf); pymeth->fSelf = nullptr;
831 return pymeth; // unbound, e.g. free functions
832 }
833#endif
834
835// create a new method object
836 bool gc_track = false;
837 CPPOverload* newPyMeth = free_list;
838 if (newPyMeth != NULL) {
839 free_list = (CPPOverload*)(newPyMeth->fSelf);
841 numfree--;
842 } else {
844 if (!newPyMeth)
845 return nullptr;
846 gc_track = true;
847 }
848
849// method info is shared, as it contains the collected overload knowledge
850 *pymeth->fMethodInfo->fRefCount += 1;
851 newPyMeth->fMethodInfo = pymeth->fMethodInfo;
852
853#if PY_VERSION_HEX >= 0x03080000
854 newPyMeth->fVectorCall = pymeth->fVectorCall;
855
856 if (pyobj && (PyObject*)pyobj != Py_None) {
858 newPyMeth->fSelf = pyobj;
860 } else {
861 newPyMeth->fSelf = nullptr;
863 }
864
865// vector calls don't get here, unless a method is looked up on an instance, for
866// e.g. class methods (C++ static); notify downstream to expect a 'self'
868
869#else
870// new method is to be bound to current object
872 newPyMeth->fSelf = pyobj;
873
874// reset flags of the new method, as there is a self (which may or may not have
875// come in through direct call syntax, but that's now impossible to know, so this
876// is the safer choice)
878#endif
879
880 if (gc_track)
882
883 return newPyMeth;
884}
885
886
887//= CPyCppyy method proxy construction/destruction ===========================
888static CPPOverload* mp_new(PyTypeObject*, PyObject*, PyObject*)
889{
890// Create a new method proxy object.
891 CPPOverload* pymeth = PyObject_GC_New(CPPOverload, &CPPOverload_Type);
892 pymeth->fSelf = nullptr;
893 pymeth->fFlags = CallContext::kNone;
894 pymeth->fMethodInfo = new CPPOverload::MethodInfo_t;
895
897 return pymeth;
898}
899
900//----------------------------------------------------------------------------
901static void mp_dealloc(CPPOverload* pymeth)
902{
903// Deallocate memory held by method proxy object.
905
906 Py_CLEAR(pymeth->fSelf);
907
908 if (--(*pymeth->fMethodInfo->fRefCount) <= 0) {
909 delete pymeth->fMethodInfo;
910 }
911
915 numfree++;
916 } else {
918 }
919}
920
921//----------------------------------------------------------------------------
922static Py_ssize_t mp_hash(CPPOverload* pymeth)
923{
924// Hash of method proxy object for insertion into dictionaries; with actual
925// method (fMethodInfo) shared, its address is best suited.
926#if PY_VERSION_HEX >= 0x030d0000
927 return Py_HashPointer(pymeth->fMethodInfo);
928#else
929 return _Py_HashPointer(pymeth->fMethodInfo);
930#endif
931}
932
933//----------------------------------------------------------------------------
934static int mp_traverse(CPPOverload* pymeth, visitproc visit, void* args)
935{
936// Garbage collector traverse of held python member objects.
937 if (pymeth->fSelf)
938 return visit((PyObject*)pymeth->fSelf, args);
939
940 return 0;
941}
942
943//----------------------------------------------------------------------------
944static int mp_clear(CPPOverload* pymeth)
945{
946// Garbage collector clear of held python member objects.
947 Py_CLEAR(pymeth->fSelf);
948
949 return 0;
950}
951
952//----------------------------------------------------------------------------
953static PyObject* mp_richcompare(CPPOverload* self, CPPOverload* other, int op)
954{
955// Rich set of comparison objects; only equals is defined.
956 if (op != Py_EQ)
957 return PyType_Type.tp_richcompare((PyObject*)self, (PyObject*)other, op);
958
959// defined by type + (shared) MethodInfo + bound self, with special case for
960// fSelf (i.e. pseudo-function)
961 if ((Py_TYPE(self) == Py_TYPE(other) && self->fMethodInfo == other->fMethodInfo) && \
962 ((IsPseudoFunc(self) && IsPseudoFunc(other)) || self->fSelf == other->fSelf)) {
964 }
966}
967
968
969//= CPyCppyy method proxy access to internals ================================
970static PyObject* mp_overload(CPPOverload* pymeth, PyObject* args)
971{
972// Select and call a specific C++ overload, based on its signature.
973 const char* sigarg = nullptr;
974 PyObject* sigarg_tuple = nullptr;
975 int want_const = -1;
977 if (args_size &&
978 PyArg_ParseTuple(args, const_cast<char*>("s|i:__overload__"), &sigarg, &want_const)) {
979 want_const = args_size == 1 ? -1 : want_const;
980 return pymeth->FindOverload(sigarg ? sigarg : "", want_const);
981 } else if (args_size &&
982 PyArg_ParseTuple(args, const_cast<char*>("O|i:__overload__"), &sigarg_tuple, &want_const)) {
983 PyErr_Clear();
984 want_const = args_size == 1 ? -1 : want_const;
985 return pymeth->FindOverload(sigarg_tuple, want_const);
986 } else {
987 PyErr_Format(PyExc_TypeError, "Unexpected arguments to __overload__");
988 return nullptr;
989 }
990}
991
993{
994 TPythonCallback* cb = new TPythonCallback(new_overload);
995 pymeth->AdoptMethod(cb);
997}
998
999static PyObject* mp_reflex(CPPOverload* pymeth, PyObject* args)
1000{
1001// Provide the requested reflection information.
1002 Cppyy::Reflex::RequestId_t request = -1;
1004 if (!PyArg_ParseTuple(args, const_cast<char*>("i|i:__cpp_reflex__"), &request, &format))
1005 return nullptr;
1006
1007 return pymeth->fMethodInfo->fMethods[0]->Reflex(request, format);
1008}
1009
1010//----------------------------------------------------------------------------
1011static PyMethodDef mp_methods[] = {
1012 {(char*)"__overload__", (PyCFunction)mp_overload, METH_VARARGS,
1013 (char*)"select overload for dispatch" },
1014 {(char*)"__add_overload__", (PyCFunction)mp_add_overload, METH_O,
1015 (char*)"add a new overload" },
1016 {(char*)"__cpp_reflex__", (PyCFunction)mp_reflex, METH_VARARGS,
1017 (char*)"C++ overload reflection information" },
1018 {(char*)nullptr, nullptr, 0, nullptr }
1019};
1020
1021} // unnamed namespace
1022
1023
1024//= CPyCppyy method proxy type ===============================================
1027 (char*)"cppyy.CPPOverload", // tp_name
1028 sizeof(CPPOverload), // tp_basicsize
1029 0, // tp_itemsize
1030 (destructor)mp_dealloc, // tp_dealloc
1031#if PY_VERSION_HEX >= 0x03080000
1033#else
1034 0, // tp_vectorcall_offset / tp_print
1035#endif
1036 0, // tp_getattr
1037 0, // tp_setattr
1038 0, // tp_as_async / tp_compare
1039 0, // tp_repr
1040 0, // tp_as_number
1041 0, // tp_as_sequence
1042 0, // tp_as_mapping
1043 (hashfunc)mp_hash, // tp_hash
1044#if PY_VERSION_HEX >= 0x03080000
1045 (ternaryfunc)PyVectorcall_Call, // tp_call
1046#else
1047 (ternaryfunc)mp_call, // tp_call
1048#endif
1049 (reprfunc)mp_str, // tp_str
1050 0, // tp_getattro
1051 0, // tp_setattro
1052 0, // tp_as_buffer
1054#if PY_VERSION_HEX >= 0x03080000
1056#endif
1057 , // tp_flags
1058 (char*)"cppyy method proxy (internal)", // tp_doc
1059 (traverseproc)mp_traverse, // tp_traverse
1060 (inquiry)mp_clear, // tp_clear
1061 (richcmpfunc)mp_richcompare, // tp_richcompare
1062 0, // tp_weaklistoffset
1063 0, // tp_iter
1064 0, // tp_iternext
1065 mp_methods, // tp_methods
1066 0, // tp_members
1067 mp_getset, // tp_getset
1068 0, // tp_base
1069 0, // tp_dict
1070 (descrgetfunc)mp_descr_get, // tp_descr_get
1071 0, // tp_descr_set
1072 0, // tp_dictoffset
1073 0, // tp_init
1074 0, // tp_alloc
1075 (newfunc)mp_new, // tp_new
1076 0, // tp_free
1077 0, // tp_is_gc
1078 0, // tp_bases
1079 0, // tp_mro
1080 0, // tp_cache
1081 0, // tp_subclasses
1082 0 // tp_weaklist
1083#if PY_VERSION_HEX >= 0x02030000
1084 , 0 // tp_del
1085#endif
1086#if PY_VERSION_HEX >= 0x02060000
1087 , 0 // tp_version_tag
1088#endif
1089#if PY_VERSION_HEX >= 0x03040000
1090 , 0 // tp_finalize
1091#endif
1092#if PY_VERSION_HEX >= 0x03080000
1093 , 0 // tp_vectorcall
1094#endif
1095#if PY_VERSION_HEX >= 0x030c0000
1096 , 0 // tp_watched
1097#endif
1098#if PY_VERSION_HEX >= 0x030d0000
1099 , 0 // tp_versions_used
1100#endif
1101};
1102
1103} // namespace CPyCppyy
1104
1105
1106//- public members -----------------------------------------------------------
1107void CPyCppyy::CPPOverload::Set(const std::string& name, std::vector<PyCallable*>& methods)
1108{
1109// Fill in the data of a freshly created method proxy.
1112 fMethodInfo->fFlags &= ~CallContext::kIsSorted;
1113
1114// special case: all constructors are considered creators by default
1115 if (name == "__init__")
1117
1118// special case, in heuristics mode also tag *Clone* methods as creators. Only
1119// check that Clone is present in the method name, not in the template argument
1120// list.
1122 std::string_view name_maybe_template = name;
1123 auto begin_template = name_maybe_template.find_first_of('<');
1124 if (begin_template <= name_maybe_template.size()) {
1126 }
1127 if (name_maybe_template.find("Clone") != std::string_view::npos) {
1129 }
1130 }
1131
1132#if PY_VERSION_HEX >= 0x03080000
1134#endif
1135}
1136
1137//----------------------------------------------------------------------------
1139{
1140// Fill in the data of a freshly created method proxy.
1141 fMethodInfo->fMethods.push_back(pc);
1142 fMethodInfo->fFlags &= ~CallContext::kIsSorted;
1143}
1144
1145//----------------------------------------------------------------------------
1147{
1148 if (!HasMethods()) // if fresh method being filled: also copy flags
1149 fMethodInfo->fFlags = meth->fMethodInfo->fFlags;
1150 fMethodInfo->fMethods.insert(fMethodInfo->fMethods.end(),
1151 meth->fMethodInfo->fMethods.begin(), meth->fMethodInfo->fMethods.end());
1152 fMethodInfo->fFlags &= ~CallContext::kIsSorted;
1153 meth->fMethodInfo->fDispatchMap.clear();
1154 meth->fMethodInfo->fMethods.clear();
1155}
1156
1157//----------------------------------------------------------------------------
1159{
1160 bool accept_any = signature == ":any:";
1161 CPPOverload* newmeth = nullptr;
1162
1163 std::string sig1{"("};
1164 if (!accept_any) {
1165 sig1.append(signature); sig1.append(")");
1166 sig1.erase(std::remove(sig1.begin(), sig1.end(), ' '), std::end(sig1));
1167 }
1168
1169 CPPOverload::Methods_t& methods = fMethodInfo->fMethods;
1170 for (auto& meth : methods) {
1171 bool found = accept_any;
1172 if (!found) {
1173 PyObject* pysig2 = meth->GetSignature(false);
1174 std::string sig2(CPyCppyy_PyText_AsString(pysig2));
1175 sig2.erase(std::remove(sig2.begin(), sig2.end(), ' '), std::end(sig2));
1177 if (sig1 == sig2) found = true;
1178
1179 if (!found) {
1180 pysig2 = meth->GetSignature(true);
1181 std::string sig3(CPyCppyy_PyText_AsString(pysig2));
1182 sig3.erase(std::remove(sig3.begin(), sig3.end(), ' '), std::end(sig3));
1184 if (sig1 == sig3) found = true;
1185 }
1186 }
1187
1188 if (found && 0 <= want_const) {
1189 bool isconst = meth->IsConst();
1190 if (!((want_const && isconst) || (!want_const && !isconst)))
1191 found = false;
1192 }
1193
1194 if (found) {
1195 if (!newmeth) {
1196 newmeth = mp_new(nullptr, nullptr, nullptr);
1197 CPPOverload::Methods_t vec; vec.push_back(meth->Clone());
1198 newmeth->Set(fMethodInfo->fName, vec);
1199
1200 if (fSelf) {
1201 Py_INCREF(fSelf);
1202 newmeth->fSelf = fSelf;
1203 }
1204 newmeth->fMethodInfo->fFlags = fMethodInfo->fFlags;
1205 } else
1206 newmeth->AdoptMethod(meth->Clone());
1207
1208 if (!accept_any)
1209 return (PyObject*)newmeth;
1210 }
1211 }
1212
1213 if (!newmeth)
1214 PyErr_Format(PyExc_LookupError, "signature \"%s\" not found", signature.c_str());
1215
1216 return (PyObject*)newmeth;
1217}
1218
1220{
1222
1223 CPPOverload::Methods_t& methods = fMethodInfo->fMethods;
1224
1225 // This value is set based on the maximum penalty in Cppyy::CompareMethodArgType
1227 bool found = false;
1228 size_t best_method = 0, method_index = 0;
1229
1230 for (auto& meth : methods) {
1231 if (0 <= want_const) {
1232 bool isconst = meth->IsConst();
1233 if (!((want_const && isconst) || (!want_const && !isconst)))
1234 continue;
1235 }
1236
1237 int score = meth->GetArgMatchScore(args_tuple);
1238
1239 if (score < min_score) {
1240 found = true;
1241 min_score = score;
1243 }
1244
1245 method_index++;
1246 }
1247
1248 if (!found) {
1249 std::string sigargs("(");
1250
1251 for (int i = 0; i < n; i++) {
1254 PyErr_Format(PyExc_LookupError, "argument types should be in string format");
1255 return (PyObject*) nullptr;
1256 }
1258 sigargs += arg_type + ", ";
1259 }
1260 sigargs += ")";
1261
1262 PyErr_Format(PyExc_LookupError, "signature with arguments \"%s\" not found", sigargs.c_str());
1263 return (PyObject*) nullptr;
1264 }
1265
1266 CPPOverload* newmeth = mp_new(nullptr, nullptr, nullptr);
1268 vec.push_back(methods[best_method]->Clone());
1269 newmeth->Set(fMethodInfo->fName, vec);
1270
1271 if (fSelf) {
1272 Py_INCREF(fSelf);
1273 newmeth->fSelf = fSelf;
1274 }
1275 newmeth->fMethodInfo->fFlags = fMethodInfo->fFlags;
1276
1277 return (PyObject*) newmeth;
1278}
1279
1280//----------------------------------------------------------------------------
1282{
1283// Destructor (this object is reference counted).
1284 for (Methods_t::iterator it = fMethods.begin(); it != fMethods.end(); ++it) {
1285 delete *it;
1286 }
1287 fMethods.clear();
1288 delete fRefCount;
1289 Py_XDECREF(fDoc);
1290}
1291
1292// TODO: something like PyMethod_Fini to clear up the free_list
#define CPyCppyy_Py3_UNUSED(name)
#define CPPOverload_MAXFREELIST
#define CO_NOFREE
#define CPPYY_BOOLEAN_PROPERTY(name, flag, label)
PyObject * fCallable
#define Py_TYPE(ob)
Definition CPyCppyy.h:196
#define Py_RETURN_TRUE
Definition CPyCppyy.h:272
#define Py_RETURN_FALSE
Definition CPyCppyy.h:276
int Py_ssize_t
Definition CPyCppyy.h:215
#define CPyCppyy_PyText_Append
Definition CPyCppyy.h:83
#define CPyCppyy_PyText_AsString
Definition CPyCppyy.h:76
PyObject * CPyCppyy_PyArgs_t
Definition CPyCppyy.h:330
#define CPyCppyy_PyText_AppendAndDel
Definition CPyCppyy.h:84
PyObject * CPyCppyy_PyObject_Call(PyObject *cb, PyObject *args, size_t, PyObject *kwds)
Definition CPyCppyy.h:346
#define CPyCppyy_PyText_FromFormat
Definition CPyCppyy.h:80
#define Py_RETURN_NONE
Definition CPyCppyy.h:268
#define CPyCppyy_PyText_FromString
Definition CPyCppyy.h:81
#define CPyCppyy_PyText_Check
Definition CPyCppyy.h:74
#define PyVarObject_HEAD_INIT(type, size)
Definition CPyCppyy.h:194
_object PyObject
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t format
char name[80]
Definition TGX11.cxx:110
void MergeOverload(CPPOverload *meth)
void AdoptMethod(PyCallable *pc)
MethodInfo_t * fMethodInfo
Definition CPPOverload.h:79
PyObject * FindOverload(const std::string &signature, int want_const=-1)
std::vector< PyCallable * > Methods_t
Definition CPPOverload.h:44
void Set(const std::string &name, std::vector< PyCallable * > &methods)
const_iterator begin() const
const_iterator end() const
const Int_t n
Definition legend1.C:16
PyObject * gLifeLine
Definition PyStrings.cxx:29
PyObject * gThisModule
Definition PyStrings.cxx:67
void SetDetailedException(std::vector< PyError_t > &&errors, PyObject *topmsg, PyObject *defexc)
Definition Utility.cxx:1191
size_t FetchError(std::vector< PyError_t > &, bool is_cpp=false)
Definition Utility.cxx:1180
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)
uint64_t HashSignature(CPyCppyy_PyArgs_t args, size_t nargsf)
Definition CPPOverload.h:17
bool CPPInstance_Check(T *object)
PyTypeObject CPPOverload_Type
bool IsConstructor(uint64_t flags)
bool IsSorted(uint64_t flags)
const FormatId_t OPTIMAL
Definition Reflex.h:22
RPY_EXPORTED size_t SizeOf(TCppType_t klass)
RPY_EXPORTED TCppFuncAddr_t GetFunctionAddress(TCppMethod_t method, bool check_enabled=true)
void * TCppFuncAddr_t
Definition cpp_cppyy.h:41
CPPOverload::Methods_t fMethods
Definition CPPOverload.h:53
static uint32_t & GlobalPolicyFlags()