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