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()) {
185 // only drop a reference if there is one to drop: ConstructorExecutor hands
186 // back the address of the new object cast to PyObject*, and decref'ing that
187 // corrupts the heap. Letting it go leaks the object, but this is the error
188 // path of a call that is about to be reported as failed anyway.
189 if (ResultIsPyObject())
191 result = nullptr;
192 }
193#endif
194
195 return result;
196}
197
198//----------------------------------------------------------------------------
200 void* self, ptrdiff_t offset, CallContext* ctxt)
201{
202// helper code to prevent some code duplication; this code embeds a "try/catch"
203// block that saves the call environment for restoration in case of an otherwise
204// fatal signal
205 PyObject* result = 0;
206
207 CLING_EXCEPTION_TRY { // copy call environment to be able to jump back on signal
208 result = ExecuteFast(self, offset, ctxt);
210 // report any outstanding Python exceptions first
211 if (PyErr_Occurred()) {
212 std::cerr << "Python exception outstanding during C++ longjmp:" << std::endl;
213 PyErr_Print();
214 std::cerr << std::endl;
215 }
216
217 // unfortunately, the excodes are not the ones from signal.h, but enums from TSysEvtHandler.h
218 if (excode == 0)
219 PyErr_SetString(gBusException, "bus error in C++; program state was reset");
220 else if (excode == 1)
221 PyErr_SetString(gSegvException, "segfault in C++; program state was reset");
222 else if (excode == 4)
223 PyErr_SetString(gIllException, "illegal instruction in C++; program state was reset");
224 else if (excode == 5)
225 PyErr_SetString(gAbrtException, "abort from C++; program state was reset");
226 else if (excode == 12)
227 PyErr_SetString(PyExc_FloatingPointError, "floating point exception in C++; program state was reset");
228 else
229 PyErr_SetString(PyExc_SystemError, "problem in C++; program state was reset");
230 result = 0;
232
233 return result;
234}
235
236//----------------------------------------------------------------------------
238{
239// build buffers for argument dispatching
240 const size_t nArgs = Cppyy::GetMethodNumArgs(fMethod);
241 fConverters.resize(nArgs);
242
243// setup the dispatch cache
244 for (int iarg = 0; iarg < (int)nArgs; ++iarg) {
245 const std::string& fullType = Cppyy::GetMethodArgType(fMethod, iarg);
247 if (!conv) {
248 PyErr_Format(PyExc_TypeError, "argument type %s not handled", fullType.c_str());
249 return false;
250 }
251
252 fConverters[iarg] = conv;
253 }
254
255 return true;
256}
257
258//----------------------------------------------------------------------------
260{
261// install executor conform to the return type
262 executor = CreateExecutor(
263 (bool)fMethod == true ? Cppyy::GetMethodResultType(fMethod) \
264 : Cppyy::GetScopedFinalName(fScope));
265
266 if (!executor)
267 return false;
268
269 return true;
270}
271
272//----------------------------------------------------------------------------
274{
275// built a signature representation (used for doc strings)
276 return Cppyy::GetMethodSignature(fMethod, fa);
277}
278
279//----------------------------------------------------------------------------
281{
282// Helper to report errors in a consistent format (derefs msg).
283//
284// Handles three cases:
285// 1. No Python error occurred yet:
286// Set a new TypeError with the message "msg" and the docstring of this
287// C++ method to give some context.
288// 2. A C++ exception has occurred:
289// Augment the exception message with the docstring of this method
290// 3. A Python exception has occurred with a traceback:
291// Do nothing, Python exceptions are already informative enough
292// 4. If the Python exception has no traceback hinting to an internally set error stack,
293// extract its message and wrap it with C++ method docstring context.
294
295#if PY_VERSION_HEX >= 0x030c0000
297 PyObject *etype = evalue ? (PyObject *)Py_TYPE(evalue) : nullptr;
298#else
299 PyObject *etype = nullptr;
300 PyObject *evalue = nullptr;
301 PyObject *etrace = nullptr;
302
303 if (PyErr_Occurred()) {
304 PyErr_Fetch(&etype, &evalue, &etrace);
305 }
306#endif
307
309 std::string details;
310
311 // If the error is not a CPPExcInstance and has a traceback, the error from
312 // Python itself is already complete and messing with it would only make it
313 // less informative.
314 // Just restore and return.
315 if (evalue && !isCppExc) {
316#if PY_VERSION_HEX >= 0x030c0000
318 if (tb) {
319 Py_DECREF(tb);
321 return;
322 }
323#else
324 if (etrace) {
325 PyErr_Restore(etype, evalue, etrace);
326 return;
327 }
328#endif
329 // no traceback, extract its message and fall through
331 if (descr) {
334 }
335 }
336
337 PyObject* doc = GetDocString();
338 const char* cdoc = CPyCppyy_PyText_AsString(doc);
339 const char* cmsg = msg ? CPyCppyy_PyText_AsString(msg) : nullptr;
340 PyObject* errtype = etype ? etype : PyExc_TypeError;
342 const char* cname = pyname ? CPyCppyy_PyText_AsString(pyname) : "Exception";
343
344 if (!isCppExc) {
345 // this is the case where no Python error has occured yet, or an internal
346 // one without traceback set a new error with context
347 if (details.empty()) {
348 PyErr_Format(errtype, "%s =>\n %s: %s", cdoc, cname, cmsg ? cmsg : "");
349 } else if (cmsg) {
350 PyErr_Format(errtype, "%s =>\n %s: %s (%s)", cdoc, cname, cmsg, details.c_str());
351 } else {
352 PyErr_Format(errtype, "%s =>\n %s: %s", cdoc, cname, details.c_str());
353 }
354 } else {
355 // augment the top message with context information
356 PyObject *&topMessage = ((CPPExcInstance*)evalue)->fTopMessage;
358 if (msg) {
359 topMessage = CPyCppyy_PyText_FromFormat("%s =>\n %s: %s | ", cdoc, cname, cmsg);
360 } else {
362 }
363 // restore the updated error
364#if PY_VERSION_HEX >= 0x030c0000
366#else
367 PyErr_Restore(etype, evalue, etrace);
368#endif
369 }
370
372 Py_DECREF(doc);
374}
375
376//- constructors and destructor ----------------------------------------------
379 fMethod(method), fScope(scope), fExecutor(nullptr), fArgIndices(nullptr),
380 fArgsRequired(-1)
381{
382 // empty
383}
384
385//----------------------------------------------------------------------------
387 PyCallable(other), fMethod(other.fMethod), fScope(other.fScope)
388{
389 Copy_(other);
390}
391
392//----------------------------------------------------------------------------
394{
395 if (this != &other) {
396 Destroy_();
397 Copy_(other);
398 fScope = other.fScope;
399 fMethod = other.fMethod;
400 }
401
402 return *this;
403}
404
405//----------------------------------------------------------------------------
407{
408 Destroy_();
409}
410
411
412//- public members -----------------------------------------------------------
413/**
414 * @brief Construct a Python string from the method's prototype
415 *
416 * @param fa Show formal arguments of the method
417 * @return PyObject* A Python string with the full method prototype, namespaces included.
418 *
419 * For example, given:
420 *
421 * int foo(int x);
422 *
423 * namespace a {
424 * namespace b {
425 * namespace c {
426 * int foo(int x);
427 * }}}
428 *
429 * This function returns:
430 *
431 * 'int foo(int x)'
432 * 'int a::b::c::foo(int x)'
433 */
435{
436 // Gather the fully qualified final scope of the method. This includes
437 // all namespaces up to the one where the method is declared, for example:
438 // namespace a { namespace b { void foo(); }}
439 // gives
440 // a::b
441 std::string finalscope = Cppyy::GetScopedFinalName(fScope);
442 return CPyCppyy_PyText_FromFormat("%s%s %s%s%s%s",
443 (Cppyy::IsStaticMethod(fMethod) ? "static " : ""),
444 Cppyy::GetMethodResultType(fMethod).c_str(),
445 finalscope.c_str(),
446 (finalscope.empty() ? "" : "::"), // Add final set of '::' if the method is scoped in namespace(s)
447 Cppyy::GetMethodName(fMethod).c_str(),
448 GetSignatureString(fa).c_str());
449}
450
451//----------------------------------------------------------------------------
453{
455 (GetReturnTypeName() + \
456 " (" + (fScope ? Cppyy::GetScopedFinalName(fScope) + "::*)" : "*)")).c_str());
457 CPyCppyy_PyText_AppendAndDel(&cppname, GetSignature(false /* show_formalargs */));
458 return cppname;
459}
460
461//----------------------------------------------------------------------------
463{
464// C++ reflection tooling for methods.
465
466 if (request == Cppyy::Reflex::RETURN_TYPE) {
467 std::string rtn = GetReturnTypeName();
471
473 return CPyCppyy_PyText_FromString(rtn.c_str());
475 if (scope) return CreateScopeProxy(scope);
476 /* TODO: builtins as type */
477 }
478 }
479
480 return PyCallable::Reflex(request, format);
481}
482
483//----------------------------------------------------------------------------
485{
486// To help with overload selection, methods are given a priority based on the
487// affinity of Python and C++ types. Priority only matters for methods that have
488// an equal number of arguments and types that are possible substitutes (the
489// normal selection mechanisms would simply distinguish them otherwise).
490
491// The following types are ordered, in favor (variants implicit):
492//
493// bool >> long >> int >> short
494// double >> long double >> float
495// const char* >> char
496//
497// Further, all integer types are preferred over floating point b/c int to float
498// is allowed implicitly, float to int is not.
499//
500// Special cases that are disliked include void* and unknown/incomplete types.
501// Also, moves are preferred over references. std::initializer_list is not a nice
502// conversion candidate either, but needs to be higher priority to mix well with
503// implicit conversions.
504// TODO: extend this to favour classes that are not bases.
505// TODO: profile this method (it's expensive, but should be called too often)
506
507 int priority = 0;
508
509 const size_t nArgs = Cppyy::GetMethodNumArgs(fMethod);
510 for (int iarg = 0; iarg < (int)nArgs; ++iarg) {
511 const std::string aname = Cppyy::GetMethodArgType(fMethod, iarg);
512
513 if (Cppyy::IsBuiltin(aname)) {
514 // complex type (note: double penalty: for complex and the template type)
515 if (strstr(aname.c_str(), "std::complex"))
516 priority -= 10; // prefer double, float, etc. over conversion
517
518 // integer types
519 if (strstr(aname.c_str(), "bool"))
520 priority += 1; // bool over int (does accept 1 and 0)
521 else if (strstr(aname.c_str(), "long long"))
522 priority += -5; // will very likely fit
523 else if (strstr(aname.c_str(), "long"))
524 priority += -10; // most affine integer type
525 // no need to compare with int; leave at zero
526 else if (strstr(aname.c_str(), "short"))
527 priority += -50; // not really relevant as a type
528
529 // floating point types (note all numbers lower than integer types)
530 else if (strstr(aname.c_str(), "float"))
531 priority += -100; // not really relevant as a type
532 else if (strstr(aname.c_str(), "long double"))
533 priority += -90; // fits double with least loss of precision
534 else if (strstr(aname.c_str(), "double"))
535 priority += -80; // most affine floating point type
536
537 // string/char types
538 else if (strstr(aname.c_str(), "char") && aname[aname.size()-1] != '*')
539 priority += -60; // prefer (const) char* over char
540
541 // oddball
542 else if (strstr(aname.c_str(), "void*"))
543 priority -= 1000; // void*/void** shouldn't be too greedy
544
545 } else {
546 // This is a user-defined type (class, struct, enum, etc.).
547
548 // There's a bit of hysteresis here for templates: once GetScope() is called, their
549 // IsComplete() succeeds, the other way around it does not. Since GetPriority() is
550 // likely called several times in a sort, the GetScope() _must_ come first, or
551 // different GetPriority() calls may return different results (since the 2nd time,
552 // GetScope() will have been called from the first), killing the stable_sort.
553
554 // prefer more derived classes
555 const std::string& clean_name = TypeManip::clean_type(aname, false);
557 if (scope)
558 priority += static_cast<int>(Cppyy::GetNumBasesLongestBranch(scope));
559
561 priority -= 100;
562
563 // a couple of special cases as explained above
564 if (aname.find("initializer_list") != std::string::npos) {
565 priority += 150; // needed for proper implicit conversion rules
566 } else if (aname.rfind("&&", aname.size()-2) != std::string::npos) {
567 priority += 100; // prefer moves over other ref/ptr
568 } else if (scope && !Cppyy::IsComplete(clean_name)) {
569 // class is known, but no dictionary available, 2 more cases: * and &
570 if (aname[aname.size() - 1] == '&')
571 priority += -5000;
572 else
573 priority += -2000; // prefer pointer passing over reference
574 }
575 }
576 }
577
578// prefer methods w/o optional arguments b/c ones with optional arguments are easier to
579// select by providing the optional arguments explicitly
580 priority += ((int)Cppyy::GetMethodReqArgs(fMethod) - (int)nArgs);
581
582// add a small penalty to prefer non-const methods over const ones for get/setitem
583 if (Cppyy::IsConstMethod(fMethod) && Cppyy::GetMethodName(fMethod) == "operator[]")
584 priority += -10;
585
586 return priority;
587}
588
589//----------------------------------------------------------------------------
591{
592// Methods will all void*-like arguments should be sorted after template
593// instanstations, so that they don't greedily take over pointers to object.
594// GetPriority() is too heavy-handed, as it will pull in all the argument
595// types, so use this cheaper check.
596 const size_t nArgs = Cppyy::GetMethodReqArgs(fMethod);
597 if (!nArgs) return false;
598
599 for (int iarg = 0; iarg < (int)nArgs; ++iarg) {
600 const std::string aname = Cppyy::GetMethodArgType(fMethod, iarg);
601 if (aname.find("void*") != 0)
602 return false;
603 }
604 return true;
605}
606
607
608//----------------------------------------------------------------------------
610{
611 return (int)Cppyy::GetMethodNumArgs(fMethod);
612}
613
614//----------------------------------------------------------------------------
616{
617// Build a tuple of the argument types/names.
618 int co_argcount = (int)GetMaxArgs() /* +1 for self */;
619
620// TODO: static methods need no 'self' (but is harmless otherwise)
621
624 for (int iarg = 0; iarg < co_argcount; ++iarg) {
625 std::string argrep = Cppyy::GetMethodArgType(fMethod, iarg);
626 const std::string& parname = Cppyy::GetMethodArgName(fMethod, iarg);
627 if (!parname.empty()) {
628 argrep += " ";
629 argrep += parname;
630 }
631
634 }
635
636 return co_varnames;
637}
638
640{
641// get and evaluate the default value (if any) of argument iarg of this method
642 if (iarg >= (int)GetMaxArgs())
643 return nullptr;
644
645// borrowed reference to cppyy.gbl module to use its dictionary to eval in
646 static PyObject* gbl = PyDict_GetItemString(PySys_GetObject((char*)"modules"), "cppyy.gbl");
647
648 std::string defvalue = Cppyy::GetMethodArgDefault(fMethod, iarg);
649 if (!defvalue.empty()) {
651 if (!(dctptr && *dctptr))
652 return nullptr;
653
654 PyObject* gdct = *dctptr;
655 PyObject* scope = nullptr;
656
657 if (defvalue.rfind('(') != std::string::npos) { // constructor-style call
658 // try to tickle scope creation, just in case, first look in the scope where
659 // the function lives, then in the global scope
660 std::string possible_scope = defvalue.substr(0, defvalue.rfind('('));
662 std::string cand_scope = Cppyy::GetScopedFinalName(fScope)+"::"+possible_scope;
664 if (!scope) {
665 PyErr_Clear();
666 // search within the global scope instead
668 if (!scope) PyErr_Clear();
669 } else {
670 // re-scope the scope; alternatively, the expression could be
671 // compiled in the dictionary of the function's namespace, but
672 // that would affect arguments passed to the constructor, too
673 defvalue = cand_scope + defvalue.substr(defvalue.rfind('('), std::string::npos);
674 }
675 }
676 }
677
678 // replace '::' -> '.'
680
681 if (!scope) {
682 // a couple of common cases that python doesn't like (technically, 'L' is okay with older
683 // pythons, but C long will always fit in Python int, so no need to bother)
684 char c = defvalue.back();
685 if (c == 'F' || c == 'D' || c == 'L') {
686 int offset = 1;
687 if (2 < defvalue.size() && defvalue[defvalue.size()-2] == 'U')
688 offset = 2;
689 defvalue = defvalue.substr(0, defvalue.size()-offset);
690 } else if (defvalue == "true") {
691 defvalue = "True";
692 } else if (defvalue == "false") {
693 defvalue = "False";
694 }
695 }
696
697 // attempt to evaluate the string representation (compilation is first to code to allow
698 // the error message to indicate where it's coming from)
699 PyObject* pyval = nullptr;
700
701 PyObject* pycode = Py_CompileString((char*)defvalue.c_str(), "cppyy_default_compiler", Py_eval_input);
702 if (pycode) {
704#if PY_VERSION_HEX < 0x03000000
705 (PyCodeObject*)
706#endif
707 pycode, gdct, gdct);
709 }
710
711 if (!pyval && PyErr_Occurred() && silent) {
712 PyErr_Clear();
713 pyval = CPyCppyy_PyText_FromString(defvalue.c_str()); // allows continuation, but is likely to fail
714 }
715
717 return pyval; // may be nullptr
718 }
719
720 PyErr_Format(PyExc_TypeError, "Could not construct default value for: %s", Cppyy::GetMethodArgName(fMethod, iarg).c_str());
721 return nullptr;
722}
723
724
726 return Cppyy::IsConstMethod(GetMethod());
727}
728
729
730//----------------------------------------------------------------------------
732{
733// Get or build the scope of this method.
734 return CreateScopeProxy(fScope);
735}
736
737
738//----------------------------------------------------------------------------
740{
741// Return the C++ pointer of this function
742 return Cppyy::GetFunctionAddress(fMethod, false /* don't check fast path envar */);
743}
744
745//----------------------------------------------------------------------------
747{
749
750 int req_args = Cppyy::GetMethodReqArgs(fMethod);
751
752 // Not enough arguments supplied: no match
753 if (req_args > n)
754 return INT_MAX;
755
756 size_t score = 0;
757 for (int i = 0; i < n; i++) {
760 PyErr_SetString(PyExc_TypeError, "argument types should be in string format");
761 return INT_MAX;
762 }
764
765 size_t arg_score = Cppyy::CompareMethodArgType(fMethod, i, req_type);
766
767 // Method is not compatible if even one argument does not match
768 if (arg_score >= 10) {
769 score = INT_MAX;
770 break;
771 }
772
773 score += arg_score;
774 }
775
776 return score;
777}
778
779//----------------------------------------------------------------------------
781{
782// done if cache is already setup
783 if (fArgsRequired != -1)
784 return true;
785
786 if (!InitConverters_())
787 return false;
788
789 if (!InitExecutor_(fExecutor, ctxt))
790 return false;
791
792// minimum number of arguments when calling
793 fArgsRequired = (int)((bool)fMethod == true ? Cppyy::GetMethodReqArgs(fMethod) : 0);
794
795 return true;
796}
797
798//----------------------------------------------------------------------------
800{
801#if PY_VERSION_HEX >= 0x03080000
802 if (!PyTuple_CheckExact(cargs.fKwds)) {
803 SetPyError_(CPyCppyy_PyText_FromString("received unknown keyword names object"));
804 return false;
805 }
807#else
808 if (!PyDict_CheckExact(cargs.fKwds)) {
809 SetPyError_(CPyCppyy_PyText_FromString("received unknown keyword arguments object"));
810 return false;
811 }
813#endif
814
815 if (nKeys == 0 && !self_in)
816 return true;
817
818 if (!fArgIndices) {
819 fArgIndices = new std::map<std::string, int>{};
820 for (int iarg = 0; iarg < (int)Cppyy::GetMethodNumArgs(fMethod); ++iarg)
821 (*fArgIndices)[Cppyy::GetMethodArgName(fMethod, iarg)] = iarg;
822 }
823
824 Py_ssize_t nArgs = CPyCppyy_PyArgs_GET_SIZE(cargs.fArgs, cargs.fNArgsf) + (self_in ? 1 : 0);
825 if (!VerifyArgCount_(nArgs+nKeys))
826 return false;
827
828 std::vector<PyObject*> vArgs{fConverters.size()};
829
830// next, insert the keyword values
831 PyObject *key, *value;
832 Py_ssize_t maxpos = -1;
833
834#if PY_VERSION_HEX >= 0x03080000
836 for (Py_ssize_t ikey = 0; ikey < nKeys; ++ikey) {
837 key = PyTuple_GET_ITEM(cargs.fKwds, ikey);
838 value = cargs.fArgs[npos_args+ikey];
839#else
840 Py_ssize_t pos = 0;
841 while (PyDict_Next(cargs.fKwds, &pos, &key, &value)) {
842#endif
843 const char* ckey = CPyCppyy_PyText_AsStringChecked(key);
844 if (!ckey)
845 return false;
846
847 auto p = fArgIndices->find(ckey);
848 if (p == fArgIndices->end()) {
849 SetPyError_(CPyCppyy_PyText_FromFormat("%s::%s got an unexpected keyword argument \'%s\'",
850 Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetMethodName(fMethod).c_str(), ckey));
851 return false;
852 }
853
854 maxpos = p->second > maxpos ? p->second : maxpos;
855 vArgs[p->second] = value; // no INCREF yet for simple cleanup in case of error
856 }
857
858// if maxpos < nArgs, it will be detected & reported as a duplicate below
861
862// set all values to zero to be able to check them later (this also guarantees normal
863// cleanup by the tuple deallocation)
864 for (Py_ssize_t i = 0; i < maxargs; ++i)
866
867// fill out the positional arguments
868 Py_ssize_t start = 0;
869 if (self_in) {
872 start = 1;
873 }
874
875 for (Py_ssize_t i = start; i < nArgs; ++i) {
876 if (vArgs[i]) {
877 SetPyError_(CPyCppyy_PyText_FromFormat("%s::%s got multiple values for argument %d",
878 Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetMethodName(fMethod).c_str(), (int)i+1));
880 return false;
881 }
882
886 }
887
888// fill out the keyword arguments
889 for (Py_ssize_t i = nArgs; i < maxargs; ++i) {
890 PyObject* item = vArgs[i];
891 if (item) {
894 } else {
895 // try retrieving the default
896 item = GetArgDefault((int)i, false /* i.e. not silent */);
897 if (!item) {
899 return false;
900 }
902 }
903 }
904
905#if PY_VERSION_HEX >= 0x03080000
906 if (cargs.fFlags & PyCallArgs::kDoFree) {
907 if (cargs.fFlags & PyCallArgs::kIsOffset)
908 cargs.fArgs -= 1;
909#else
910 if (cargs.fFlags & PyCallArgs::kDoDecref) {
911#endif
913 }
914
915 cargs.fArgs = newArgs;
916 cargs.fNArgsf = maxargs;
917#if PY_VERSION_HEX >= 0x03080000
918 cargs.fFlags = PyCallArgs::kDoFree | PyCallArgs::kDoItemDecref;
919#else
921#endif
922
923 return true;
924}
925
926//----------------------------------------------------------------------------
928{
929// verify existence of self, return if ok
930 if (cargs.fSelf) {
931 if (cargs.fKwds) { return ProcessKwds(nullptr, cargs); }
932 return true;
933 }
934
935// otherwise, check for a suitable 'self' in args and update accordingly
936 if (CPyCppyy_PyArgs_GET_SIZE(cargs.fArgs, cargs.fNArgsf) != 0) {
938
939 // demand CPyCppyy object, and an argument that may match down the road
941 Cppyy::TCppType_t oisa = pyobj->ObjectIsA();
942 if (fScope == Cppyy::gGlobalScope || // free global
943 oisa == 0 || // null pointer or ctor call
944 oisa == fScope || // matching types
945 Cppyy::IsSubtype(oisa, fScope)) { // id.
946
947 // reset self
948 Py_INCREF(pyobj); // corresponding Py_DECREF is in CPPOverload
949 cargs.fSelf = pyobj;
950
951 // offset args by 1
952#if PY_VERSION_HEX >= 0x03080000
953 cargs.fArgs += 1;
955#else
956 if (cargs.fFlags & PyCallArgs::kDoDecref)
957 Py_DECREF((PyObject*)cargs.fArgs);
958 cargs.fArgs = PyTuple_GetSlice(cargs.fArgs, 1, PyTuple_GET_SIZE(cargs.fArgs));
960#endif
961 cargs.fNArgsf -= 1;
962
963 // put the keywords, if any, in their places in the arguments array
964 if (cargs.fKwds)
965 return ProcessKwds(nullptr, cargs);
966 return true;
967 }
968 }
969 }
970
971// no self, set error and lament
972 SetPyError_(CPyCppyy_PyText_FromFormat(
973 "unbound method %s::%s must be called with a %s instance as first argument",
974 Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetMethodName(fMethod).c_str(),
975 Cppyy::GetFinalName(fScope).c_str()));
976 return false;
977}
978
979//----------------------------------------------------------------------------
981{
983 if (!VerifyArgCount_(argc))
984 return false;
985
986// pass current scope for which the call is made
987 ctxt->fCurScope = fScope;
988
989 if (argc == 0)
990 return true;
991
992// convert the arguments to the method call array
993 bool isOK = true;
994 Parameter* cppArgs = ctxt->GetArgs(argc);
995 for (int i = 0; i < (int)argc; ++i) {
996 if (!fConverters[i]->SetArg(CPyCppyy_PyArgs_GET_ITEM(args, i), cppArgs[i], ctxt)) {
997 SetPyError_(CPyCppyy_PyText_FromFormat("could not convert argument %d", i+1));
998 isOK = false;
999 break;
1000 }
1001 }
1002
1003 return isOK;
1004}
1005
1006//----------------------------------------------------------------------------
1008{
1009// call the interface method
1010 PyObject* result = 0;
1011
1013 // bypasses try block (i.e. segfaults will abort)
1014 result = ExecuteFast(self, offset, ctxt);
1015 } else {
1016 // at the cost of ~10% performance, don't abort the interpreter on any signal
1017 result = ExecuteProtected(self, offset, ctxt);
1018 }
1019
1020 if (!result && PyErr_Occurred())
1021 SetPyError_(0);
1022
1023 return result;
1024}
1025
1026//----------------------------------------------------------------------------
1029{
1030// setup as necessary
1031 if (fArgsRequired == -1 && !Initialize(ctxt))
1032 return nullptr;
1033
1034// fetch self, verify, and put the arguments in usable order
1035 PyCallArgs cargs{self, args, nargsf, kwds};
1036 if (!ProcessArgs(cargs))
1037 return nullptr;
1038
1039// self provides the python context for lifelines
1040 if (!ctxt->fPyContext)
1041 ctxt->fPyContext = (PyObject*)cargs.fSelf; // no Py_INCREF as no ownership
1042
1043// translate the arguments
1044 if (fArgsRequired || CPyCppyy_PyArgs_GET_SIZE(args, cargs.fNArgsf)) {
1045 if (!ConvertAndSetArgs(cargs.fArgs, cargs.fNArgsf, ctxt))
1046 return nullptr;
1047 }
1048
1049// get the C++ object that this object proxy is a handle for
1050 void* object = self->GetObject();
1051
1052// validity check that should not fail
1053 if (!object) {
1054 PyErr_SetString(PyExc_ReferenceError, "attempt to access a null-pointer");
1055 return nullptr;
1056 }
1057
1058// get its class
1059 Cppyy::TCppType_t derived = self->ObjectIsA();
1060
1061// calculate offset (the method expects 'this' to be an object of fScope)
1062 ptrdiff_t offset = 0;
1063 if (derived && derived != fScope)
1064 offset = Cppyy::GetBaseOffset(derived, fScope, object, 1 /* up-cast */);
1065
1066// actual call; recycle self instead of returning new object for same address objects
1067 CPPInstance* pyobj = (CPPInstance*)Execute(object, offset, ctxt);
1068 if (CPPInstance_Check(pyobj) &&
1069 derived && pyobj->ObjectIsA() == derived &&
1070 pyobj->GetObject() == object) {
1073 return (PyObject*)self;
1074 }
1075
1076 return (PyObject*)pyobj;
1077}
1078
1079//- protected members --------------------------------------------------------
1081{
1082// construct python string from the method's signature
1083 return CPyCppyy_PyText_FromString(GetSignatureString(fa).c_str());
1084}
1085
1086/**
1087 * @brief Returns a tuple with the names of the input parameters of this method.
1088 *
1089 * For example given a function with prototype:
1090 *
1091 * double foo(int a, float b, double c)
1092 *
1093 * this function returns:
1094 *
1095 * ('a', 'b', 'c')
1096 */
1098{
1099 // Build a tuple of the argument names for this signature.
1100 int argcount = GetMaxArgs();
1102
1103 for (int iarg = 0; iarg < argcount; ++iarg) {
1104 const std::string &argname_cpp = Cppyy::GetMethodArgName(fMethod, iarg);
1107 }
1108
1109 return signature_names;
1110}
1111
1112/**
1113 * @brief Returns a dictionary with the types of the signature of this method.
1114 *
1115 * This dictionary will store both the return type and the input parameter
1116 * types of this method, respectively with keys "return_type" and
1117 * "input_types", for example given a function with prototype:
1118 *
1119 * double foo(int a, float b, double c)
1120 *
1121 * this function returns:
1122 *
1123 * {'input_types': ('int', 'float', 'double'), 'return_type': 'double'}
1124 */
1126{
1127
1129
1130 // Insert the return type first
1131 std::string return_type = GetReturnTypeName();
1134
1135 // Build a tuple of the argument types for this signature.
1136 int argcount = GetMaxArgs();
1138
1139 for (int iarg = 0; iarg < argcount; ++iarg) {
1140 const std::string &argtype_cpp = Cppyy::GetMethodArgType(fMethod, iarg);
1143 }
1144
1146
1147 return signature_types_dict;
1148}
1149
1150//----------------------------------------------------------------------------
1152{
1153 return Cppyy::GetMethodResultType(fMethod);
1154}
#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()