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