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 <assert.h>
18#include <string.h>
19#include <exception>
20#include <sstream>
21#include <string>
22#include <typeinfo>
23#include <memory>
24
25
26//- data and local helpers ---------------------------------------------------
27namespace CPyCppyy {
28 extern PyObject* gThisModule;
29 extern PyObject* gBusException;
31 extern PyObject* gIllException;
33}
34
35
36//- private helpers ----------------------------------------------------------
37inline void CPyCppyy::CPPMethod::Copy_(const CPPMethod& /* other */)
38{
39// fScope and fMethod handled separately
40
41// do not copy caches
42 fExecutor = nullptr;
43 fArgIndices = nullptr;
44 fArgsRequired = -1;
45}
46
47//----------------------------------------------------------------------------
49{
50// destroy executor and argument converters
51 if (fExecutor && fExecutor->HasState()) delete fExecutor;
52
53 for (auto p : fConverters) {
54 if (p && p->HasState()) delete p;
55 }
56
57 delete fArgIndices;
58
59 fExecutor = nullptr;
60 fArgIndices = nullptr;
61 fConverters.clear();
62 fArgsRequired = -1;
63}
64
65//----------------------------------------------------------------------------
67 void* self, ptrdiff_t offset, CallContext* ctxt)
68{
69// call into C++ through fExecutor; abstracted out from Execute() to prevent some
70// code duplication with ProtectedCall()
71 PyObject* result = nullptr;
72
73 try { // C++ try block
74 result = fExecutor->Execute(fMethod, (Cppyy::TCppObject_t)((intptr_t)self+offset), ctxt);
75 } catch (PyException&) {
76 result = nullptr; // error already set
77 } catch (std::exception& e) {
78 // attempt to set the exception to the actual type, to allow catching with the Python C++ type
79 static Cppyy::TCppType_t exc_type = (Cppyy::TCppType_t)Cppyy::GetScope("std::exception");
80
81 PyObject* pyexc_type = nullptr;
82 PyObject* pyexc_obj = nullptr;
83
84 // TODO: factor this code with the same in ProxyWrappers (and cache it there to be able to
85 // look up based on TCppType_t):
86 Cppyy::TCppType_t actual = Cppyy::GetActualClass(exc_type, &e);
87 const std::string& finalname = Cppyy::GetScopedFinalName(actual);
88 const std::string& parentname = TypeManip::extract_namespace(finalname);
89 PyObject* parent = CreateScopeProxy(parentname);
90 if (parent) {
91 pyexc_type = PyObject_GetAttrString(parent,
92 parentname.empty() ? finalname.c_str() : finalname.substr(parentname.size()+2, std::string::npos).c_str());
93 Py_DECREF(parent);
94 }
95
96 if (pyexc_type) {
97 // create a copy of the exception (TODO: factor this code with the same in ProxyWrappers)
98 PyObject* pyclass = CPyCppyy::GetScopeProxy(actual);
99 PyObject* source = BindCppObjectNoCast(&e, actual);
100 PyObject* pyexc_copy = PyObject_CallFunctionObjArgs(pyclass, source, nullptr);
101 Py_DECREF(source);
102 Py_DECREF(pyclass);
103 if (pyexc_copy) {
104 pyexc_obj = CPPExcInstance_Type.tp_new((PyTypeObject*)pyexc_type, nullptr, nullptr);
105 ((CPPExcInstance*)pyexc_obj)->fCppInstance = (PyObject*)pyexc_copy;
106 } else
107 PyErr_Clear();
108 } else
109 PyErr_Clear();
110
111 if (pyexc_type && pyexc_obj) {
112 PyErr_SetObject(pyexc_type, pyexc_obj);
113 Py_DECREF(pyexc_obj);
114 Py_DECREF(pyexc_type);
115 } else {
116 PyErr_Format(PyExc_Exception, "%s (C++ exception)", e.what());
117 Py_XDECREF(pyexc_obj);
118 Py_XDECREF(pyexc_type);
119 }
120
121 result = nullptr;
122 } catch (...) {
123 PyErr_SetString(PyExc_Exception, "unhandled, unknown C++ exception");
124 result = nullptr;
125 }
126
127// TODO: covers the PyException throw case, which does not seem to work on Windows, so
128// instead leaves the error be
129#ifdef _WIN32
130 if (PyErr_Occurred()) {
131 Py_XDECREF(result);
132 result = nullptr;
133 }
134#endif
135
136 return result;
137}
138
139//----------------------------------------------------------------------------
141 void* self, ptrdiff_t offset, CallContext* ctxt)
142{
143// helper code to prevent some code duplication; this code embeds a "try/catch"
144// block that saves the call environment for restoration in case of an otherwise
145// fatal signal
146 PyObject* result = 0;
147
148 TRY { // copy call environment to be able to jump back on signal
149 result = ExecuteFast(self, offset, ctxt);
150 } CATCH(excode) {
151 // Unfortunately, the excodes are not the ones from signal.h, but enums from TSysEvtHandler.h
152 if (excode == 0)
153 PyErr_SetString(gBusException, "bus error in C++; program state was reset");
154 else if (excode == 1)
155 PyErr_SetString(gSegvException, "segfault in C++; program state was reset");
156 else if (excode == 4)
157 PyErr_SetString(gIllException, "illegal instruction in C++; program state was reset");
158 else if (excode == 5)
159 PyErr_SetString(gAbrtException, "abort from C++; program state was reset");
160 else if (excode == 12)
161 PyErr_SetString(PyExc_FloatingPointError, "floating point exception in C++; program state was reset");
162 else
163 PyErr_SetString(PyExc_SystemError, "problem in C++; program state was reset");
164 result = 0;
165 } ENDTRY;
166
167 return result;
168}
169
170//----------------------------------------------------------------------------
172{
173// build buffers for argument dispatching
174 const size_t nArgs = Cppyy::GetMethodNumArgs(fMethod);
175 fConverters.resize(nArgs);
176
177// setup the dispatch cache
178 for (int iarg = 0; iarg < (int)nArgs; ++iarg) {
179 const std::string& fullType = Cppyy::GetMethodArgType(fMethod, iarg);
180 Converter* conv = CreateConverter(fullType);
181 if (!conv) {
182 PyErr_Format(PyExc_TypeError, "argument type %s not handled", fullType.c_str());
183 return false;
184 }
185
186 fConverters[iarg] = conv;
187 }
188
189 return true;
190}
191
192//----------------------------------------------------------------------------
194{
195// install executor conform to the return type
196 executor = CreateExecutor(
197 (bool)fMethod == true ? Cppyy::GetMethodResultType(fMethod) \
198 : Cppyy::GetScopedFinalName(fScope));
199
200 if (!executor)
201 return false;
202
203 return true;
204}
205
206//----------------------------------------------------------------------------
208{
209// built a signature representation (used for doc strings)
210 std::stringstream sig; sig << "(";
211 int count = 0;
212 const size_t nArgs = Cppyy::GetMethodNumArgs(fMethod);
213 for (int iarg = 0; iarg < (int)nArgs; ++iarg) {
214 if (count) sig << (fa ? ", " : ",");
215
216 sig << Cppyy::GetMethodArgType(fMethod, iarg);
217
218 if (fa) {
219 const std::string& parname = Cppyy::GetMethodArgName(fMethod, iarg);
220 if (!parname.empty())
221 sig << " " << parname;
222
223 const std::string& defvalue = Cppyy::GetMethodArgDefault(fMethod, iarg);
224 if (!defvalue.empty())
225 sig << " = " << defvalue;
226 }
227 count++;
228 }
229 sig << ")";
230 return sig.str();
231}
232
233//----------------------------------------------------------------------------
235{
236// helper to report errors in a consistent format (derefs msg)
237 std::string details{};
238
239 PyObject *etype = nullptr, *evalue = nullptr;
240 if (PyErr_Occurred()) {
241 PyObject* etrace = nullptr;
242
243 PyErr_Fetch(&etype, &evalue, &etrace);
244
245 if (evalue) {
246 PyObject* descr = PyObject_Str(evalue);
247 if (descr) {
248 details = CPyCppyy_PyText_AsString(descr);
249 Py_DECREF(descr);
250 }
251 }
252
253 Py_XDECREF(etrace);
254 }
255
256 PyObject* doc = GetDocString();
257 PyObject* errtype = etype;
258 if (!errtype)
259 errtype = PyExc_TypeError;
260 PyObject* pyname = PyObject_GetAttr(errtype, PyStrings::gName);
261 const char* cname = pyname ? CPyCppyy_PyText_AsString(pyname) : "Exception";
262
263 if (!PyType_IsSubtype((PyTypeObject*)errtype, &CPPExcInstance_Type)) {
264 if (details.empty()) {
265 PyErr_Format(errtype, "%s =>\n %s: %s", CPyCppyy_PyText_AsString(doc),
266 cname, msg ? CPyCppyy_PyText_AsString(msg) : "");
267 } else if (msg) {
268 PyErr_Format(errtype, "%s =>\n %s: %s (%s)",
270 details.c_str());
271 } else {
272 PyErr_Format(errtype, "%s =>\n %s: %s",
273 CPyCppyy_PyText_AsString(doc), cname, details.c_str());
274 }
275 } else if (evalue) {
276 Py_XDECREF(((CPPExcInstance*)evalue)->fTopMessage);
277 if (msg) {
278 ((CPPExcInstance*)evalue)->fTopMessage = CPyCppyy_PyText_FromFormat(\
279 "%s =>\n %s: %s | ", CPyCppyy_PyText_AsString(doc), cname, CPyCppyy_PyText_AsString(msg));
280 } else {
281 ((CPPExcInstance*)evalue)->fTopMessage = CPyCppyy_PyText_FromFormat(\
282 "%s =>\n %s: ", CPyCppyy_PyText_AsString(doc), cname);
283 }
284 PyErr_SetObject(errtype, evalue);
285 }
286
287 Py_XDECREF(pyname);
288 Py_XDECREF(evalue);
289 Py_XDECREF(etype);
290 Py_DECREF(doc);
291 Py_XDECREF(msg);
292}
293
294//- constructors and destructor ----------------------------------------------
297 fMethod(method), fScope(scope), fExecutor(nullptr), fArgIndices(nullptr),
298 fArgsRequired(-1)
299{
300 // empty
301}
302
303//----------------------------------------------------------------------------
305 PyCallable(other), fMethod(other.fMethod), fScope(other.fScope)
306{
307 Copy_(other);
308}
309
310//----------------------------------------------------------------------------
312{
313 if (this != &other) {
314 Destroy_();
315 Copy_(other);
316 fScope = other.fScope;
317 fMethod = other.fMethod;
318 }
319
320 return *this;
321}
322
323//----------------------------------------------------------------------------
325{
326 Destroy_();
327}
328
329
330//- public members -----------------------------------------------------------
332{
333// construct python string from the method's prototype
334 return CPyCppyy_PyText_FromFormat("%s%s %s::%s%s",
335 (Cppyy::IsStaticMethod(fMethod) ? "static " : ""),
336 Cppyy::GetMethodResultType(fMethod).c_str(),
337 Cppyy::GetScopedFinalName(fScope).c_str(), Cppyy::GetMethodName(fMethod).c_str(),
338 GetSignatureString(fa).c_str());
339}
340//----------------------------------------------------------------------------
342{
343// C++ reflection tooling for methods.
344
345 if (request == Cppyy::Reflex::RETURN_TYPE) {
346 std::string rtn = GetReturnTypeName();
347 Cppyy::TCppScope_t scope = 0;
349 scope = Cppyy::GetScope(rtn);
350
352 return CPyCppyy_PyText_FromString(rtn.c_str());
354 if (scope) return CreateScopeProxy(scope);
355 /* TODO: builtins as type */
356 }
357 }
358
359 return PyCallable::Reflex(request, format);
360}
361
362//----------------------------------------------------------------------------
364{
365// To help with overload selection, methods are given a priority based on the
366// affinity of Python and C++ types. Priority only matters for methods that have
367// an equal number of arguments and types that are possible substitutes (the
368// normal selection mechanisms would simply distinguish them otherwise).
369
370// The following types are ordered, in favor (variants implicit):
371//
372// bool >> long >> int >> short
373// double >> long double >> float
374// const char* >> char
375//
376// Further, all integer types are preferred over floating point b/c int to float
377// is allowed implicitly, float to int is not.
378//
379// Special cases that are disliked include void* and unknown/incomplete types.
380// Also, moves are preferred over references. std::initializer_list is not a nice
381// conversion candidate either, but needs to be higher priority to mix well with
382// implicit conversions.
383// TODO: extend this to favour classes that are not bases.
384// TODO: profile this method (it's expensive, but should be called too often)
385
386 int priority = 0;
387
388 const size_t nArgs = Cppyy::GetMethodNumArgs(fMethod);
389 for (int iarg = 0; iarg < (int)nArgs; ++iarg) {
390 const std::string aname = Cppyy::GetMethodArgType(fMethod, iarg);
391
392 if (Cppyy::IsBuiltin(aname)) {
393 // integer types
394 if (strstr(aname.c_str(), "bool"))
395 priority += 1; // bool over int (does accept 1 and 0)
396 else if (strstr(aname.c_str(), "long long"))
397 priority += -5; // will very likely fit
398 else if (strstr(aname.c_str(), "long"))
399 priority += -10; // most affine integer type
400 // no need to compare with int; leave at zero
401 else if (strstr(aname.c_str(), "short"))
402 priority += -50; // not really relevant as a type
403
404 // floating point types (note all numbers lower than integer types)
405 else if (strstr(aname.c_str(), "float"))
406 priority += -100; // not really relevant as a type
407 else if (strstr(aname.c_str(), "long double"))
408 priority += -90; // fits double with least loss of precision
409 else if (strstr(aname.c_str(), "double"))
410 priority += -80; // most affine floating point type
411
412 // string/char types
413 else if (strstr(aname.c_str(), "char") && aname[aname.size()-1] != '*')
414 priority += -60; // prefer (const) char* over char
415
416 // oddball
417 else if (strstr(aname.c_str(), "void*"))
418 priority -= 1000; // void*/void** shouldn't be too greedy
419
420 } else {
421 // This is a user-defined type (class, struct, enum, etc.).
422
423 // There's a bit of hysteresis here for templates: once GetScope() is called, their
424 // IsComplete() succeeds, the other way around it does not. Since GetPriority() is
425 // likely called several times in a sort, the GetScope() _must_ come first, or
426 // different GetPriority() calls may return different results (since the 2nd time,
427 // GetScope() will have been called from the first), killing the stable_sort.
428
429 // prefer more derived classes
430 const std::string& clean_name = TypeManip::clean_type(aname, false);
431 Cppyy::TCppScope_t scope = Cppyy::GetScope(clean_name);
432 if (scope)
433 priority += static_cast<int>(Cppyy::GetNumBasesLongestBranch(scope));
434
435 if (Cppyy::IsEnum(clean_name))
436 priority -= 100;
437
438 // a couple of special cases as explained above
439 if (aname.find("initializer_list") != std::string::npos) {
440 priority += 150; // needed for proper implicit conversion rules
441 } else if (aname.rfind("&&", aname.size()-2) != std::string::npos) {
442 priority += 100; // prefer moves over other ref/ptr
443 } else if (!aname.empty() && !Cppyy::IsComplete(aname)) {
444 // class is known, but no dictionary available, 2 more cases: * and &
445 if (aname[aname.size() - 1] == '&')
446 priority += -5000;
447 else
448 priority += -2000; // prefer pointer passing over reference
449 }
450 }
451 }
452
453// prefer methods w/o optional arguments b/c ones with optional arguments are easier to
454// select by providing the optional arguments explicitly
455 priority += ((int)Cppyy::GetMethodReqArgs(fMethod) - (int)nArgs);
456
457// add a small penalty to prefer non-const methods over const ones for get/setitem
458 if (Cppyy::IsConstMethod(fMethod) && Cppyy::GetMethodName(fMethod) == "operator[]")
459 priority += -10;
460
461 return priority;
462}
463
464//----------------------------------------------------------------------------
466{
467// Methods will all void*-like arguments should be sorted after template
468// instanstations, so that they don't greedily take over pointers to object.
469// GetPriority() is too heavy-handed, as it will pull in all the argument
470// types, so use this cheaper check.
471 const size_t nArgs = Cppyy::GetMethodReqArgs(fMethod);
472 if (!nArgs) return false;
473
474 for (int iarg = 0; iarg < (int)nArgs; ++iarg) {
475 const std::string aname = Cppyy::GetMethodArgType(fMethod, iarg);
476 if (aname.find("void*") != 0)
477 return false;
478 }
479 return true;
480}
481
482
483//----------------------------------------------------------------------------
485{
486 return (int)Cppyy::GetMethodNumArgs(fMethod);
487}
488
489//----------------------------------------------------------------------------
491{
492// Build a tuple of the argument types/names.
493 int co_argcount = (int)GetMaxArgs() /* +1 for self */;
494
495// TODO: static methods need no 'self' (but is harmless otherwise)
496
497 PyObject* co_varnames = PyTuple_New(co_argcount+1 /* self */);
498 PyTuple_SET_ITEM(co_varnames, 0, CPyCppyy_PyText_FromString("self"));
499 for (int iarg = 0; iarg < co_argcount; ++iarg) {
500 std::string argrep = Cppyy::GetMethodArgType(fMethod, iarg);
501 const std::string& parname = Cppyy::GetMethodArgName(fMethod, iarg);
502 if (!parname.empty()) {
503 argrep += " ";
504 argrep += parname;
505 }
506
507 PyObject* pyspec = CPyCppyy_PyText_FromString(argrep.c_str());
508 PyTuple_SET_ITEM(co_varnames, iarg+1, pyspec);
509 }
510
511 return co_varnames;
512}
513
515{
516// get the default value (if any) of argument iarg of this method
517 if (iarg >= (int)GetMaxArgs())
518 return nullptr;
519
520 const std::string& defvalue = Cppyy::GetMethodArgDefault(fMethod, iarg);
521 if (!defvalue.empty()) {
522
523 // attempt to evaluate the string representation (will work for all builtin types)
524 PyObject* pyval = (PyObject*)PyRun_String(
525 (char*)defvalue.c_str(), Py_eval_input, gThisModule, gThisModule);
526 if (!pyval && PyErr_Occurred()) {
527 PyErr_Clear();
528 return CPyCppyy_PyText_FromString(defvalue.c_str());
529 }
530
531 return pyval;
532 }
533
534 return nullptr;
535}
536
538 return Cppyy::IsConstMethod(GetMethod());
539}
540
541//----------------------------------------------------------------------------
543{
544// Get or build the scope of this method.
545 return CreateScopeProxy(fScope);
546}
547
548
549//----------------------------------------------------------------------------
551{
552// Return the C++ pointer of this function
553 return Cppyy::GetFunctionAddress(fMethod, false /* don't check fast path envar */);
554}
555
556//----------------------------------------------------------------------------
558{
559 Py_ssize_t n = PyTuple_Size(args_tuple);
560
561 int req_args = Cppyy::GetMethodReqArgs(fMethod);
562
563 // Not enough arguments supplied: no match
564 if (req_args > n)
565 return INT_MAX;
566
567 size_t score = 0;
568 for (int i = 0; i < n; i++) {
569 PyObject *pItem = PyTuple_GetItem(args_tuple, i);
570 if(!CPyCppyy_PyText_Check(pItem)) {
571 PyErr_SetString(PyExc_TypeError, "argument types should be in string format");
572 return INT_MAX;
573 }
574 std::string req_type(CPyCppyy_PyText_AsString(pItem));
575
576 size_t arg_score = Cppyy::CompareMethodArgType(fMethod, i, req_type);
577
578 // Method is not compatible if even one argument does not match
579 if (arg_score >= 10) {
580 score = INT_MAX;
581 break;
582 }
583
584 score += arg_score;
585 }
586
587 return score;
588}
589
590//----------------------------------------------------------------------------
592{
593// done if cache is already setup
594 if (fArgsRequired != -1)
595 return true;
596
597 if (!InitConverters_())
598 return false;
599
600 if (!InitExecutor_(fExecutor, ctxt))
601 return false;
602
603// minimum number of arguments when calling
604 fArgsRequired = (int)((bool)fMethod == true ? Cppyy::GetMethodReqArgs(fMethod) : 0);
605
606 return true;
607}
608
609//----------------------------------------------------------------------------
611{
612 if (!PyDict_CheckExact(kwds)) {
613 SetPyError_(CPyCppyy_PyText_FromString("received unknown keyword arguments object"));
614 return nullptr;
615 }
616
617 if (PyDict_Size(kwds) == 0 && !self) {
618 Py_INCREF(args);
619 return args;
620 }
621
622 if (!fArgIndices) {
623 fArgIndices = new std::map<std::string, int>{};
624 for (int iarg = 0; iarg < (int)Cppyy::GetMethodNumArgs(fMethod); ++iarg)
625 (*fArgIndices)[Cppyy::GetMethodArgName(fMethod, iarg)] = iarg;
626 }
627
628 Py_ssize_t nKeys = PyDict_Size(kwds);
629 Py_ssize_t nArgs = PyTuple_GET_SIZE(args) + (self ? 1 : 0);
630 if (nKeys+nArgs < fArgsRequired) {
631 SetPyError_(CPyCppyy_PyText_FromFormat(
632 "takes at least %d arguments (%zd given)", fArgsRequired, nKeys+nArgs));
633 return nullptr;
634 }
635
636 PyObject* newArgs = PyTuple_New(nArgs+nKeys);
637
638// set all values to zero to be able to check them later (this also guarantees normal
639// cleanup by the tuple deallocation)
640 for (Py_ssize_t i = 0; i < nArgs+nKeys; ++i)
641 PyTuple_SET_ITEM(newArgs, i, static_cast<PyObject*>(nullptr));
642
643// next, insert the keyword values
644 PyObject *key, *value;
645 Py_ssize_t pos = 0;
646
647 while (PyDict_Next(kwds, &pos, &key, &value)) {
648 const char* ckey = CPyCppyy_PyText_AsStringChecked(key);
649 if (!ckey) {
650 Py_DECREF(newArgs);
651 return nullptr;
652 }
653 auto p = fArgIndices->find(ckey);
654 if (p == fArgIndices->end()) {
655 SetPyError_(CPyCppyy_PyText_FromFormat("%s::%s got an unexpected keyword argument \'%s\'",
656 Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetMethodName(fMethod).c_str(), ckey));
657 Py_DECREF(newArgs);
658 return nullptr;
659 }
660 Py_INCREF(value);
661 PyTuple_SetItem(newArgs, (*fArgIndices)[ckey], value);
662 }
663
664// fill out the rest of the arguments
665 Py_ssize_t start = 0;
666 if (self) {
667 Py_INCREF(self);
668 PyTuple_SET_ITEM(newArgs, 0, self);
669 start = 1;
670 }
671
672 for (Py_ssize_t i = start; i < nArgs; ++i) {
673 if (PyTuple_GET_ITEM(newArgs, i)) {
674 SetPyError_(CPyCppyy_PyText_FromFormat("%s::%s got multiple values for argument %d",
675 Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetMethodName(fMethod).c_str(), (int)i+1));
676 Py_DECREF(newArgs);
677 return nullptr;
678 }
679
680 PyObject* item = PyTuple_GET_ITEM(args, i);
681 Py_INCREF(item);
682 PyTuple_SET_ITEM(newArgs, i, item);
683 }
684
685 return newArgs;
686}
687
688//----------------------------------------------------------------------------
690 CPPInstance*& self, PyObject* args, PyObject* kwds)
691{
692// verify existence of self, return if ok
693 if (self) {
694 if (kwds) return ProcessKeywords(nullptr, args, kwds);
695 Py_INCREF(args);
696 return args;
697 }
698
699// otherwise, check for a suitable 'self' in args and update accordingly
700 if (PyTuple_GET_SIZE(args) != 0) {
701 CPPInstance* pyobj = (CPPInstance*)PyTuple_GET_ITEM(args, 0);
702
703 // demand CPyCppyy object, and an argument that may match down the road
704 if (CPPInstance_Check(pyobj) &&
705 (fScope == Cppyy::gGlobalScope || // free global
706 (pyobj->ObjectIsA() == 0) || // null pointer or ctor call
707 (Cppyy::IsSubtype(pyobj->ObjectIsA(), fScope)))) { // matching types
708
709 // reset self
710 Py_INCREF(pyobj); // corresponding Py_DECREF is in CPPOverload
711 self = pyobj;
712
713 // offset args by 1 (new ref)
714 PyObject* newArgs = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));
715
716 // put the keywords, if any, in their places in the arguments array
717 if (kwds) {
718 args = ProcessKeywords(nullptr, newArgs, kwds);
719 Py_DECREF(newArgs);
720 newArgs = args;
721 }
722
723 return newArgs; // may be nullptr if kwds insertion failed
724 }
725 }
726
727// no self, set error and lament
728 SetPyError_(CPyCppyy_PyText_FromFormat(
729 "unbound method %s::%s must be called with a %s instance as first argument",
730 Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetMethodName(fMethod).c_str(),
731 Cppyy::GetFinalName(fScope).c_str()));
732 return nullptr;
733}
734
735//----------------------------------------------------------------------------
737{
738 Py_ssize_t argc = PyTuple_GET_SIZE(args);
739 Py_ssize_t argMax = (Py_ssize_t)fConverters.size();
740
741 if (argMax != argc) {
742 // argc must be between min and max number of arguments
743 if (argc < (Py_ssize_t)fArgsRequired) {
744 SetPyError_(CPyCppyy_PyText_FromFormat(
745 "takes at least %d arguments (%zd given)", fArgsRequired, argc));
746 return false;
747 } else if (argMax < argc) {
748 SetPyError_(CPyCppyy_PyText_FromFormat(
749 "takes at most %zd arguments (%zd given)", argMax, argc));
750 return false;
751 }
752 }
753
754 if (argc == 0)
755 return true;
756
757// pass current scope for which the call is made
758 ctxt->fCurScope = fScope;
759
760// convert the arguments to the method call array
761 bool isOK = true;
762 Parameter* cppArgs = ctxt->GetArgs(argc);
763 for (int i = 0; i < (int)argc; ++i) {
764 if (!fConverters[i]->SetArg(PyTuple_GET_ITEM(args, i), cppArgs[i], ctxt)) {
765 SetPyError_(CPyCppyy_PyText_FromFormat("could not convert argument %d", i+1));
766 isOK = false;
767 break;
768 }
769 }
770
771 return isOK;
772}
773
774//----------------------------------------------------------------------------
776{
777// call the interface method
778 PyObject* result = 0;
779
781 !(ctxt->fFlags & CallContext::kProtected)) {
782 // bypasses try block (i.e. segfaults will abort)
783 result = ExecuteFast(self, offset, ctxt);
784 } else {
785 // at the cost of ~10% performance, don't abort the interpreter on any signal
786 result = ExecuteProtected(self, offset, ctxt);
787 }
788
789// TODO: the following is dreadfully slow and dead-locks on Apache: revisit
790// raising exceptions through callbacks by using magic returns
791// if (result && Utility::PyErr_Occurred_WithGIL()) {
792// // can happen in the case of a CINT error: trigger exception processing
793// Py_DECREF(result);
794// result = 0;
795// } else if (!result && PyErr_Occurred())
796 if (!result && PyErr_Occurred())
797 SetPyError_(0);
798
799 return result;
800}
801
802//----------------------------------------------------------------------------
804 CPPInstance*& self, PyObject* args, PyObject* kwds, CallContext* ctxt)
805{
806// setup as necessary
807 if (fArgsRequired == -1 && !Initialize(ctxt))
808 return nullptr;
809
810// fetch self, verify, and put the arguments in usable order
811 if (!(args = PreProcessArgs(self, args, kwds)))
812 return nullptr;
813
814// translate the arguments
815 if (fArgsRequired || PyTuple_GET_SIZE(args)) {
816 if (!ConvertAndSetArgs(args, ctxt)) {
817 Py_DECREF(args);
818 return nullptr;
819 }
820 }
821
822// get the C++ object that this object proxy is a handle for
823 void* object = self->GetObject();
824
825// validity check that should not fail
826 if (!object) {
827 PyErr_SetString(PyExc_ReferenceError, "attempt to access a null-pointer");
828 Py_DECREF(args);
829 return nullptr;
830 }
831
832// get its class
833 Cppyy::TCppType_t derived = self->ObjectIsA();
834
835// calculate offset (the method expects 'this' to be an object of fScope)
836 ptrdiff_t offset = 0;
837 if (derived && derived != fScope)
838 offset = Cppyy::GetBaseOffset(derived, fScope, object, 1 /* up-cast */);
839
840// actual call; recycle self instead of returning new object for same address objects
841 CPPInstance* pyobj = (CPPInstance*)Execute(object, offset, ctxt);
842 Py_DECREF(args);
843
844 if (CPPInstance_Check(pyobj) &&
845 derived && pyobj->ObjectIsA() == derived &&
846 pyobj->GetObject() == object) {
847 Py_INCREF((PyObject*)self);
848 Py_DECREF(pyobj);
849 return (PyObject*)self;
850 }
851
852 return (PyObject*)pyobj;
853}
854
855//- protected members --------------------------------------------------------
857{
858// construct python string from the method's signature
859 return CPyCppyy_PyText_FromString(GetSignatureString(fa).c_str());
860}
861
862//----------------------------------------------------------------------------
864{
865 return Cppyy::GetMethodResultType(fMethod);
866}
int Py_ssize_t
Definition CPyCppyy.h:236
#define CPyCppyy_PyText_AsString
Definition CPyCppyy.h:97
#define CPyCppyy_PyText_FromFormat
Definition CPyCppyy.h:101
#define CPyCppyy_PyText_AsStringChecked
Definition CPyCppyy.h:98
#define CPyCppyy_PyText_FromString
Definition CPyCppyy.h:102
#define CPyCppyy_PyText_Check
Definition CPyCppyy.h:95
_object PyObject
#define e(i)
Definition RSha256.hxx:103
#define CATCH(n)
Definition TException.h:63
#define ENDTRY
Definition TException.h:69
#define TRY
Definition TException.h:56
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
#define pyname
Cppyy::TCppType_t ObjectIsA(bool check_smart=true) const
std::string GetReturnTypeName()
bool Initialize(CallContext *ctxt=nullptr)
void SetPyError_(PyObject *msg)
virtual PyObject * GetSignature(bool show_formalargs=true)
Cppyy::TCppScope_t fScope
Definition CPPMethod.h:82
virtual PyObject * Reflex(Cppyy::Reflex::RequestId_t request, Cppyy::Reflex::FormatId_t=Cppyy::Reflex::OPTIMAL)
virtual int GetArgMatchScore(PyObject *args_tuple)
virtual bool InitExecutor_(Executor *&, CallContext *ctxt=nullptr)
virtual PyObject * GetArgDefault(int iarg)
virtual bool IsConst()
virtual PyObject * GetPrototype(bool show_formalargs=true)
std::string GetSignatureString(bool show_formalargs=true)
PyObject * ExecuteProtected(void *, ptrdiff_t, CallContext *)
std::map< std::string, int > * fArgIndices
Definition CPPMethod.h:87
virtual PyObject * GetCoVarNames()
virtual PyObject * GetScopeProxy()
PyObject * ExecuteFast(void *, ptrdiff_t, CallContext *)
Definition CPPMethod.cxx:66
PyObject * Execute(void *self, ptrdiff_t offset, CallContext *ctxt=nullptr)
void Copy_(const CPPMethod &)
Definition CPPMethod.cxx:37
virtual int GetPriority()
PyObject * ProcessKeywords(PyObject *self, PyObject *args, PyObject *kwds)
Executor * fExecutor
Definition CPPMethod.h:83
Cppyy::TCppMethod_t fMethod
Definition CPPMethod.h:81
virtual PyObject * Call(CPPInstance *&self, PyObject *args, PyObject *kwds, CallContext *ctxt=nullptr)
virtual bool IsGreedy()
bool ConvertAndSetArgs(PyObject *args, CallContext *ctxt=nullptr)
CPPMethod & operator=(const CPPMethod &)
virtual PyObject * PreProcessArgs(CPPInstance *&self, PyObject *args, PyObject *kwds)
virtual int GetMaxArgs()
CPPMethod(Cppyy::TCppScope_t scope, Cppyy::TCppMethod_t method)
virtual Cppyy::TCppFuncAddr_t GetFunctionAddress()
virtual PyObject * Reflex(Cppyy::Reflex::RequestId_t request, Cppyy::Reflex::FormatId_t format=Cppyy::Reflex::OPTIMAL)
Definition PyCallable.h:23
const Int_t n
Definition legend1.C:16
R__EXTERN PyObject * gName
Definition TPython.cxx:105
std::string clean_type(const std::string &cppname, bool template_strip=true, bool const_strip=true)
Definition TypeManip.cxx:98
std::string extract_namespace(const std::string &name)
Set of helper functions that are invoked from the pythonizors, on the Python side.
PyObject * gAbrtException
PyTypeObject CPPExcInstance_Type
PyObject * GetScopeProxy(Cppyy::TCppScope_t)
PyObject * gSegvException
PyObject * BindCppObjectNoCast(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
CPYCPPYY_EXTERN Executor * CreateExecutor(const std::string &name)
bool CPPInstance_Check(T *object)
PyObject * CreateScopeProxy(Cppyy::TCppScope_t)
R__EXTERN PyObject * gThisModule
Definition TPython.cxx:100
PyObject * gIllException
PyObject * gBusException
CPYCPPYY_EXTERN Converter * CreateConverter(const std::string &name, Py_ssize_t *dims=nullptr)
const RequestId_t RETURN_TYPE
Definition Reflex.h:17
const FormatId_t AS_STRING
Definition Reflex.h:23
const FormatId_t OPTIMAL
Definition Reflex.h:21
const FormatId_t AS_TYPE
Definition Reflex.h:22
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:51
RPY_EXPORTED bool IsSubtype(TCppType_t derived, TCppType_t base)
void * TCppObject_t
Definition cpp_cppyy.h:21
RPY_EXPORTED std::string GetMethodArgName(TCppMethod_t, TCppIndex_t iarg)
TCppScope_t TCppType_t
Definition cpp_cppyy.h:19
RPY_EXPORTED TCppIndex_t GetMethodNumArgs(TCppMethod_t)
RPY_EXPORTED TCppType_t GetActualClass(TCppType_t klass, TCppObject_t obj)
RPY_EXPORTED std::string GetScopedFinalName(TCppType_t type)
RPY_EXPORTED std::string GetMethodArgType(TCppMethod_t, TCppIndex_t iarg)
RPY_EXPORTED bool IsComplete(const std::string &type_name)
RPY_EXPORTED bool IsBuiltin(const std::string &type_name)
RPY_EXPORTED bool IsStaticMethod(TCppMethod_t method)
RPY_EXPORTED TCppScope_t GetScope(const std::string &scope_name)
RPY_EXPORTED bool IsConstMethod(TCppMethod_t)
size_t TCppScope_t
Definition cpp_cppyy.h:18
RPY_EXPORTED TCppFuncAddr_t GetFunctionAddress(TCppMethod_t method, bool check_enabled=true)
RPY_EXPORTED TCppIndex_t GetNumBasesLongestBranch(TCppType_t type)
Retrieve number of base classes in the longest branch of the inheritance tree.
RPY_EXPORTED std::string GetMethodResultType(TCppMethod_t)
RPY_EXPORTED std::string GetFinalName(TCppType_t type)
RPY_EXPORTED std::string GetMethodArgDefault(TCppMethod_t, TCppIndex_t iarg)
void * TCppFuncAddr_t
Definition cpp_cppyy.h:25
Cppyy::TCppScope_t fCurScope
Definition CallContext.h:99
Parameter * GetArgs(size_t sz)
Definition CallContext.h:81
static ECallFlags sSignalPolicy
Definition CallContext.h:78