Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
CPPMethod.cxx
Go to the documentation of this file.
1// Bindings
2#include "CPyCppyy.h"
3#include "CPPMethod.h"
4#include "CPPExcInstance.h"
5#include "CPPInstance.h"
6#include "Converters.h"
7#include "Executors.h"
8#include "ProxyWrappers.h"
9#include "PyStrings.h"
10#include "TypeManip.h"
11#include "SignalTryCatch.h"
12#include "Utility.h"
13
15
16// Standard
17#include <algorithm>
18#include <assert.h>
19#include <string.h>
20#include <exception>
21#include <iostream>
22#include <sstream>
23#include <string>
24#include <typeinfo>
25#include <memory>
26
27
28//- data and local helpers ---------------------------------------------------
29namespace CPyCppyy {
31 extern PyObject* gBusException;
33 extern PyObject* gIllException;
35}
36
37
38//- public helper ------------------------------------------------------------
40 if (fFlags & kSelfSwap) // if self swap, fArgs has been offset by -1
41 std::swap((PyObject*&)fSelf, ((PyObject**)fArgs)[0]);
42
43#if PY_VERSION_HEX >= 0x03080000
44 if (fFlags & kIsOffset) fArgs -= 1;
45
46 if (fFlags & kDoItemDecref) {
47 for (Py_ssize_t iarg = 0; iarg < CPyCppyy_PyArgs_GET_SIZE(fArgs, fNArgsf); ++iarg)
48 Py_DECREF(fArgs[iarg]);
49 }
50
51 if (fFlags & kDoFree)
52 PyMem_Free((void*)fArgs);
53 else if (fFlags & kArgsSwap) {
54 // if self swap, fArgs has been offset by -1
55 int offset = (fFlags & kSelfSwap) ? 1 : 0;
56 std::swap(((PyObject**)fArgs+offset)[0], ((PyObject**)fArgs+offset)[1]);
57 }
58#else
59 if (fFlags & kDoDecref)
60 Py_DECREF((PyObject*)fArgs);
61 else if (fFlags & kArgsSwap)
62 std::swap(PyTuple_GET_ITEM(fArgs, 0), PyTuple_GET_ITEM(fArgs, 1));
63#endif
64}
65
66
67//- private helpers ----------------------------------------------------------
69{
70// actual number of arguments must be between required and max args
71 Py_ssize_t maxargs = (Py_ssize_t)fConverters.size();
72
73 if (maxargs != actual) {
74 if (actual < (Py_ssize_t)fArgsRequired) {
76 "takes at least %d arguments (%zd given)", fArgsRequired, actual));
77 return false;
78 } else if (maxargs < actual) {
80 "takes at most %zd arguments (%zd given)", maxargs, actual));
81 return false;
82 }
83 }
84 return true;
85}
86
87//----------------------------------------------------------------------------
88inline void CPyCppyy::CPPMethod::Copy_(const CPPMethod& /* other */)
89{
90// fScope and fMethod handled separately
91
92// do not copy caches
93 fExecutor = nullptr;
94 fArgIndices = nullptr;
95 fArgsRequired = -1;
96}
97
98//----------------------------------------------------------------------------
100{
101// destroy executor and argument converters
102 if (fExecutor && fExecutor->HasState()) delete fExecutor;
103 fExecutor = nullptr;
104
105 for (auto p : fConverters) {
106 if (p && p->HasState()) delete p;
107 }
108 fConverters.clear();
109
110 delete fArgIndices; fArgIndices = nullptr;
111 fArgsRequired = -1;
112}
113
114//----------------------------------------------------------------------------
116 void* self, ptrdiff_t offset, CallContext* ctxt)
117{
118// call into C++ through fExecutor; abstracted out from Execute() to prevent some
119// code duplication with ProtectedCall()
120 PyObject* result = nullptr;
121
122 try { // C++ try block
123 result = fExecutor->Execute(fMethod, (Cppyy::TCppObject_t)((intptr_t)self+offset), ctxt);
124 } catch (PyException&) {
126 result = nullptr; // error already set
127 } catch (std::exception& e) {
128 // attempt to set the exception to the actual type, to allow catching with the Python C++ type
129 static Cppyy::TCppType_t exc_type = (Cppyy::TCppType_t)Cppyy::GetScope("std::exception");
130
132
133 PyObject* pyexc_type = nullptr;
134 PyObject* pyexc_obj = nullptr;
135
136 // TODO: factor this code with the same in ProxyWrappers (and cache it there to be able to
137 // look up based on TCppType_t):
138 Cppyy::TCppType_t actual = Cppyy::GetActualClass(exc_type, &e);
139 const std::string& finalname = Cppyy::GetScopedFinalName(actual);
140 const std::string& parentname = TypeManip::extract_namespace(finalname);
141 PyObject* parent = CreateScopeProxy(parentname);
142 if (parent) {
143 pyexc_type = PyObject_GetAttrString(parent,
144 parentname.empty() ? finalname.c_str() : finalname.substr(parentname.size()+2, std::string::npos).c_str());
145 Py_DECREF(parent);
146 }
147
148 if (pyexc_type) {
149 // create a copy of the exception (TODO: factor this code with the same in ProxyWrappers)
150 PyObject* pyclass = CPyCppyy::GetScopeProxy(actual);
151 PyObject* source = BindCppObjectNoCast(&e, actual);
152 PyObject* pyexc_copy = PyObject_CallFunctionObjArgs(pyclass, source, nullptr);
153 Py_DECREF(source);
154 Py_DECREF(pyclass);
155 if (pyexc_copy) {
156 pyexc_obj = CPPExcInstance_Type.tp_new((PyTypeObject*)pyexc_type, nullptr, nullptr);
157 ((CPPExcInstance*)pyexc_obj)->fCppInstance = (PyObject*)pyexc_copy;
158 } else
159 PyErr_Clear();
160 } else
161 PyErr_Clear();
162
163 if (pyexc_type && pyexc_obj) {
164 PyErr_SetObject(pyexc_type, pyexc_obj);
165 Py_DECREF(pyexc_obj);
166 Py_DECREF(pyexc_type);
167 } else {
168 PyErr_Format(PyExc_Exception, "%s (C++ exception)", e.what());
169 Py_XDECREF(pyexc_obj);
170 Py_XDECREF(pyexc_type);
171 }
172
173 result = nullptr;
174 } catch (...) {
175 // don't set the kCppException flag here, as there is basically no useful
176 // extra information to be had and caller has to catch Exception either way
177 PyErr_SetString(PyExc_Exception, "unhandled, unknown C++ exception");
178 result = nullptr;
179 }
180
181// TODO: covers the PyException throw case, which does not seem to work on Windows, so
182// instead leaves the error be
183#ifdef _WIN32
184 if (PyErr_Occurred()) {
185 Py_XDECREF(result);
186 result = nullptr;
187 }
188#endif
189
190 return result;
191}
192
193//----------------------------------------------------------------------------
195 void* self, ptrdiff_t offset, CallContext* ctxt)
196{
197// helper code to prevent some code duplication; this code embeds a "try/catch"
198// block that saves the call environment for restoration in case of an otherwise
199// fatal signal
200 PyObject* result = 0;
201
202 CLING_EXCEPTION_TRY { // copy call environment to be able to jump back on signal
203 result = ExecuteFast(self, offset, ctxt);
204 } CLING_EXCEPTION_CATCH(excode) {
205 // report any outstanding Python exceptions first
206 if (PyErr_Occurred()) {
207 std::cerr << "Python exception outstanding during C++ longjmp:" << std::endl;
208 PyErr_Print();
209 std::cerr << std::endl;
210 }
211
212 // unfortunately, the excodes are not the ones from signal.h, but enums from TSysEvtHandler.h
213 if (excode == 0)
214 PyErr_SetString(gBusException, "bus error in C++; program state was reset");
215 else if (excode == 1)
216 PyErr_SetString(gSegvException, "segfault in C++; program state was reset");
217 else if (excode == 4)
218 PyErr_SetString(gIllException, "illegal instruction in C++; program state was reset");
219 else if (excode == 5)
220 PyErr_SetString(gAbrtException, "abort from C++; program state was reset");
221 else if (excode == 12)
222 PyErr_SetString(PyExc_FloatingPointError, "floating point exception in C++; program state was reset");
223 else
224 PyErr_SetString(PyExc_SystemError, "problem in C++; program state was reset");
225 result = 0;
227
228 return result;
229}
230
231//----------------------------------------------------------------------------
233{
234// build buffers for argument dispatching
235 const size_t nArgs = Cppyy::GetMethodNumArgs(fMethod);
236 fConverters.resize(nArgs);
237
238// setup the dispatch cache
239 for (int iarg = 0; iarg < (int)nArgs; ++iarg) {
240 const std::string& fullType = Cppyy::GetMethodArgType(fMethod, iarg);
241 Converter* conv = CreateConverter(fullType);
242 if (!conv) {
243 PyErr_Format(PyExc_TypeError, "argument type %s not handled", fullType.c_str());
244 return false;
245 }
246
247 fConverters[iarg] = conv;
248 }
249
250 return true;
251}
252
253//----------------------------------------------------------------------------
255{
256// install executor conform to the return type
257 executor = CreateExecutor(
258 (bool)fMethod == true ? Cppyy::GetMethodResultType(fMethod) \
259 : Cppyy::GetScopedFinalName(fScope));
260
261 if (!executor)
262 return false;
263
264 return true;
265}
266
267//----------------------------------------------------------------------------
269{
270// built a signature representation (used for doc strings)
271 return Cppyy::GetMethodSignature(fMethod, fa);
272}
273
274//----------------------------------------------------------------------------
276{
277// helper to report errors in a consistent format (derefs msg)
278 std::string details{};
279
280 PyObject *etype = nullptr, *evalue = nullptr;
281 if (PyErr_Occurred()) {
282 PyObject* etrace = nullptr;
283
284 PyErr_Fetch(&etype, &evalue, &etrace);
285
286 if (evalue) {
287 PyObject* descr = PyObject_Str(evalue);
288 if (descr) {
289 details = CPyCppyy_PyText_AsString(descr);
290 Py_DECREF(descr);
291 }
292 }
293
294 Py_XDECREF(etrace);
295 }
296
297 PyObject* doc = GetDocString();
298 PyObject* errtype = etype;
299 if (!errtype)
300 errtype = PyExc_TypeError;
301 PyObject* pyname = PyObject_GetAttr(errtype, PyStrings::gName);
302 const char* cname = pyname ? CPyCppyy_PyText_AsString(pyname) : "Exception";
303
304 if (!PyType_IsSubtype((PyTypeObject*)errtype, &CPPExcInstance_Type)) {
305 if (details.empty()) {
306 PyErr_Format(errtype, "%s =>\n %s: %s", CPyCppyy_PyText_AsString(doc),
307 cname, msg ? CPyCppyy_PyText_AsString(msg) : "");
308 } else if (msg) {
309 PyErr_Format(errtype, "%s =>\n %s: %s (%s)",
311 details.c_str());
312 } else {
313 PyErr_Format(errtype, "%s =>\n %s: %s",
314 CPyCppyy_PyText_AsString(doc), cname, details.c_str());
315 }
316 } else {
317 Py_XDECREF(((CPPExcInstance*)evalue)->fTopMessage);
318 if (msg) {
319 ((CPPExcInstance*)evalue)->fTopMessage = CPyCppyy_PyText_FromFormat(\
320 "%s =>\n %s: %s | ", CPyCppyy_PyText_AsString(doc), cname, CPyCppyy_PyText_AsString(msg));
321 } else {
322 ((CPPExcInstance*)evalue)->fTopMessage = CPyCppyy_PyText_FromFormat(\
323 "%s =>\n %s: ", CPyCppyy_PyText_AsString(doc), cname);
324 }
325 PyErr_SetObject(errtype, evalue);
326 }
327
328 Py_XDECREF(pyname);
329 Py_XDECREF(evalue);
330 Py_XDECREF(etype);
331 Py_DECREF(doc);
332 Py_XDECREF(msg);
333}
334
335//- constructors and destructor ----------------------------------------------
338 fMethod(method), fScope(scope), fExecutor(nullptr), fArgIndices(nullptr),
339 fArgsRequired(-1)
340{
341 // empty
342}
343
344//----------------------------------------------------------------------------
346 PyCallable(other), fMethod(other.fMethod), fScope(other.fScope)
347{
348 Copy_(other);
349}
350
351//----------------------------------------------------------------------------
353{
354 if (this != &other) {
355 Destroy_();
356 Copy_(other);
357 fScope = other.fScope;
358 fMethod = other.fMethod;
359 }
360
361 return *this;
362}
363
364//----------------------------------------------------------------------------
366{
367 Destroy_();
368}
369
370
371//- public members -----------------------------------------------------------
372/**
373 * @brief Construct a Python string from the method's prototype
374 *
375 * @param fa Show formal arguments of the method
376 * @return PyObject* A Python string with the full method prototype, namespaces included.
377 *
378 * For example, given:
379 *
380 * int foo(int x);
381 *
382 * namespace a {
383 * namespace b {
384 * namespace c {
385 * int foo(int x);
386 * }}}
387 *
388 * This function returns:
389 *
390 * 'int foo(int x)'
391 * 'int a::b::c::foo(int x)'
392 */
394{
395 // Gather the fully qualified final scope of the method. This includes
396 // all namespaces up to the one where the method is declared, for example:
397 // namespace a { namespace b { void foo(); }}
398 // gives
399 // a::b
400 std::string finalscope = Cppyy::GetScopedFinalName(fScope);
401 return CPyCppyy_PyText_FromFormat("%s%s %s%s%s%s",
402 (Cppyy::IsStaticMethod(fMethod) ? "static " : ""),
403 Cppyy::GetMethodResultType(fMethod).c_str(),
404 finalscope.c_str(),
405 (finalscope.empty() ? "" : "::"), // Add final set of '::' if the method is scoped in namespace(s)
406 Cppyy::GetMethodName(fMethod).c_str(),
407 GetSignatureString(fa).c_str());
408}
409
410//----------------------------------------------------------------------------
412{
413 PyObject* cppname = CPyCppyy_PyText_FromString((GetReturnTypeName() + " (*)").c_str());
414 CPyCppyy_PyText_AppendAndDel(&cppname, GetSignature(false /* show_formalargs */));
415 return cppname;
416}
417
418//----------------------------------------------------------------------------
420{
421// C++ reflection tooling for methods.
422
423 if (request == Cppyy::Reflex::RETURN_TYPE) {
424 std::string rtn = GetReturnTypeName();
425 Cppyy::TCppScope_t scope = 0;
427 scope = Cppyy::GetScope(rtn);
428
430 return CPyCppyy_PyText_FromString(rtn.c_str());
432 if (scope) return CreateScopeProxy(scope);
433 /* TODO: builtins as type */
434 }
435 }
436
437 return PyCallable::Reflex(request, format);
438}
439
440//----------------------------------------------------------------------------
442{
443// To help with overload selection, methods are given a priority based on the
444// affinity of Python and C++ types. Priority only matters for methods that have
445// an equal number of arguments and types that are possible substitutes (the
446// normal selection mechanisms would simply distinguish them otherwise).
447
448// The following types are ordered, in favor (variants implicit):
449//
450// bool >> long >> int >> short
451// double >> long double >> float
452// const char* >> char
453//
454// Further, all integer types are preferred over floating point b/c int to float
455// is allowed implicitly, float to int is not.
456//
457// Special cases that are disliked include void* and unknown/incomplete types.
458// Also, moves are preferred over references. std::initializer_list is not a nice
459// conversion candidate either, but needs to be higher priority to mix well with
460// implicit conversions.
461// TODO: extend this to favour classes that are not bases.
462// TODO: profile this method (it's expensive, but should be called too often)
463
464 int priority = 0;
465
466 const size_t nArgs = Cppyy::GetMethodNumArgs(fMethod);
467 for (int iarg = 0; iarg < (int)nArgs; ++iarg) {
468 const std::string aname = Cppyy::GetMethodArgType(fMethod, iarg);
469
470 if (Cppyy::IsBuiltin(aname)) {
471 // complex type (note: double penalty: for complex and the template type)
472 if (strstr(aname.c_str(), "std::complex"))
473 priority -= 10; // prefer double, float, etc. over conversion
474
475 // integer types
476 if (strstr(aname.c_str(), "bool"))
477 priority += 1; // bool over int (does accept 1 and 0)
478 else if (strstr(aname.c_str(), "long long"))
479 priority += -5; // will very likely fit
480 else if (strstr(aname.c_str(), "long"))
481 priority += -10; // most affine integer type
482 // no need to compare with int; leave at zero
483 else if (strstr(aname.c_str(), "short"))
484 priority += -50; // not really relevant as a type
485
486 // floating point types (note all numbers lower than integer types)
487 else if (strstr(aname.c_str(), "float"))
488 priority += -100; // not really relevant as a type
489 else if (strstr(aname.c_str(), "long double"))
490 priority += -90; // fits double with least loss of precision
491 else if (strstr(aname.c_str(), "double"))
492 priority += -80; // most affine floating point type
493
494 // string/char types
495 else if (strstr(aname.c_str(), "char") && aname[aname.size()-1] != '*')
496 priority += -60; // prefer (const) char* over char
497
498 // oddball
499 else if (strstr(aname.c_str(), "void*"))
500 priority -= 1000; // void*/void** shouldn't be too greedy
501
502 } else {
503 // This is a user-defined type (class, struct, enum, etc.).
504
505 // There's a bit of hysteresis here for templates: once GetScope() is called, their
506 // IsComplete() succeeds, the other way around it does not. Since GetPriority() is
507 // likely called several times in a sort, the GetScope() _must_ come first, or
508 // different GetPriority() calls may return different results (since the 2nd time,
509 // GetScope() will have been called from the first), killing the stable_sort.
510
511 // prefer more derived classes
512 const std::string& clean_name = TypeManip::clean_type(aname, false);
513 Cppyy::TCppScope_t scope = Cppyy::GetScope(clean_name);
514 if (scope)
515 priority += static_cast<int>(Cppyy::GetNumBasesLongestBranch(scope));
516
517 if (Cppyy::IsEnum(clean_name))
518 priority -= 100;
519
520 // a couple of special cases as explained above
521 if (aname.find("initializer_list") != std::string::npos) {
522 priority += 150; // needed for proper implicit conversion rules
523 } else if (aname.rfind("&&", aname.size()-2) != std::string::npos) {
524 priority += 100; // prefer moves over other ref/ptr
525 } else if (scope && !Cppyy::IsComplete(clean_name)) {
526 // class is known, but no dictionary available, 2 more cases: * and &
527 if (aname[aname.size() - 1] == '&')
528 priority += -5000;
529 else
530 priority += -2000; // prefer pointer passing over reference
531 }
532 }
533 }
534
535// prefer methods w/o optional arguments b/c ones with optional arguments are easier to
536// select by providing the optional arguments explicitly
537 priority += ((int)Cppyy::GetMethodReqArgs(fMethod) - (int)nArgs);
538
539// add a small penalty to prefer non-const methods over const ones for get/setitem
540 if (Cppyy::IsConstMethod(fMethod) && Cppyy::GetMethodName(fMethod) == "operator[]")
541 priority += -10;
542
543 return priority;
544}
545
546//----------------------------------------------------------------------------
548{
549// Methods will all void*-like arguments should be sorted after template
550// instanstations, so that they don't greedily take over pointers to object.
551// GetPriority() is too heavy-handed, as it will pull in all the argument
552// types, so use this cheaper check.
553 const size_t nArgs = Cppyy::GetMethodReqArgs(fMethod);
554 if (!nArgs) return false;
555
556 for (int iarg = 0; iarg < (int)nArgs; ++iarg) {
557 const std::string aname = Cppyy::GetMethodArgType(fMethod, iarg);
558 if (aname.find("void*") != 0)
559 return false;
560 }
561 return true;
562}
563
564
565//----------------------------------------------------------------------------
567{
568 return (int)Cppyy::GetMethodNumArgs(fMethod);
569}
570
571//----------------------------------------------------------------------------
573{
574// Build a tuple of the argument types/names.
575 int co_argcount = (int)GetMaxArgs() /* +1 for self */;
576
577// TODO: static methods need no 'self' (but is harmless otherwise)
578
579 PyObject* co_varnames = PyTuple_New(co_argcount+1 /* self */);
580 PyTuple_SET_ITEM(co_varnames, 0, CPyCppyy_PyText_FromString("self"));
581 for (int iarg = 0; iarg < co_argcount; ++iarg) {
582 std::string argrep = Cppyy::GetMethodArgType(fMethod, iarg);
583 const std::string& parname = Cppyy::GetMethodArgName(fMethod, iarg);
584 if (!parname.empty()) {
585 argrep += " ";
586 argrep += parname;
587 }
588
589 PyObject* pyspec = CPyCppyy_PyText_FromString(argrep.c_str());
590 PyTuple_SET_ITEM(co_varnames, iarg+1, pyspec);
591 }
592
593 return co_varnames;
594}
595
597{
598// get and evaluate the default value (if any) of argument iarg of this method
599 if (iarg >= (int)GetMaxArgs())
600 return nullptr;
601
602// borrowed reference to cppyy.gbl module to use its dictionary to eval in
603 static PyObject* gbl = PyDict_GetItemString(PySys_GetObject((char*)"modules"), "cppyy.gbl");
604
605 std::string defvalue = Cppyy::GetMethodArgDefault(fMethod, iarg);
606 if (!defvalue.empty()) {
607 PyObject** dctptr = _PyObject_GetDictPtr(gbl);
608 if (!(dctptr && *dctptr))
609 return nullptr;
610
611 PyObject* gdct = *dctptr;
612 PyObject* scope = nullptr;
613
614 if (defvalue.find("::") != std::string::npos) {
615 // try to tickle scope creation, just in case
616 scope = CreateScopeProxy(defvalue.substr(0, defvalue.rfind('(')));
617 if (!scope) PyErr_Clear();
618
619 // rename '::' -> '.'
621 }
622
623 if (!scope) {
624 // a couple of common cases that python doesn't like (technically, 'L' is okay with older
625 // pythons, but C long will always fit in Python int, so no need to bother)
626 char c = defvalue.back();
627 if (c == 'F' || c == 'D' || c == 'L') {
628 int offset = 1;
629 if (2 < defvalue.size() && defvalue[defvalue.size()-2] == 'U')
630 offset = 2;
631 defvalue = defvalue.substr(0, defvalue.size()-offset);
632 }
633 }
634
635 // attempt to evaluate the string representation (compilation is first to code to allow
636 // the error message to indicate where it's coming from)
637 PyObject* pyval = nullptr;
638
639 PyObject* pycode = Py_CompileString((char*)defvalue.c_str(), "cppyy_default_compiler", Py_eval_input);
640 if (pycode) {
641 pyval = PyEval_EvalCode(
642#if PY_VERSION_HEX < 0x03000000
643 (PyCodeObject*)
644#endif
645 pycode, gdct, gdct);
646 Py_DECREF(pycode);
647 }
648
649 if (!pyval && PyErr_Occurred() && silent) {
650 PyErr_Clear();
651 pyval = CPyCppyy_PyText_FromString(defvalue.c_str()); // allows continuation, but is likely to fail
652 }
653
654 Py_XDECREF(scope);
655 return pyval; // may be nullptr
656 }
657
658 PyErr_Format(PyExc_TypeError, "Could not construct default value for: %s", Cppyy::GetMethodArgName(fMethod, iarg).c_str());
659 return nullptr;
660}
661
662
664 return Cppyy::IsConstMethod(GetMethod());
665}
666
667
668//----------------------------------------------------------------------------
670{
671// Get or build the scope of this method.
672 return CreateScopeProxy(fScope);
673}
674
675
676//----------------------------------------------------------------------------
678{
679// Return the C++ pointer of this function
680 return Cppyy::GetFunctionAddress(fMethod, false /* don't check fast path envar */);
681}
682
683//----------------------------------------------------------------------------
685{
686 Py_ssize_t n = PyTuple_Size(args_tuple);
687
688 int req_args = Cppyy::GetMethodReqArgs(fMethod);
689
690 // Not enough arguments supplied: no match
691 if (req_args > n)
692 return INT_MAX;
693
694 size_t score = 0;
695 for (int i = 0; i < n; i++) {
696 PyObject *pItem = PyTuple_GetItem(args_tuple, i);
697 if(!CPyCppyy_PyText_Check(pItem)) {
698 PyErr_SetString(PyExc_TypeError, "argument types should be in string format");
699 return INT_MAX;
700 }
701 std::string req_type(CPyCppyy_PyText_AsString(pItem));
702
703 size_t arg_score = Cppyy::CompareMethodArgType(fMethod, i, req_type);
704
705 // Method is not compatible if even one argument does not match
706 if (arg_score >= 10) {
707 score = INT_MAX;
708 break;
709 }
710
711 score += arg_score;
712 }
713
714 return score;
715}
716
717//----------------------------------------------------------------------------
719{
720// done if cache is already setup
721 if (fArgsRequired != -1)
722 return true;
723
724 if (!InitConverters_())
725 return false;
726
727 if (!InitExecutor_(fExecutor, ctxt))
728 return false;
729
730// minimum number of arguments when calling
731 fArgsRequired = (int)((bool)fMethod == true ? Cppyy::GetMethodReqArgs(fMethod) : 0);
732
733 return true;
734}
735
736//----------------------------------------------------------------------------
738{
739#if PY_VERSION_HEX >= 0x03080000
740 if (!PyTuple_CheckExact(cargs.fKwds)) {
741 SetPyError_(CPyCppyy_PyText_FromString("received unknown keyword names object"));
742 return false;
743 }
744 Py_ssize_t nKeys = PyTuple_GET_SIZE(cargs.fKwds);
745#else
746 if (!PyDict_CheckExact(cargs.fKwds)) {
747 SetPyError_(CPyCppyy_PyText_FromString("received unknown keyword arguments object"));
748 return false;
749 }
750 Py_ssize_t nKeys = PyDict_Size(cargs.fKwds);
751#endif
752
753 if (nKeys == 0 && !self_in)
754 return true;
755
756 if (!fArgIndices) {
757 fArgIndices = new std::map<std::string, int>{};
758 for (int iarg = 0; iarg < (int)Cppyy::GetMethodNumArgs(fMethod); ++iarg)
759 (*fArgIndices)[Cppyy::GetMethodArgName(fMethod, iarg)] = iarg;
760 }
761
762 Py_ssize_t nArgs = CPyCppyy_PyArgs_GET_SIZE(cargs.fArgs, cargs.fNArgsf) + (self_in ? 1 : 0);
763 if (!VerifyArgCount_(nArgs+nKeys))
764 return false;
765
766 std::vector<PyObject*> vArgs{fConverters.size()};
767
768// next, insert the keyword values
769 PyObject *key, *value;
770 Py_ssize_t maxpos = -1;
771
772#if PY_VERSION_HEX >= 0x03080000
773 Py_ssize_t npos_args = CPyCppyy_PyArgs_GET_SIZE(cargs.fArgs, cargs.fNArgsf);
774 for (Py_ssize_t ikey = 0; ikey < nKeys; ++ikey) {
775 key = PyTuple_GET_ITEM(cargs.fKwds, ikey);
776 value = cargs.fArgs[npos_args+ikey];
777#else
778 Py_ssize_t pos = 0;
779 while (PyDict_Next(cargs.fKwds, &pos, &key, &value)) {
780#endif
781 const char* ckey = CPyCppyy_PyText_AsStringChecked(key);
782 if (!ckey)
783 return false;
784
785 auto p = fArgIndices->find(ckey);
786 if (p == fArgIndices->end()) {
787 SetPyError_(CPyCppyy_PyText_FromFormat("%s::%s got an unexpected keyword argument \'%s\'",
788 Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetMethodName(fMethod).c_str(), ckey));
789 return false;
790 }
791
792 maxpos = p->second > maxpos ? p->second : maxpos;
793 vArgs[p->second] = value; // no INCREF yet for simple cleanup in case of error
794 }
795
796// if maxpos < nArgs, it will be detected & reported as a duplicate below
797 Py_ssize_t maxargs = maxpos + 1;
798 CPyCppyy_PyArgs_t newArgs = CPyCppyy_PyArgs_New(maxargs);
799
800// set all values to zero to be able to check them later (this also guarantees normal
801// cleanup by the tuple deallocation)
802 for (Py_ssize_t i = 0; i < maxargs; ++i)
803 CPyCppyy_PyArgs_SET_ITEM(newArgs, i, nullptr);
804
805// fill out the positional arguments
806 Py_ssize_t start = 0;
807 if (self_in) {
808 Py_INCREF(self_in);
809 CPyCppyy_PyArgs_SET_ITEM(newArgs, 0, self_in);
810 start = 1;
811 }
812
813 for (Py_ssize_t i = start; i < nArgs; ++i) {
814 if (vArgs[i]) {
815 SetPyError_(CPyCppyy_PyText_FromFormat("%s::%s got multiple values for argument %d",
816 Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetMethodName(fMethod).c_str(), (int)i+1));
817 CPyCppyy_PyArgs_DEL(newArgs);
818 return false;
819 }
820
821 PyObject* item = CPyCppyy_PyArgs_GET_ITEM(cargs.fArgs, i);
822 Py_INCREF(item);
823 CPyCppyy_PyArgs_SET_ITEM(newArgs, i, item);
824 }
825
826// fill out the keyword arguments
827 for (Py_ssize_t i = nArgs; i < maxargs; ++i) {
828 PyObject* item = vArgs[i];
829 if (item) {
830 Py_INCREF(item);
831 CPyCppyy_PyArgs_SET_ITEM(newArgs, i, item);
832 } else {
833 // try retrieving the default
834 item = GetArgDefault((int)i, false /* i.e. not silent */);
835 if (!item) {
836 CPyCppyy_PyArgs_DEL(newArgs);
837 return false;
838 }
839 CPyCppyy_PyArgs_SET_ITEM(newArgs, i, item);
840 }
841 }
842
843#if PY_VERSION_HEX >= 0x03080000
844 if (cargs.fFlags & PyCallArgs::kDoFree) {
845 if (cargs.fFlags & PyCallArgs::kIsOffset)
846 cargs.fArgs -= 1;
847#else
848 if (cargs.fFlags & PyCallArgs::kDoDecref) {
849#endif
851 }
852
853 cargs.fArgs = newArgs;
854 cargs.fNArgsf = maxargs;
855#if PY_VERSION_HEX >= 0x03080000
856 cargs.fFlags = PyCallArgs::kDoFree | PyCallArgs::kDoItemDecref;
857#else
859#endif
860
861 return true;
862}
863
864//----------------------------------------------------------------------------
866{
867// verify existence of self, return if ok
868 if (cargs.fSelf) {
869 if (cargs.fKwds) { return ProcessKwds(nullptr, cargs); }
870 return true;
871 }
872
873// otherwise, check for a suitable 'self' in args and update accordingly
874 if (CPyCppyy_PyArgs_GET_SIZE(cargs.fArgs, cargs.fNArgsf) != 0) {
876
877 // demand CPyCppyy object, and an argument that may match down the road
878 if (CPPInstance_Check(pyobj)) {
879 Cppyy::TCppType_t oisa = pyobj->ObjectIsA();
880 if (fScope == Cppyy::gGlobalScope || // free global
881 oisa == 0 || // null pointer or ctor call
882 oisa == fScope || // matching types
883 Cppyy::IsSubtype(oisa, fScope)) { // id.
884
885 // reset self
886 Py_INCREF(pyobj); // corresponding Py_DECREF is in CPPOverload
887 cargs.fSelf = pyobj;
888
889 // offset args by 1
890#if PY_VERSION_HEX >= 0x03080000
891 cargs.fArgs += 1;
893#else
894 if (cargs.fFlags & PyCallArgs::kDoDecref)
895 Py_DECREF((PyObject*)cargs.fArgs);
896 cargs.fArgs = PyTuple_GetSlice(cargs.fArgs, 1, PyTuple_GET_SIZE(cargs.fArgs));
898#endif
899 cargs.fNArgsf -= 1;
900
901 // put the keywords, if any, in their places in the arguments array
902 if (cargs.fKwds)
903 return ProcessKwds(nullptr, cargs);
904 return true;
905 }
906 }
907 }
908
909// no self, set error and lament
910 SetPyError_(CPyCppyy_PyText_FromFormat(
911 "unbound method %s::%s must be called with a %s instance as first argument",
912 Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetMethodName(fMethod).c_str(),
913 Cppyy::GetFinalName(fScope).c_str()));
914 return false;
915}
916
917//----------------------------------------------------------------------------
919{
920 Py_ssize_t argc = CPyCppyy_PyArgs_GET_SIZE(args, nargsf);
921 if (!VerifyArgCount_(argc))
922 return false;
923
924// pass current scope for which the call is made
925 ctxt->fCurScope = fScope;
926
927 if (argc == 0)
928 return true;
929
930// convert the arguments to the method call array
931 bool isOK = true;
932 Parameter* cppArgs = ctxt->GetArgs(argc);
933 for (int i = 0; i < (int)argc; ++i) {
934 if (!fConverters[i]->SetArg(CPyCppyy_PyArgs_GET_ITEM(args, i), cppArgs[i], ctxt)) {
935 SetPyError_(CPyCppyy_PyText_FromFormat("could not convert argument %d", i+1));
936 isOK = false;
937 break;
938 }
939 }
940
941 return isOK;
942}
943
944//----------------------------------------------------------------------------
946{
947// call the interface method
948 PyObject* result = 0;
949
951 !(ctxt->fFlags & CallContext::kProtected)) {
952 // bypasses try block (i.e. segfaults will abort)
953 result = ExecuteFast(self, offset, ctxt);
954 } else {
955 // at the cost of ~10% performance, don't abort the interpreter on any signal
956 result = ExecuteProtected(self, offset, ctxt);
957 }
958
959// TODO: the following is dreadfully slow and dead-locks on Apache: revisit
960// raising exceptions through callbacks by using magic returns
961// if (result && Utility::PyErr_Occurred_WithGIL()) {
962// // can happen in the case of a CINT error: trigger exception processing
963// Py_DECREF(result);
964// result = 0;
965// } else if (!result && PyErr_Occurred())
966 if (!result && PyErr_Occurred())
967 SetPyError_(0);
968
969 return result;
970}
971
972//----------------------------------------------------------------------------
974 CPyCppyy_PyArgs_t args, size_t nargsf, PyObject* kwds, CallContext* ctxt)
975{
976// setup as necessary
977 if (fArgsRequired == -1 && !Initialize(ctxt))
978 return nullptr;
979
980// fetch self, verify, and put the arguments in usable order
981 PyCallArgs cargs{self, args, nargsf, kwds};
982 if (!ProcessArgs(cargs))
983 return nullptr;
984
985// self provides the python context for lifelines
986 if (!ctxt->fPyContext)
987 ctxt->fPyContext = (PyObject*)cargs.fSelf; // no Py_INCREF as no ownership
988
989// translate the arguments
990 if (fArgsRequired || CPyCppyy_PyArgs_GET_SIZE(args, nargsf)) {
991 if (!ConvertAndSetArgs(cargs.fArgs, cargs.fNArgsf, ctxt))
992 return nullptr;
993 }
994
995// get the C++ object that this object proxy is a handle for
996 void* object = self->GetObject();
997
998// validity check that should not fail
999 if (!object) {
1000 PyErr_SetString(PyExc_ReferenceError, "attempt to access a null-pointer");
1001 return nullptr;
1002 }
1003
1004// get its class
1005 Cppyy::TCppType_t derived = self->ObjectIsA();
1006
1007// calculate offset (the method expects 'this' to be an object of fScope)
1008 ptrdiff_t offset = 0;
1009 if (derived && derived != fScope)
1010 offset = Cppyy::GetBaseOffset(derived, fScope, object, 1 /* up-cast */);
1011
1012// actual call; recycle self instead of returning new object for same address objects
1013 CPPInstance* pyobj = (CPPInstance*)Execute(object, offset, ctxt);
1014 if (CPPInstance_Check(pyobj) &&
1015 derived && pyobj->ObjectIsA() == derived &&
1016 pyobj->GetObject() == object) {
1017 Py_INCREF((PyObject*)self);
1018 Py_DECREF(pyobj);
1019 return (PyObject*)self;
1020 }
1021
1022 return (PyObject*)pyobj;
1023}
1024
1025//- protected members --------------------------------------------------------
1027{
1028// construct python string from the method's signature
1029 return CPyCppyy_PyText_FromString(GetSignatureString(fa).c_str());
1030}
1031
1032//----------------------------------------------------------------------------
1034{
1035 return Cppyy::GetMethodResultType(fMethod);
1036}
static void CPyCppyy_PyArgs_DEL(CPyCppyy_PyArgs_t args)
Definition CPyCppyy.h:343
int Py_ssize_t
Definition CPyCppyy.h:215
static PyObject * CPyCppyy_PyArgs_SET_ITEM(CPyCppyy_PyArgs_t args, Py_ssize_t i, PyObject *item)
Definition CPyCppyy.h:334
#define CPyCppyy_PyText_AsString
Definition CPyCppyy.h:76
static Py_ssize_t CPyCppyy_PyArgs_GET_SIZE(CPyCppyy_PyArgs_t args, size_t)
Definition CPyCppyy.h:337
PyObject * CPyCppyy_PyArgs_t
Definition CPyCppyy.h:330
#define CPyCppyy_PyText_AppendAndDel
Definition CPyCppyy.h:84
#define CPyCppyy_PyText_FromFormat
Definition CPyCppyy.h:80
#define CPyCppyy_PyText_AsStringChecked
Definition CPyCppyy.h:77
static CPyCppyy_PyArgs_t CPyCppyy_PyArgs_New(Py_ssize_t N)
Definition CPyCppyy.h:340
#define CPyCppyy_PyText_FromString
Definition CPyCppyy.h:81
static PyObject * CPyCppyy_PyArgs_GET_ITEM(CPyCppyy_PyArgs_t args, Py_ssize_t i)
Definition CPyCppyy.h:331
#define CPyCppyy_PyText_Check
Definition CPyCppyy.h:74
std::vector< Converter * > fConverters
_object PyObject
#define c(i)
Definition RSha256.hxx:101
#define e(i)
Definition RSha256.hxx:103
#define CLING_EXCEPTION_ENDTRY
#define CLING_EXCEPTION_CATCH(n)
#define CLING_EXCEPTION_TRY
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 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 cname
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 req_type
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
Cppyy::TCppType_t ObjectIsA(bool check_smart=true) const
std::string GetReturnTypeName()
bool Initialize(CallContext *ctxt=nullptr)
bool ProcessKwds(PyObject *self_in, PyCallArgs &args)
void SetPyError_(PyObject *msg)
virtual PyObject * GetSignature(bool show_formalargs=true)
Cppyy::TCppScope_t fScope
Definition CPPMethod.h:109
virtual bool ProcessArgs(PyCallArgs &args)
virtual PyObject * Reflex(Cppyy::Reflex::RequestId_t request, Cppyy::Reflex::FormatId_t=Cppyy::Reflex::OPTIMAL)
virtual int GetArgMatchScore(PyObject *args_tuple)
virtual bool InitExecutor_(Executor *&, CallContext *ctxt=nullptr)
virtual bool IsConst()
virtual PyObject * GetPrototype(bool show_formalargs=true)
Construct a Python string from the method's prototype.
std::string GetSignatureString(bool show_formalargs=true)
PyObject * ExecuteProtected(void *, ptrdiff_t, CallContext *)
bool ConvertAndSetArgs(CPyCppyy_PyArgs_t, size_t nargsf, CallContext *ctxt=nullptr)
virtual PyObject * GetCoVarNames()
virtual PyObject * GetScopeProxy()
PyObject * ExecuteFast(void *, ptrdiff_t, CallContext *)
PyObject * Execute(void *self, ptrdiff_t offset, CallContext *ctxt=nullptr)
void Copy_(const CPPMethod &)
Definition CPPMethod.cxx:88
virtual int GetPriority()
Cppyy::TCppMethod_t fMethod
Definition CPPMethod.h:108
virtual PyObject * Call(CPPInstance *&self, CPyCppyy_PyArgs_t args, size_t nargsf, PyObject *kwds, CallContext *ctxt=nullptr)
virtual PyObject * GetArgDefault(int iarg, bool silent=true)
virtual bool IsGreedy()
bool VerifyArgCount_(Py_ssize_t)
Definition CPPMethod.cxx:68
CPPMethod & operator=(const CPPMethod &)
virtual int GetMaxArgs()
CPPMethod(Cppyy::TCppScope_t scope, Cppyy::TCppMethod_t method)
virtual PyObject * GetTypeName()
virtual Cppyy::TCppFuncAddr_t GetFunctionAddress()
CPyCppyy_PyArgs_t fArgs
Definition CPPMethod.h:39
CPPInstance *& fSelf
Definition CPPMethod.h:38
virtual PyObject * Reflex(Cppyy::Reflex::RequestId_t request, Cppyy::Reflex::FormatId_t format=Cppyy::Reflex::OPTIMAL)
Definition PyCallable.h:24
const Int_t n
Definition legend1.C:16
void cppscope_to_pyscope(std::string &cppscope)
std::string clean_type(const std::string &cppname, bool template_strip=true, bool const_strip=true)
std::string extract_namespace(const std::string &name)
PyObject * gAbrtException
PyTypeObject CPPExcInstance_Type
PyObject * GetScopeProxy(Cppyy::TCppScope_t)
PyObject * CreateScopeProxy(Cppyy::TCppScope_t, const unsigned flags=0)
PyObject * gSegvException
PyObject * BindCppObjectNoCast(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
CPYCPPYY_EXTERN Executor * CreateExecutor(const std::string &name, cdims_t=0)
bool CPPInstance_Check(T *object)
PyObject * gThisModule
Definition CPPMethod.cxx:30
CPYCPPYY_EXTERN Converter * CreateConverter(const std::string &name, cdims_t=0)
PyObject * gIllException
PyObject * gBusException
const RequestId_t RETURN_TYPE
Definition Reflex.h:18
const FormatId_t AS_STRING
Definition Reflex.h:24
const FormatId_t OPTIMAL
Definition Reflex.h:22
const FormatId_t AS_TYPE
Definition Reflex.h:23
RPY_EXPORTED TCppIndex_t CompareMethodArgType(TCppMethod_t, TCppIndex_t iarg, const std::string &req_type)
RPY_EXPORTED ptrdiff_t GetBaseOffset(TCppType_t derived, TCppType_t base, TCppObject_t address, int direction, bool rerror=false)
intptr_t TCppMethod_t
Definition cpp_cppyy.h:22
RPY_EXPORTED TCppIndex_t GetMethodReqArgs(TCppMethod_t)
RPY_EXPORTED bool IsEnum(const std::string &type_name)
RPY_EXPORTED std::string GetMethodName(TCppMethod_t)
RPY_EXPORTED TCppScope_t gGlobalScope
Definition cpp_cppyy.h:53
RPY_EXPORTED std::string GetMethodSignature(TCppMethod_t, bool show_formalargs, TCppIndex_t maxargs=(TCppIndex_t) -1)
RPY_EXPORTED bool IsSubtype(TCppType_t derived, TCppType_t base)
void * TCppObject_t
Definition cpp_cppyy.h:21
RPY_EXPORTED std::string GetMethodArgName(TCppMethod_t, TCppIndex_t iarg)
TCppScope_t TCppType_t
Definition cpp_cppyy.h:19
RPY_EXPORTED TCppIndex_t GetMethodNumArgs(TCppMethod_t)
RPY_EXPORTED TCppType_t GetActualClass(TCppType_t klass, TCppObject_t obj)
RPY_EXPORTED std::string GetScopedFinalName(TCppType_t type)
RPY_EXPORTED std::string GetMethodArgType(TCppMethod_t, TCppIndex_t iarg)
RPY_EXPORTED bool IsComplete(const std::string &type_name)
RPY_EXPORTED bool IsBuiltin(const std::string &type_name)
RPY_EXPORTED bool IsStaticMethod(TCppMethod_t method)
RPY_EXPORTED TCppScope_t GetScope(const std::string &scope_name)
RPY_EXPORTED bool IsConstMethod(TCppMethod_t)
size_t TCppScope_t
Definition cpp_cppyy.h:18
RPY_EXPORTED TCppFuncAddr_t GetFunctionAddress(TCppMethod_t method, bool check_enabled=true)
RPY_EXPORTED TCppIndex_t GetNumBasesLongestBranch(TCppType_t type)
Retrieve number of base classes in the longest branch of the inheritance tree.
RPY_EXPORTED std::string GetMethodResultType(TCppMethod_t)
RPY_EXPORTED std::string GetFinalName(TCppType_t type)
RPY_EXPORTED std::string GetMethodArgDefault(TCppMethod_t, TCppIndex_t iarg)
void * TCppFuncAddr_t
Definition cpp_cppyy.h:25
Cppyy::TCppScope_t fCurScope
Parameter * GetArgs(size_t sz)
Definition CallContext.h:88
static ECallFlags sSignalPolicy
Definition CallContext.h:85