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//
279// Handles three cases:
280// 1. No Python error occured yet:
281// Set a new TypeError with the message "msg" and the docstring of this
282// C++ method to give some context.
283// 2. A C++ exception has occured:
284// Augment the exception message with the docstring of this method
285// 3. A Python exception has occured:
286// Do nothing, Python exceptions are already informative enough
287
288#if PY_VERSION_HEX >= 0x030c0000
290 PyObject *etype = evalue ? (PyObject *)Py_TYPE(evalue) : nullptr;
291#else
292 PyObject *etype = nullptr;
293 PyObject *evalue = nullptr;
294 PyObject *etrace = nullptr;
295
296 if (PyErr_Occurred()) {
297 PyErr_Fetch(&etype, &evalue, &etrace);
298 }
299#endif
300
302 // If the error is not a CPPExcInstance, the error from Python itself is
303 // already complete and messing with it would only make it less informative.
304 // Just restore and return.
305 if (evalue && !isCppExc) {
306#if PY_VERSION_HEX >= 0x030c0000
308#else
309 PyErr_Restore(etype, evalue, etrace);
310#endif
311 return;
312 }
313
314 PyObject* doc = GetDocString();
315 const char* cdoc = CPyCppyy_PyText_AsString(doc);
316 const char* cmsg = msg ? CPyCppyy_PyText_AsString(msg) : nullptr;
317 PyObject* errtype = etype ? etype : PyExc_TypeError;
319 const char* cname = pyname ? CPyCppyy_PyText_AsString(pyname) : "Exception";
320
321 if (!isCppExc) {
322 // this is the case where no Python error has occured yet, and we set a new
323 // error with context info
324 PyErr_Format(errtype, "%s =>\n %s: %s", cdoc, cname, cmsg ? cmsg : "");
325 } else {
326 // augment the top message with context information
327 PyObject *&topMessage = ((CPPExcInstance*)evalue)->fTopMessage;
329 if (msg) {
330 topMessage = CPyCppyy_PyText_FromFormat("%s =>\n %s: %s | ", cdoc, cname, cmsg);
331 } else {
333 }
334 // restore the updated error
335#if PY_VERSION_HEX >= 0x030c0000
337#else
338 PyErr_Restore(etype, evalue, etrace);
339#endif
340 }
341
343 Py_DECREF(doc);
345}
346
347//- constructors and destructor ----------------------------------------------
350 fMethod(method), fScope(scope), fExecutor(nullptr), fArgIndices(nullptr),
351 fArgsRequired(-1)
352{
353 // empty
354}
355
356//----------------------------------------------------------------------------
358 PyCallable(other), fMethod(other.fMethod), fScope(other.fScope)
359{
360 Copy_(other);
361}
362
363//----------------------------------------------------------------------------
365{
366 if (this != &other) {
367 Destroy_();
368 Copy_(other);
369 fScope = other.fScope;
370 fMethod = other.fMethod;
371 }
372
373 return *this;
374}
375
376//----------------------------------------------------------------------------
378{
379 Destroy_();
380}
381
382
383//- public members -----------------------------------------------------------
384/**
385 * @brief Construct a Python string from the method's prototype
386 *
387 * @param fa Show formal arguments of the method
388 * @return PyObject* A Python string with the full method prototype, namespaces included.
389 *
390 * For example, given:
391 *
392 * int foo(int x);
393 *
394 * namespace a {
395 * namespace b {
396 * namespace c {
397 * int foo(int x);
398 * }}}
399 *
400 * This function returns:
401 *
402 * 'int foo(int x)'
403 * 'int a::b::c::foo(int x)'
404 */
406{
407 // Gather the fully qualified final scope of the method. This includes
408 // all namespaces up to the one where the method is declared, for example:
409 // namespace a { namespace b { void foo(); }}
410 // gives
411 // a::b
412 std::string finalscope = Cppyy::GetScopedFinalName(fScope);
413 return CPyCppyy_PyText_FromFormat("%s%s %s%s%s%s",
414 (Cppyy::IsStaticMethod(fMethod) ? "static " : ""),
415 Cppyy::GetMethodResultType(fMethod).c_str(),
416 finalscope.c_str(),
417 (finalscope.empty() ? "" : "::"), // Add final set of '::' if the method is scoped in namespace(s)
418 Cppyy::GetMethodName(fMethod).c_str(),
419 GetSignatureString(fa).c_str());
420}
421
422//----------------------------------------------------------------------------
424{
426 (GetReturnTypeName() + \
427 " (" + (fScope ? Cppyy::GetScopedFinalName(fScope) + "::*)" : "*)")).c_str());
428 CPyCppyy_PyText_AppendAndDel(&cppname, GetSignature(false /* show_formalargs */));
429 return cppname;
430}
431
432//----------------------------------------------------------------------------
434{
435// C++ reflection tooling for methods.
436
437 if (request == Cppyy::Reflex::RETURN_TYPE) {
438 std::string rtn = GetReturnTypeName();
442
444 return CPyCppyy_PyText_FromString(rtn.c_str());
446 if (scope) return CreateScopeProxy(scope);
447 /* TODO: builtins as type */
448 }
449 }
450
451 return PyCallable::Reflex(request, format);
452}
453
454//----------------------------------------------------------------------------
456{
457// To help with overload selection, methods are given a priority based on the
458// affinity of Python and C++ types. Priority only matters for methods that have
459// an equal number of arguments and types that are possible substitutes (the
460// normal selection mechanisms would simply distinguish them otherwise).
461
462// The following types are ordered, in favor (variants implicit):
463//
464// bool >> long >> int >> short
465// double >> long double >> float
466// const char* >> char
467//
468// Further, all integer types are preferred over floating point b/c int to float
469// is allowed implicitly, float to int is not.
470//
471// Special cases that are disliked include void* and unknown/incomplete types.
472// Also, moves are preferred over references. std::initializer_list is not a nice
473// conversion candidate either, but needs to be higher priority to mix well with
474// implicit conversions.
475// TODO: extend this to favour classes that are not bases.
476// TODO: profile this method (it's expensive, but should be called too often)
477
478 int priority = 0;
479
480 const size_t nArgs = Cppyy::GetMethodNumArgs(fMethod);
481 for (int iarg = 0; iarg < (int)nArgs; ++iarg) {
482 const std::string aname = Cppyy::GetMethodArgType(fMethod, iarg);
483
484 if (Cppyy::IsBuiltin(aname)) {
485 // complex type (note: double penalty: for complex and the template type)
486 if (strstr(aname.c_str(), "std::complex"))
487 priority -= 10; // prefer double, float, etc. over conversion
488
489 // integer types
490 if (strstr(aname.c_str(), "bool"))
491 priority += 1; // bool over int (does accept 1 and 0)
492 else if (strstr(aname.c_str(), "long long"))
493 priority += -5; // will very likely fit
494 else if (strstr(aname.c_str(), "long"))
495 priority += -10; // most affine integer type
496 // no need to compare with int; leave at zero
497 else if (strstr(aname.c_str(), "short"))
498 priority += -50; // not really relevant as a type
499
500 // floating point types (note all numbers lower than integer types)
501 else if (strstr(aname.c_str(), "float"))
502 priority += -100; // not really relevant as a type
503 else if (strstr(aname.c_str(), "long double"))
504 priority += -90; // fits double with least loss of precision
505 else if (strstr(aname.c_str(), "double"))
506 priority += -80; // most affine floating point type
507
508 // string/char types
509 else if (strstr(aname.c_str(), "char") && aname[aname.size()-1] != '*')
510 priority += -60; // prefer (const) char* over char
511
512 // oddball
513 else if (strstr(aname.c_str(), "void*"))
514 priority -= 1000; // void*/void** shouldn't be too greedy
515
516 } else {
517 // This is a user-defined type (class, struct, enum, etc.).
518
519 // There's a bit of hysteresis here for templates: once GetScope() is called, their
520 // IsComplete() succeeds, the other way around it does not. Since GetPriority() is
521 // likely called several times in a sort, the GetScope() _must_ come first, or
522 // different GetPriority() calls may return different results (since the 2nd time,
523 // GetScope() will have been called from the first), killing the stable_sort.
524
525 // prefer more derived classes
526 const std::string& clean_name = TypeManip::clean_type(aname, false);
528 if (scope)
529 priority += static_cast<int>(Cppyy::GetNumBasesLongestBranch(scope));
530
532 priority -= 100;
533
534 // a couple of special cases as explained above
535 if (aname.find("initializer_list") != std::string::npos) {
536 priority += 150; // needed for proper implicit conversion rules
537 } else if (aname.rfind("&&", aname.size()-2) != std::string::npos) {
538 priority += 100; // prefer moves over other ref/ptr
539 } else if (scope && !Cppyy::IsComplete(clean_name)) {
540 // class is known, but no dictionary available, 2 more cases: * and &
541 if (aname[aname.size() - 1] == '&')
542 priority += -5000;
543 else
544 priority += -2000; // prefer pointer passing over reference
545 }
546 }
547 }
548
549// prefer methods w/o optional arguments b/c ones with optional arguments are easier to
550// select by providing the optional arguments explicitly
551 priority += ((int)Cppyy::GetMethodReqArgs(fMethod) - (int)nArgs);
552
553// add a small penalty to prefer non-const methods over const ones for get/setitem
554 if (Cppyy::IsConstMethod(fMethod) && Cppyy::GetMethodName(fMethod) == "operator[]")
555 priority += -10;
556
557 return priority;
558}
559
560//----------------------------------------------------------------------------
562{
563// Methods will all void*-like arguments should be sorted after template
564// instanstations, so that they don't greedily take over pointers to object.
565// GetPriority() is too heavy-handed, as it will pull in all the argument
566// types, so use this cheaper check.
567 const size_t nArgs = Cppyy::GetMethodReqArgs(fMethod);
568 if (!nArgs) return false;
569
570 for (int iarg = 0; iarg < (int)nArgs; ++iarg) {
571 const std::string aname = Cppyy::GetMethodArgType(fMethod, iarg);
572 if (aname.find("void*") != 0)
573 return false;
574 }
575 return true;
576}
577
578
579//----------------------------------------------------------------------------
581{
582 return (int)Cppyy::GetMethodNumArgs(fMethod);
583}
584
585//----------------------------------------------------------------------------
587{
588// Build a tuple of the argument types/names.
589 int co_argcount = (int)GetMaxArgs() /* +1 for self */;
590
591// TODO: static methods need no 'self' (but is harmless otherwise)
592
595 for (int iarg = 0; iarg < co_argcount; ++iarg) {
596 std::string argrep = Cppyy::GetMethodArgType(fMethod, iarg);
597 const std::string& parname = Cppyy::GetMethodArgName(fMethod, iarg);
598 if (!parname.empty()) {
599 argrep += " ";
600 argrep += parname;
601 }
602
605 }
606
607 return co_varnames;
608}
609
611{
612// get and evaluate the default value (if any) of argument iarg of this method
613 if (iarg >= (int)GetMaxArgs())
614 return nullptr;
615
616// borrowed reference to cppyy.gbl module to use its dictionary to eval in
617 static PyObject* gbl = PyDict_GetItemString(PySys_GetObject((char*)"modules"), "cppyy.gbl");
618
619 std::string defvalue = Cppyy::GetMethodArgDefault(fMethod, iarg);
620 if (!defvalue.empty()) {
622 if (!(dctptr && *dctptr))
623 return nullptr;
624
625 PyObject* gdct = *dctptr;
626 PyObject* scope = nullptr;
627
628 if (defvalue.rfind('(') != std::string::npos) { // constructor-style call
629 // try to tickle scope creation, just in case, first look in the scope where
630 // the function lives, then in the global scope
631 std::string possible_scope = defvalue.substr(0, defvalue.rfind('('));
633 std::string cand_scope = Cppyy::GetScopedFinalName(fScope)+"::"+possible_scope;
635 if (!scope) {
636 PyErr_Clear();
637 // search within the global scope instead
639 if (!scope) PyErr_Clear();
640 } else {
641 // re-scope the scope; alternatively, the expression could be
642 // compiled in the dictionary of the function's namespace, but
643 // that would affect arguments passed to the constructor, too
644 defvalue = cand_scope + defvalue.substr(defvalue.rfind('('), std::string::npos);
645 }
646 }
647 }
648
649 // replace '::' -> '.'
651
652 if (!scope) {
653 // a couple of common cases that python doesn't like (technically, 'L' is okay with older
654 // pythons, but C long will always fit in Python int, so no need to bother)
655 char c = defvalue.back();
656 if (c == 'F' || c == 'D' || c == 'L') {
657 int offset = 1;
658 if (2 < defvalue.size() && defvalue[defvalue.size()-2] == 'U')
659 offset = 2;
660 defvalue = defvalue.substr(0, defvalue.size()-offset);
661 } else if (defvalue == "true") {
662 defvalue = "True";
663 } else if (defvalue == "false") {
664 defvalue = "False";
665 }
666 }
667
668 // attempt to evaluate the string representation (compilation is first to code to allow
669 // the error message to indicate where it's coming from)
670 PyObject* pyval = nullptr;
671
672 PyObject* pycode = Py_CompileString((char*)defvalue.c_str(), "cppyy_default_compiler", Py_eval_input);
673 if (pycode) {
675#if PY_VERSION_HEX < 0x03000000
676 (PyCodeObject*)
677#endif
678 pycode, gdct, gdct);
680 }
681
682 if (!pyval && PyErr_Occurred() && silent) {
683 PyErr_Clear();
684 pyval = CPyCppyy_PyText_FromString(defvalue.c_str()); // allows continuation, but is likely to fail
685 }
686
688 return pyval; // may be nullptr
689 }
690
691 PyErr_Format(PyExc_TypeError, "Could not construct default value for: %s", Cppyy::GetMethodArgName(fMethod, iarg).c_str());
692 return nullptr;
693}
694
695
697 return Cppyy::IsConstMethod(GetMethod());
698}
699
700
701//----------------------------------------------------------------------------
703{
704// Get or build the scope of this method.
705 return CreateScopeProxy(fScope);
706}
707
708
709//----------------------------------------------------------------------------
711{
712// Return the C++ pointer of this function
713 return Cppyy::GetFunctionAddress(fMethod, false /* don't check fast path envar */);
714}
715
716//----------------------------------------------------------------------------
718{
720
721 int req_args = Cppyy::GetMethodReqArgs(fMethod);
722
723 // Not enough arguments supplied: no match
724 if (req_args > n)
725 return INT_MAX;
726
727 size_t score = 0;
728 for (int i = 0; i < n; i++) {
731 PyErr_SetString(PyExc_TypeError, "argument types should be in string format");
732 return INT_MAX;
733 }
735
736 size_t arg_score = Cppyy::CompareMethodArgType(fMethod, i, req_type);
737
738 // Method is not compatible if even one argument does not match
739 if (arg_score >= 10) {
740 score = INT_MAX;
741 break;
742 }
743
744 score += arg_score;
745 }
746
747 return score;
748}
749
750//----------------------------------------------------------------------------
752{
753// done if cache is already setup
754 if (fArgsRequired != -1)
755 return true;
756
757 if (!InitConverters_())
758 return false;
759
760 if (!InitExecutor_(fExecutor, ctxt))
761 return false;
762
763// minimum number of arguments when calling
764 fArgsRequired = (int)((bool)fMethod == true ? Cppyy::GetMethodReqArgs(fMethod) : 0);
765
766 return true;
767}
768
769//----------------------------------------------------------------------------
771{
772#if PY_VERSION_HEX >= 0x03080000
773 if (!PyTuple_CheckExact(cargs.fKwds)) {
774 SetPyError_(CPyCppyy_PyText_FromString("received unknown keyword names object"));
775 return false;
776 }
778#else
779 if (!PyDict_CheckExact(cargs.fKwds)) {
780 SetPyError_(CPyCppyy_PyText_FromString("received unknown keyword arguments object"));
781 return false;
782 }
784#endif
785
786 if (nKeys == 0 && !self_in)
787 return true;
788
789 if (!fArgIndices) {
790 fArgIndices = new std::map<std::string, int>{};
791 for (int iarg = 0; iarg < (int)Cppyy::GetMethodNumArgs(fMethod); ++iarg)
792 (*fArgIndices)[Cppyy::GetMethodArgName(fMethod, iarg)] = iarg;
793 }
794
795 Py_ssize_t nArgs = CPyCppyy_PyArgs_GET_SIZE(cargs.fArgs, cargs.fNArgsf) + (self_in ? 1 : 0);
796 if (!VerifyArgCount_(nArgs+nKeys))
797 return false;
798
799 std::vector<PyObject*> vArgs{fConverters.size()};
800
801// next, insert the keyword values
802 PyObject *key, *value;
803 Py_ssize_t maxpos = -1;
804
805#if PY_VERSION_HEX >= 0x03080000
807 for (Py_ssize_t ikey = 0; ikey < nKeys; ++ikey) {
808 key = PyTuple_GET_ITEM(cargs.fKwds, ikey);
809 value = cargs.fArgs[npos_args+ikey];
810#else
811 Py_ssize_t pos = 0;
812 while (PyDict_Next(cargs.fKwds, &pos, &key, &value)) {
813#endif
814 const char* ckey = CPyCppyy_PyText_AsStringChecked(key);
815 if (!ckey)
816 return false;
817
818 auto p = fArgIndices->find(ckey);
819 if (p == fArgIndices->end()) {
820 SetPyError_(CPyCppyy_PyText_FromFormat("%s::%s got an unexpected keyword argument \'%s\'",
821 Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetMethodName(fMethod).c_str(), ckey));
822 return false;
823 }
824
825 maxpos = p->second > maxpos ? p->second : maxpos;
826 vArgs[p->second] = value; // no INCREF yet for simple cleanup in case of error
827 }
828
829// if maxpos < nArgs, it will be detected & reported as a duplicate below
832
833// set all values to zero to be able to check them later (this also guarantees normal
834// cleanup by the tuple deallocation)
835 for (Py_ssize_t i = 0; i < maxargs; ++i)
837
838// fill out the positional arguments
839 Py_ssize_t start = 0;
840 if (self_in) {
843 start = 1;
844 }
845
846 for (Py_ssize_t i = start; i < nArgs; ++i) {
847 if (vArgs[i]) {
848 SetPyError_(CPyCppyy_PyText_FromFormat("%s::%s got multiple values for argument %d",
849 Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetMethodName(fMethod).c_str(), (int)i+1));
851 return false;
852 }
853
857 }
858
859// fill out the keyword arguments
860 for (Py_ssize_t i = nArgs; i < maxargs; ++i) {
861 PyObject* item = vArgs[i];
862 if (item) {
865 } else {
866 // try retrieving the default
867 item = GetArgDefault((int)i, false /* i.e. not silent */);
868 if (!item) {
870 return false;
871 }
873 }
874 }
875
876#if PY_VERSION_HEX >= 0x03080000
877 if (cargs.fFlags & PyCallArgs::kDoFree) {
878 if (cargs.fFlags & PyCallArgs::kIsOffset)
879 cargs.fArgs -= 1;
880#else
881 if (cargs.fFlags & PyCallArgs::kDoDecref) {
882#endif
884 }
885
886 cargs.fArgs = newArgs;
887 cargs.fNArgsf = maxargs;
888#if PY_VERSION_HEX >= 0x03080000
889 cargs.fFlags = PyCallArgs::kDoFree | PyCallArgs::kDoItemDecref;
890#else
892#endif
893
894 return true;
895}
896
897//----------------------------------------------------------------------------
899{
900// verify existence of self, return if ok
901 if (cargs.fSelf) {
902 if (cargs.fKwds) { return ProcessKwds(nullptr, cargs); }
903 return true;
904 }
905
906// otherwise, check for a suitable 'self' in args and update accordingly
907 if (CPyCppyy_PyArgs_GET_SIZE(cargs.fArgs, cargs.fNArgsf) != 0) {
909
910 // demand CPyCppyy object, and an argument that may match down the road
912 Cppyy::TCppType_t oisa = pyobj->ObjectIsA();
913 if (fScope == Cppyy::gGlobalScope || // free global
914 oisa == 0 || // null pointer or ctor call
915 oisa == fScope || // matching types
916 Cppyy::IsSubtype(oisa, fScope)) { // id.
917
918 // reset self
919 Py_INCREF(pyobj); // corresponding Py_DECREF is in CPPOverload
920 cargs.fSelf = pyobj;
921
922 // offset args by 1
923#if PY_VERSION_HEX >= 0x03080000
924 cargs.fArgs += 1;
926#else
927 if (cargs.fFlags & PyCallArgs::kDoDecref)
928 Py_DECREF((PyObject*)cargs.fArgs);
929 cargs.fArgs = PyTuple_GetSlice(cargs.fArgs, 1, PyTuple_GET_SIZE(cargs.fArgs));
931#endif
932 cargs.fNArgsf -= 1;
933
934 // put the keywords, if any, in their places in the arguments array
935 if (cargs.fKwds)
936 return ProcessKwds(nullptr, cargs);
937 return true;
938 }
939 }
940 }
941
942// no self, set error and lament
943 SetPyError_(CPyCppyy_PyText_FromFormat(
944 "unbound method %s::%s must be called with a %s instance as first argument",
945 Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetMethodName(fMethod).c_str(),
946 Cppyy::GetFinalName(fScope).c_str()));
947 return false;
948}
949
950//----------------------------------------------------------------------------
952{
954 if (!VerifyArgCount_(argc))
955 return false;
956
957// pass current scope for which the call is made
958 ctxt->fCurScope = fScope;
959
960 if (argc == 0)
961 return true;
962
963// convert the arguments to the method call array
964 bool isOK = true;
965 Parameter* cppArgs = ctxt->GetArgs(argc);
966 for (int i = 0; i < (int)argc; ++i) {
967 if (!fConverters[i]->SetArg(CPyCppyy_PyArgs_GET_ITEM(args, i), cppArgs[i], ctxt)) {
968 SetPyError_(CPyCppyy_PyText_FromFormat("could not convert argument %d", i+1));
969 isOK = false;
970 break;
971 }
972 }
973
974 return isOK;
975}
976
977//----------------------------------------------------------------------------
979{
980// call the interface method
981 PyObject* result = 0;
982
984 !(ctxt->fFlags & CallContext::kProtected)) {
985 // bypasses try block (i.e. segfaults will abort)
986 result = ExecuteFast(self, offset, ctxt);
987 } else {
988 // at the cost of ~10% performance, don't abort the interpreter on any signal
989 result = ExecuteProtected(self, offset, ctxt);
990 }
991
992 if (!result && PyErr_Occurred())
993 SetPyError_(0);
994
995 return result;
996}
997
998//----------------------------------------------------------------------------
1001{
1002// setup as necessary
1003 if (fArgsRequired == -1 && !Initialize(ctxt))
1004 return nullptr;
1005
1006// fetch self, verify, and put the arguments in usable order
1007 PyCallArgs cargs{self, args, nargsf, kwds};
1008 if (!ProcessArgs(cargs))
1009 return nullptr;
1010
1011// self provides the python context for lifelines
1012 if (!ctxt->fPyContext)
1013 ctxt->fPyContext = (PyObject*)cargs.fSelf; // no Py_INCREF as no ownership
1014
1015// translate the arguments
1016 if (fArgsRequired || CPyCppyy_PyArgs_GET_SIZE(args, cargs.fNArgsf)) {
1017 if (!ConvertAndSetArgs(cargs.fArgs, cargs.fNArgsf, ctxt))
1018 return nullptr;
1019 }
1020
1021// get the C++ object that this object proxy is a handle for
1022 void* object = self->GetObject();
1023
1024// validity check that should not fail
1025 if (!object) {
1026 PyErr_SetString(PyExc_ReferenceError, "attempt to access a null-pointer");
1027 return nullptr;
1028 }
1029
1030// get its class
1031 Cppyy::TCppType_t derived = self->ObjectIsA();
1032
1033// calculate offset (the method expects 'this' to be an object of fScope)
1034 ptrdiff_t offset = 0;
1035 if (derived && derived != fScope)
1036 offset = Cppyy::GetBaseOffset(derived, fScope, object, 1 /* up-cast */);
1037
1038// actual call; recycle self instead of returning new object for same address objects
1039 CPPInstance* pyobj = (CPPInstance*)Execute(object, offset, ctxt);
1040 if (CPPInstance_Check(pyobj) &&
1041 derived && pyobj->ObjectIsA() == derived &&
1042 pyobj->GetObject() == object) {
1045 return (PyObject*)self;
1046 }
1047
1048 return (PyObject*)pyobj;
1049}
1050
1051//- protected members --------------------------------------------------------
1053{
1054// construct python string from the method's signature
1055 return CPyCppyy_PyText_FromString(GetSignatureString(fa).c_str());
1056}
1057
1058//----------------------------------------------------------------------------
1060{
1061 return Cppyy::GetMethodResultType(fMethod);
1062}
#define Py_TYPE(ob)
Definition CPyCppyy.h:196
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()
PyObject * GetSignature(bool show_formalargs=true) override
bool Initialize(CallContext *ctxt=nullptr)
bool ProcessKwds(PyObject *self_in, PyCallArgs &args)
void SetPyError_(PyObject *msg)
virtual bool ProcessArgs(PyCallArgs &args)
int GetPriority() override
virtual bool InitExecutor_(Executor *&, CallContext *ctxt=nullptr)
int GetArgMatchScore(PyObject *args_tuple) override
std::string GetSignatureString(bool show_formalargs=true)
PyObject * ExecuteProtected(void *, ptrdiff_t, CallContext *)
bool ConvertAndSetArgs(CPyCppyy_PyArgs_t, size_t nargsf, CallContext *ctxt=nullptr)
PyObject * GetTypeName() override
PyObject * ExecuteFast(void *, ptrdiff_t, CallContext *)
PyObject * Execute(void *self, ptrdiff_t offset, CallContext *ctxt=nullptr)
Cppyy::TCppFuncAddr_t GetFunctionAddress() override
void Copy_(const CPPMethod &)
Definition CPPMethod.cxx:88
PyObject * GetCoVarNames() override
bool IsConst() override
PyObject * GetPrototype(bool show_formalargs=true) override
Construct a Python string from the method's prototype.
PyObject * GetArgDefault(int iarg, bool silent=true) override
int GetMaxArgs() override
bool VerifyArgCount_(Py_ssize_t)
Definition CPPMethod.cxx:68
bool IsGreedy() override
CPPMethod & operator=(const CPPMethod &)
CPPMethod(Cppyy::TCppScope_t scope, Cppyy::TCppMethod_t method)
PyObject * Reflex(Cppyy::Reflex::RequestId_t request, Cppyy::Reflex::FormatId_t=Cppyy::Reflex::OPTIMAL) override
PyObject * Call(CPPInstance *&self, CPyCppyy_PyArgs_t args, size_t nargsf, PyObject *kwds, CallContext *ctxt=nullptr) override
PyObject * GetScopeProxy() override
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:89