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