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) {
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)
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
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
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):
139 const std::string& finalname = Cppyy::GetScopedFinalName(actual);
142 if (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)
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) {
167 } else {
168 PyErr_Format(PyExc_Exception, "%s (C++ exception)", e.what());
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()) {
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);
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);
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) {
288 if (descr) {
291 }
292 }
293
295 }
296
297 PyObject* doc = GetDocString();
298 PyObject* errtype = etype;
299 if (!errtype)
302 const char* cname = pyname ? CPyCppyy_PyText_AsString(pyname) : "Exception";
303
305 if (details.empty()) {
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",
315 }
316 } else {
317 Py_XDECREF(((CPPExcInstance*)evalue)->fTopMessage);
318 if (msg) {
321 } else {
323 "%s =>\n %s: ", CPyCppyy_PyText_AsString(doc), cname);
324 }
326 }
327
330 Py_XDECREF(etype);
331 Py_DECREF(doc);
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();
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);
514 if (scope)
515 priority += static_cast<int>(Cppyy::GetNumBasesLongestBranch(scope));
516
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
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
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()) {
608 if (!(dctptr && *dctptr))
609 return nullptr;
610
611 PyObject* gdct = *dctptr;
612 PyObject* scope = nullptr;
613
614 if (defvalue.rfind('(') != std::string::npos) { // constructor-style call
615 // try to tickle scope creation, just in case, first look in the scope where
616 // the function lives, then in the global scope
617 std::string possible_scope = defvalue.substr(0, defvalue.rfind('('));
619 std::string cand_scope = Cppyy::GetScopedFinalName(fScope)+"::"+possible_scope;
621 if (!scope) {
622 PyErr_Clear();
623 // search within the global scope instead
625 if (!scope) PyErr_Clear();
626 } else {
627 // re-scope the scope; alternatively, the expression could be
628 // compiled in the dictionary of the function's namespace, but
629 // that would affect arguments passed to the constructor, too
630 defvalue = cand_scope + defvalue.substr(defvalue.rfind('('), std::string::npos);
631 }
632 }
633 }
634
635 // replace '::' -> '.'
637
638 if (!scope) {
639 // a couple of common cases that python doesn't like (technically, 'L' is okay with older
640 // pythons, but C long will always fit in Python int, so no need to bother)
641 char c = defvalue.back();
642 if (c == 'F' || c == 'D' || c == 'L') {
643 int offset = 1;
644 if (2 < defvalue.size() && defvalue[defvalue.size()-2] == 'U')
645 offset = 2;
646 defvalue = defvalue.substr(0, defvalue.size()-offset);
647 }
648 }
649
650 // attempt to evaluate the string representation (compilation is first to code to allow
651 // the error message to indicate where it's coming from)
652 PyObject* pyval = nullptr;
653
654 PyObject* pycode = Py_CompileString((char*)defvalue.c_str(), "cppyy_default_compiler", Py_eval_input);
655 if (pycode) {
657#if PY_VERSION_HEX < 0x03000000
658 (PyCodeObject*)
659#endif
660 pycode, gdct, gdct);
662 }
663
664 if (!pyval && PyErr_Occurred() && silent) {
665 PyErr_Clear();
666 pyval = CPyCppyy_PyText_FromString(defvalue.c_str()); // allows continuation, but is likely to fail
667 }
668
670 return pyval; // may be nullptr
671 }
672
673 PyErr_Format(PyExc_TypeError, "Could not construct default value for: %s", Cppyy::GetMethodArgName(fMethod, iarg).c_str());
674 return nullptr;
675}
676
677
679 return Cppyy::IsConstMethod(GetMethod());
680}
681
682
683//----------------------------------------------------------------------------
685{
686// Get or build the scope of this method.
687 return CreateScopeProxy(fScope);
688}
689
690
691//----------------------------------------------------------------------------
693{
694// Return the C++ pointer of this function
695 return Cppyy::GetFunctionAddress(fMethod, false /* don't check fast path envar */);
696}
697
698//----------------------------------------------------------------------------
700{
702
703 int req_args = Cppyy::GetMethodReqArgs(fMethod);
704
705 // Not enough arguments supplied: no match
706 if (req_args > n)
707 return INT_MAX;
708
709 size_t score = 0;
710 for (int i = 0; i < n; i++) {
713 PyErr_SetString(PyExc_TypeError, "argument types should be in string format");
714 return INT_MAX;
715 }
717
718 size_t arg_score = Cppyy::CompareMethodArgType(fMethod, i, req_type);
719
720 // Method is not compatible if even one argument does not match
721 if (arg_score >= 10) {
722 score = INT_MAX;
723 break;
724 }
725
726 score += arg_score;
727 }
728
729 return score;
730}
731
732//----------------------------------------------------------------------------
734{
735// done if cache is already setup
736 if (fArgsRequired != -1)
737 return true;
738
739 if (!InitConverters_())
740 return false;
741
742 if (!InitExecutor_(fExecutor, ctxt))
743 return false;
744
745// minimum number of arguments when calling
746 fArgsRequired = (int)((bool)fMethod == true ? Cppyy::GetMethodReqArgs(fMethod) : 0);
747
748 return true;
749}
750
751//----------------------------------------------------------------------------
753{
754#if PY_VERSION_HEX >= 0x03080000
755 if (!PyTuple_CheckExact(cargs.fKwds)) {
756 SetPyError_(CPyCppyy_PyText_FromString("received unknown keyword names object"));
757 return false;
758 }
760#else
761 if (!PyDict_CheckExact(cargs.fKwds)) {
762 SetPyError_(CPyCppyy_PyText_FromString("received unknown keyword arguments object"));
763 return false;
764 }
766#endif
767
768 if (nKeys == 0 && !self_in)
769 return true;
770
771 if (!fArgIndices) {
772 fArgIndices = new std::map<std::string, int>{};
773 for (int iarg = 0; iarg < (int)Cppyy::GetMethodNumArgs(fMethod); ++iarg)
774 (*fArgIndices)[Cppyy::GetMethodArgName(fMethod, iarg)] = iarg;
775 }
776
777 Py_ssize_t nArgs = CPyCppyy_PyArgs_GET_SIZE(cargs.fArgs, cargs.fNArgsf) + (self_in ? 1 : 0);
778 if (!VerifyArgCount_(nArgs+nKeys))
779 return false;
780
781 std::vector<PyObject*> vArgs{fConverters.size()};
782
783// next, insert the keyword values
784 PyObject *key, *value;
785 Py_ssize_t maxpos = -1;
786
787#if PY_VERSION_HEX >= 0x03080000
789 for (Py_ssize_t ikey = 0; ikey < nKeys; ++ikey) {
790 key = PyTuple_GET_ITEM(cargs.fKwds, ikey);
791 value = cargs.fArgs[npos_args+ikey];
792#else
793 Py_ssize_t pos = 0;
794 while (PyDict_Next(cargs.fKwds, &pos, &key, &value)) {
795#endif
796 const char* ckey = CPyCppyy_PyText_AsStringChecked(key);
797 if (!ckey)
798 return false;
799
800 auto p = fArgIndices->find(ckey);
801 if (p == fArgIndices->end()) {
802 SetPyError_(CPyCppyy_PyText_FromFormat("%s::%s got an unexpected keyword argument \'%s\'",
803 Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetMethodName(fMethod).c_str(), ckey));
804 return false;
805 }
806
807 maxpos = p->second > maxpos ? p->second : maxpos;
808 vArgs[p->second] = value; // no INCREF yet for simple cleanup in case of error
809 }
810
811// if maxpos < nArgs, it will be detected & reported as a duplicate below
814
815// set all values to zero to be able to check them later (this also guarantees normal
816// cleanup by the tuple deallocation)
817 for (Py_ssize_t i = 0; i < maxargs; ++i)
819
820// fill out the positional arguments
821 Py_ssize_t start = 0;
822 if (self_in) {
825 start = 1;
826 }
827
828 for (Py_ssize_t i = start; i < nArgs; ++i) {
829 if (vArgs[i]) {
830 SetPyError_(CPyCppyy_PyText_FromFormat("%s::%s got multiple values for argument %d",
831 Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetMethodName(fMethod).c_str(), (int)i+1));
833 return false;
834 }
835
836 PyObject* item = CPyCppyy_PyArgs_GET_ITEM(cargs.fArgs, i);
837 Py_INCREF(item);
839 }
840
841// fill out the keyword arguments
842 for (Py_ssize_t i = nArgs; i < maxargs; ++i) {
843 PyObject* item = vArgs[i];
844 if (item) {
845 Py_INCREF(item);
847 } else {
848 // try retrieving the default
849 item = GetArgDefault((int)i, false /* i.e. not silent */);
850 if (!item) {
852 return false;
853 }
855 }
856 }
857
858#if PY_VERSION_HEX >= 0x03080000
859 if (cargs.fFlags & PyCallArgs::kDoFree) {
860 if (cargs.fFlags & PyCallArgs::kIsOffset)
861 cargs.fArgs -= 1;
862#else
863 if (cargs.fFlags & PyCallArgs::kDoDecref) {
864#endif
866 }
867
868 cargs.fArgs = newArgs;
869 cargs.fNArgsf = maxargs;
870#if PY_VERSION_HEX >= 0x03080000
871 cargs.fFlags = PyCallArgs::kDoFree | PyCallArgs::kDoItemDecref;
872#else
874#endif
875
876 return true;
877}
878
879//----------------------------------------------------------------------------
881{
882// verify existence of self, return if ok
883 if (cargs.fSelf) {
884 if (cargs.fKwds) { return ProcessKwds(nullptr, cargs); }
885 return true;
886 }
887
888// otherwise, check for a suitable 'self' in args and update accordingly
889 if (CPyCppyy_PyArgs_GET_SIZE(cargs.fArgs, cargs.fNArgsf) != 0) {
891
892 // demand CPyCppyy object, and an argument that may match down the road
894 Cppyy::TCppType_t oisa = pyobj->ObjectIsA();
895 if (fScope == Cppyy::gGlobalScope || // free global
896 oisa == 0 || // null pointer or ctor call
897 oisa == fScope || // matching types
898 Cppyy::IsSubtype(oisa, fScope)) { // id.
899
900 // reset self
901 Py_INCREF(pyobj); // corresponding Py_DECREF is in CPPOverload
902 cargs.fSelf = pyobj;
903
904 // offset args by 1
905#if PY_VERSION_HEX >= 0x03080000
906 cargs.fArgs += 1;
908#else
909 if (cargs.fFlags & PyCallArgs::kDoDecref)
910 Py_DECREF((PyObject*)cargs.fArgs);
911 cargs.fArgs = PyTuple_GetSlice(cargs.fArgs, 1, PyTuple_GET_SIZE(cargs.fArgs));
913#endif
914 cargs.fNArgsf -= 1;
915
916 // put the keywords, if any, in their places in the arguments array
917 if (cargs.fKwds)
918 return ProcessKwds(nullptr, cargs);
919 return true;
920 }
921 }
922 }
923
924// no self, set error and lament
925 SetPyError_(CPyCppyy_PyText_FromFormat(
926 "unbound method %s::%s must be called with a %s instance as first argument",
927 Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetMethodName(fMethod).c_str(),
928 Cppyy::GetFinalName(fScope).c_str()));
929 return false;
930}
931
932//----------------------------------------------------------------------------
934{
936 if (!VerifyArgCount_(argc))
937 return false;
938
939// pass current scope for which the call is made
940 ctxt->fCurScope = fScope;
941
942 if (argc == 0)
943 return true;
944
945// convert the arguments to the method call array
946 bool isOK = true;
947 Parameter* cppArgs = ctxt->GetArgs(argc);
948 for (int i = 0; i < (int)argc; ++i) {
949 if (!fConverters[i]->SetArg(CPyCppyy_PyArgs_GET_ITEM(args, i), cppArgs[i], ctxt)) {
950 SetPyError_(CPyCppyy_PyText_FromFormat("could not convert argument %d", i+1));
951 isOK = false;
952 break;
953 }
954 }
955
956 return isOK;
957}
958
959//----------------------------------------------------------------------------
961{
962// call the interface method
963 PyObject* result = 0;
964
966 !(ctxt->fFlags & CallContext::kProtected)) {
967 // bypasses try block (i.e. segfaults will abort)
968 result = ExecuteFast(self, offset, ctxt);
969 } else {
970 // at the cost of ~10% performance, don't abort the interpreter on any signal
971 result = ExecuteProtected(self, offset, ctxt);
972 }
973
974 if (!result && PyErr_Occurred())
975 SetPyError_(0);
976
977 return result;
978}
979
980//----------------------------------------------------------------------------
983{
984// setup as necessary
985 if (fArgsRequired == -1 && !Initialize(ctxt))
986 return nullptr;
987
988// fetch self, verify, and put the arguments in usable order
990 if (!ProcessArgs(cargs))
991 return nullptr;
992
993// self provides the python context for lifelines
994 if (!ctxt->fPyContext)
995 ctxt->fPyContext = (PyObject*)cargs.fSelf; // no Py_INCREF as no ownership
996
997// translate the arguments
998 if (fArgsRequired || CPyCppyy_PyArgs_GET_SIZE(args, nargsf)) {
999 if (!ConvertAndSetArgs(cargs.fArgs, cargs.fNArgsf, ctxt))
1000 return nullptr;
1001 }
1002
1003// get the C++ object that this object proxy is a handle for
1004 void* object = self->GetObject();
1005
1006// validity check that should not fail
1007 if (!object) {
1008 PyErr_SetString(PyExc_ReferenceError, "attempt to access a null-pointer");
1009 return nullptr;
1010 }
1011
1012// get its class
1013 Cppyy::TCppType_t derived = self->ObjectIsA();
1014
1015// calculate offset (the method expects 'this' to be an object of fScope)
1016 ptrdiff_t offset = 0;
1017 if (derived && derived != fScope)
1018 offset = Cppyy::GetBaseOffset(derived, fScope, object, 1 /* up-cast */);
1019
1020// actual call; recycle self instead of returning new object for same address objects
1021 CPPInstance* pyobj = (CPPInstance*)Execute(object, offset, ctxt);
1022 if (CPPInstance_Check(pyobj) &&
1023 derived && pyobj->ObjectIsA() == derived &&
1024 pyobj->GetObject() == object) {
1027 return (PyObject*)self;
1028 }
1029
1030 return (PyObject*)pyobj;
1031}
1032
1033//- protected members --------------------------------------------------------
1035{
1036// construct python string from the method's signature
1037 return CPyCppyy_PyText_FromString(GetSignatureString(fa).c_str());
1038}
1039
1040//----------------------------------------------------------------------------
1042{
1043 return Cppyy::GetMethodResultType(fMethod);
1044}
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
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 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
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)
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()
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_iterator end() const
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
static ECallFlags sSignalPolicy
Definition CallContext.h:85