Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
CPPMethod.cxx
Go to the documentation of this file.
1// Bindings
2#include "cpyrt.h"
3
4using namespace cppjit;
5#include "CPPExcInstance.h"
6#include "CPPInstance.h"
7#include "CPPMethod.h"
8#include "Converters.h"
9#include "Executors.h"
10#include "ProxyWrappers.h"
11#include "PyStrings.h"
12#include "TypeManip.h"
13#include "Utility.h"
14#include "cppjit_interop.h"
15
16#include "cpyrt/PyException.h"
17
18// Standard
19#include <algorithm>
20#include <assert.h>
21#include <exception>
22#include <memory>
23#include <sstream>
24#include <string.h>
25#include <string>
26#include <typeinfo>
27
28//- data and local helpers ---------------------------------------------------
29namespace cppjit::cpyrt {
35} // namespace cppjit::cpyrt
36
37//- public helper ------------------------------------------------------------
39 if (fFlags & kSelfSwap) // if self swap, fArgs has been offset by -1
40 std::swap((PyObject*&)fSelf, ((PyObject**)fArgs)[0]);
41
42 if (fFlags & kIsOffset)
43 fArgs -= 1;
45 if (fFlags & kDoItemDecref) {
46 for (Py_ssize_t iarg = 0; iarg < cpyrt_PyArgs_GET_SIZE(fArgs, fNArgsf);
47 ++iarg)
48 Py_DECREF(fArgs[iarg]);
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}
59
60//- private helpers ----------------------------------------------------------
61inline bool cpyrt::CPPMethod::VerifyArgCount_(Py_ssize_t actual) {
62 // actual number of arguments must be between required and max args
63 Py_ssize_t maxargs = (Py_ssize_t)fConverters.size();
64
65 if (maxargs != actual) {
66 if (actual < (Py_ssize_t)fArgsRequired) {
67 SetPyError_(PyUnicode_FromFormat(
68 "takes at least %d arguments (%zd given)", fArgsRequired, actual));
69 return false;
70 } else if (maxargs < actual) {
71 SetPyError_(PyUnicode_FromFormat(
72 "takes at most %zd arguments (%zd given)", maxargs, actual));
73 return false;
74 }
75 }
76 return true;
77}
78
79//----------------------------------------------------------------------------
80inline void cpyrt::CPPMethod::Copy_(const CPPMethod& /* other */) {
81 // fScope and fMethod handled separately
82
83 // do not copy caches
84 fDeclaringScope = nullptr;
85 fExecutor = nullptr;
86 fArgIndices = nullptr;
87 fArgsRequired = -1;
88}
89
90//----------------------------------------------------------------------------
92 // destroy executor and argument converters
93 if (fExecutor && fExecutor->HasState())
94 delete fExecutor;
95 fExecutor = nullptr;
96
97 for (auto p : fConverters) {
98 if (p && p->HasState())
99 delete p;
100 }
101 fConverters.clear();
102
103 delete fArgIndices;
104 fArgIndices = nullptr;
105 fArgsRequired = -1;
106}
107
108//----------------------------------------------------------------------------
109inline PyObject* cpyrt::CPPMethod::ExecuteFast(void* self, ptrdiff_t offset,
110 CallContext* ctxt) {
111 // call into C++ through fExecutor; abstracted out from Execute() to prevent
112 // some code duplication with ProtectedCall()
113 PyObject* result = nullptr;
114
115 try { // C++ try block
116 result = fExecutor->Execute(
117 fMethod, interop::TCppObject_t((void*)((intptr_t)self + offset)), ctxt);
118 } catch (PyException&) {
120 result = nullptr; // error already set
121 } catch (std::exception& e) {
122 // attempt to set the exception to the actual type, to allow catching with
123 // the Python C++ type
124 static interop::TCppScope_t exc_type =
125 interop::GetFullScope("std::exception");
126
128
129 PyObject* pyexc_type = nullptr;
130 PyObject* pyexc_obj = nullptr;
131
132 // TODO: factor this code with the same in ProxyWrappers (and cache it there
133 // to be able to look up based on TCppType_t):
135 const std::string& finalname = interop::GetScopedFinalName(actual);
136 const std::string& parentname = TypeManip::extract_namespace(finalname);
137 PyObject* parent = CreateScopeProxy(parentname);
138 if (parent) {
139 pyexc_type = PyObject_GetAttrString(
140 parent,
141 parentname.empty()
142 ? finalname.c_str()
143 : finalname.substr(parentname.size() + 2, std::string::npos)
144 .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
150 // ProxyWrappers)
151 PyObject* pyclass = cpyrt::GetScopeProxy(actual);
152 PyObject* source = BindCppObjectNoCast(&e, actual);
153 PyObject* pyexc_copy =
154 PyObject_CallFunctionObjArgs(pyclass, source, nullptr);
155 Py_DECREF(source);
156 Py_DECREF(pyclass);
157 if (pyexc_copy) {
158 pyexc_obj = CPPExcInstance_Type.tp_new((PyTypeObject*)pyexc_type,
159 nullptr, nullptr);
160 ((CPPExcInstance*)pyexc_obj)->fCppInstance = (PyObject*)pyexc_copy;
161 } else
162 PyErr_Clear();
163 } else
164 PyErr_Clear();
165
166 if (pyexc_type && pyexc_obj) {
167 PyErr_SetObject(pyexc_type, pyexc_obj);
168 Py_DECREF(pyexc_obj);
169 Py_DECREF(pyexc_type);
170 } else {
171 PyErr_Format(PyExc_Exception, "%s (C++ exception)", e.what());
172 Py_XDECREF(pyexc_obj);
173 Py_XDECREF(pyexc_type);
174 }
175
176 result = nullptr;
177 } catch (...) {
178 // don't set the kCppException flag here, as there is basically no useful
179 // extra information to be had and caller has to catch Exception either way
180 PyErr_SetString(PyExc_Exception, "unhandled, unknown C++ exception");
181 result = nullptr;
182 }
183
184// TODO: covers the PyException throw case, which does not seem to work on
185// Windows, so instead leaves the error be
186#ifdef _WIN32
187 if (PyErr_Occurred()) {
188 if (ResultIsPyObject())
189 Py_XDECREF(result);
190 result = nullptr;
191 }
192#endif
193
194 return result;
195}
196
197//----------------------------------------------------------------------------
199 // build buffers for argument dispatching
200 const size_t nArgs = interop::GetMethodNumArgs(fMethod);
201 fConverters.resize(nArgs);
202
203 // setup the dispatch cache
204 for (int iarg = 0; iarg < (int)nArgs; ++iarg) {
205 interop::TCppType_t fullType = interop::GetMethodArgType(fMethod, iarg);
206 Converter* conv = CreateConverter(fullType);
207 if (!conv) {
208 PyErr_Format(PyExc_TypeError, "argument type %s not handled",
209 interop::GetTypeAsString(fullType).c_str());
210 return false;
211 }
212
213 fConverters[iarg] = conv;
214 }
215
216 return true;
217}
218
219//----------------------------------------------------------------------------
221 CallContext* /* ctxt */) {
222 // install executor conform to the return type
223 executor = (bool)fMethod == true
226
227 if (!executor)
228 return false;
229
230 return true;
231}
232
233//----------------------------------------------------------------------------
235 // built a signature representation (used for doc strings)
236 return interop::GetMethodSignature(fMethod, fa);
237}
238
239//----------------------------------------------------------------------------
241 // Helper to report errors in a consistent format (derefs msg).
242 //
243 // Handles three cases:
244 // 1. No Python error occured yet:
245 // Set a new TypeError with the message "msg" and the docstring of this
246 // C++ method to give some context.
247 // 2. A C++ exception has occured:
248 // Augment the exception message with the docstring of this method
249 // 3. A Python exception has occurred with a traceback:
250 // Do nothing, Python exceptions are already informative enough
251 // 4. If the Python exception has no traceback hinting to an internally set
252 // error stack,
253 // extract its message and wrap it with C++ method docstring context.
254
255#if PY_VERSION_HEX >= 0x030c0000
256 PyObject* evalue = PyErr_Occurred() ? PyErr_GetRaisedException() : nullptr;
257 PyObject* etype = evalue ? (PyObject*)Py_TYPE(evalue) : nullptr;
258#else
259 PyObject* etype = nullptr;
260 PyObject* evalue = nullptr;
261 PyObject* etrace = nullptr;
262
263 if (PyErr_Occurred()) {
264 PyErr_Fetch(&etype, &evalue, &etrace);
265 }
266#endif
267
268 const bool isCppExc =
269 evalue && PyType_IsSubtype((PyTypeObject*)etype, &CPPExcInstance_Type);
270 std::string details;
271
272 // If the error is not a CPPExcInstance and has a traceback, the error from
273 // Python itself is already complete and messing with it would only make it
274 // less informative.
275 // Just restore and return.
276 if (evalue && !isCppExc) {
277#if PY_VERSION_HEX >= 0x030c0000
278 PyObject* tb = PyException_GetTraceback(evalue);
279 if (tb) {
280 Py_DECREF(tb);
281 PyErr_SetRaisedException(evalue);
282 return;
283 }
284#else
285 if (etrace) {
286 PyErr_Restore(etype, evalue, etrace);
287 return;
288 }
289#endif
290 // no traceback, extract its message and fall through
291 PyObject* descr = PyObject_Str(evalue);
292 if (descr) {
293 details = PyUnicode_AsUTF8(descr);
294 Py_DECREF(descr);
295 }
296 }
297
298 PyObject* doc = GetDocString();
299 const char* cdoc = PyUnicode_AsUTF8(doc);
300 const char* cmsg = msg ? PyUnicode_AsUTF8(msg) : nullptr;
301 PyObject* errtype = etype ? etype : PyExc_TypeError;
302 PyObject* pyname = PyObject_GetAttr(errtype, PyStrings::gName);
303 const char* cname = pyname ? PyUnicode_AsUTF8(pyname) : "Exception";
304
305 if (!isCppExc) {
306 // this is the case where no Python error has occured yet, or an internal
307 // one without traceback set a new error with context
308 if (details.empty()) {
309 PyErr_Format(errtype, "%s =>\n %s: %s", cdoc, cname, cmsg ? cmsg : "");
310 } else if (cmsg) {
311 PyErr_Format(errtype, "%s =>\n %s: %s (%s)", cdoc, cname, cmsg,
312 details.c_str());
313 } else {
314 PyErr_Format(errtype, "%s =>\n %s: %s", cdoc, cname, details.c_str());
315 }
316 } else {
317 // augment the top message with context information
318 PyObject*& topMessage = ((CPPExcInstance*)evalue)->fTopMessage;
319 Py_XDECREF(topMessage);
320 if (msg) {
321 topMessage =
322 PyUnicode_FromFormat("%s =>\n %s: %s | ", cdoc, cname, cmsg);
323 } else {
324 topMessage = PyUnicode_FromFormat("%s =>\n %s: ", cdoc, cname);
325 }
326 // restore the updated error
327#if PY_VERSION_HEX >= 0x030c0000
328 PyErr_SetRaisedException(evalue);
329#else
330 PyErr_Restore(etype, evalue, etrace);
331#endif
332 }
333
334 Py_XDECREF(pyname);
335 Py_DECREF(doc);
336 Py_XDECREF(msg);
337}
338
339extern std::unordered_map<interop::TCppType_t, interop::TCppType_t>
341
342//- constructors and destructor ----------------------------------------------
345 : fMethod(method), fScope(scope), fDeclaringScope(nullptr),
346 fExecutor(nullptr), fArgIndices(nullptr), fArgsRequired(-1) {
349 if (TypeReductionMap.find(result) != TypeReductionMap.end())
353}
354
355//----------------------------------------------------------------------------
357 : PyCallable(other), fMethod(other.fMethod), fScope(other.fScope) {
358 Copy_(other);
359}
360
361//----------------------------------------------------------------------------
363 if (this != &other) {
364 Destroy_();
365 Copy_(other);
366 fScope = other.fScope;
367 fMethod = other.fMethod;
368 }
369
370 return *this;
371}
372
373//----------------------------------------------------------------------------
375
376//- public members -----------------------------------------------------------
377/**
378 * @brief Construct a Python string from the method's prototype
379 *
380 * @param fa Show formal arguments of the method
381 * @return PyObject* A Python string with the full method prototype, namespaces
382 * included.
383 *
384 * For example, given:
385 *
386 * int foo(int x);
387 *
388 * namespace a {
389 * namespace b {
390 * namespace c {
391 * int foo(int x);
392 * }}}
393 *
394 * This function returns:
395 *
396 * 'int foo(int x)'
397 * 'int a::b::c::foo(int x)'
398 */
400 // Gather the fully qualified final scope of the method. This includes
401 // all namespaces up to the one where the method is declared, for example:
402 // namespace a { namespace b { void foo(); }}
403 // gives
404 // a::b
405 std::string finalscope = interop::GetScopedFinalName(fScope);
406 return PyUnicode_FromFormat(
407 "%s%s %s%s", (interop::IsStaticMethod(fMethod) ? "static " : ""),
410 GetSignatureString(fa).c_str());
411}
412
413//----------------------------------------------------------------------------
415 PyObject* cppname = PyUnicode_FromString(
416 (GetReturnTypeName() + " (" +
417 (fScope ? interop::GetScopedFinalName(fScope) + "::*)" : "*)"))
418 .c_str());
419 PyUnicode_AppendAndDel(&cppname, GetSignature(false /* show_formalargs */));
420 return cppname;
421}
422
423//----------------------------------------------------------------------------
426 // C++ reflection tooling for methods.
427
428 if (request == interop::Reflex::RETURN_TYPE) {
429 std::string rtn = GetReturnTypeName();
430 interop::TCppScope_t scope = nullptr;
433 scope = interop::GetScope(rtn);
434
436 (format == interop::Reflex::OPTIMAL && !scope))
437 return PyUnicode_FromString(rtn.c_str());
438 else if (format == interop::Reflex::AS_TYPE ||
440 if (scope)
441 return CreateScopeProxy(scope);
442 /* TODO: builtins as type */
443 }
444 }
445
446 return PyCallable::Reflex(request, format);
447}
448
449//----------------------------------------------------------------------------
451 // To help with overload selection, methods are given a priority based on the
452 // affinity of Python and C++ types. Priority only matters for methods that
453 // have an equal number of arguments and types that are possible substitutes
454 // (the normal selection mechanisms would simply distinguish them otherwise).
455
456 // The following types are ordered, in favor (variants implicit):
457 //
458 // bool >> long >> int >> short
459 // double >> long double >> float
460 // const char* >> char
461 //
462 // Further, all integer types are preferred over floating point b/c int to
463 // float is allowed implicitly, float to int is not.
464 //
465 // Special cases that are disliked include void* and unknown/incomplete types.
466 // Also, moves are preferred over references. std::initializer_list is not a
467 // nice conversion candidate either, but needs to be higher priority to mix
468 // well with implicit conversions.
469 // TODO: extend this to favour classes that are not bases.
470 // TODO: profile this method (it's expensive, but should be called too often)
471
472 int priority = 0;
473
474 const size_t nArgs = interop::GetMethodNumArgs(fMethod);
475 for (int iarg = 0; iarg < (int)nArgs; ++iarg) {
476 const std::string aname = interop::GetMethodArgTypeAsString(fMethod, iarg);
477 // FIXME: convert the string comparisons with comparison to the underlying
478 // type:
479 // interop::TCppType_t type = interop::GetMethodArgType(fMethod, iarg);
480
481 // Not builtin and spelled "const void *", so match the compacted name.
482 std::string compact = aname;
483 compact.erase(std::remove(compact.begin(), compact.end(), ' '),
484 compact.end());
485
486 if (compact.find("void*") != std::string::npos) {
487 priority -= 1000; // void*/void** shouldn't be too greedy
488 } else if (interop::IsBuiltin(aname)) {
489 // complex type (note: double penalty: for complex and the template type)
490 if (strstr(aname.c_str(), "std::complex"))
491 priority -= 10; // prefer double, float, etc. over conversion
492
493 // integer types
494 if (strstr(aname.c_str(), "bool"))
495 priority += 1; // bool over int (does accept 1 and 0)
496 else if (strstr(aname.c_str(), "long long"))
497 priority += -5; // will very likely fit
498 else if (strstr(aname.c_str(), "long"))
499 priority += -10; // most affine integer type
500 // no need to compare with int; leave at zero
501 else if (strstr(aname.c_str(), "short"))
502 priority += -50; // not really relevant as a type
503
504 // floating point types (note all numbers lower than integer types)
505 else if (strstr(aname.c_str(), "float"))
506 priority += -100; // not really relevant as a type
507 else if (strstr(aname.c_str(), "long double"))
508 priority += -90; // fits double with least loss of precision
509 else if (strstr(aname.c_str(), "double"))
510 priority += -80; // most affine floating point type
511
512 // string/char types
513 else if (strstr(aname.c_str(), "char") && aname[aname.size() - 1] != '*')
514 priority += -60; // prefer (const) char* over char
515
516 } else {
517 // This is a user-defined type (class, struct, enum, etc.).
518
519 // There's a bit of hysteresis here for templates: once GetScope() is
520 // called, their IsComplete() succeeds, the other way around it does not.
521 // Since GetPriority() is likely called several times in a sort, the
522 // GetScope() _must_ come first, or different GetPriority() calls may
523 // return different results (since the 2nd time, GetScope() will have been
524 // called from the first), killing the stable_sort.
525
526 // prefer more derived classes
527 const std::string& clean_name = TypeManip::clean_type(aname, false);
528 interop::TCppScope_t scope = interop::GetScope(clean_name);
529 if (scope)
530 priority += static_cast<int>(interop::GetNumBasesLongestBranch(scope));
531
532 // GetScope does not resolve enum names, so also detect enums by type
533 if (interop::IsEnumScope(scope) ||
535 priority -= 100;
536
537 // a couple of special cases as explained above
538 if (aname.find("initializer_list") != std::string::npos) {
539 priority += 150; // needed for proper implicit conversion rules
540 } else if (aname.rfind("&&", aname.size() - 2) != std::string::npos) {
541 priority += 100; // prefer moves over other ref/ptr
542 } else if (scope && !interop::IsComplete(scope)) {
543 // class is known, but no dictionary available, 2 more cases: * and &
544 if (aname[aname.size() - 1] == '&')
545 priority += -5000;
546 else
547 priority += -2000; // prefer pointer passing over reference
548 }
549 }
550 }
551
552 // prefer methods w/o optional arguments b/c ones with optional arguments are
553 // easier to select by providing the optional arguments explicitly
554 priority += ((int)interop::GetMethodReqArgs(fMethod) - (int)nArgs);
555
556 // add a small penalty to prefer non-const methods over const ones for
557 // get/setitem
558 if (interop::IsConstMethod(fMethod) &&
559 interop::GetName(interop::TCppScope_t(fMethod.data)) == "operator[]")
560 priority += -10;
561
562 // constructors are prefered
563 if (interop::IsConstructor(fMethod))
564 priority += 100;
565
566 return priority;
567}
568
569//----------------------------------------------------------------------------
571 // Methods will all void*-like arguments should be sorted after template
572 // instanstations, so that they don't greedily take over pointers to object.
573 // GetPriority() is too heavy-handed, as it will pull in all the argument
574 // types, so use this cheaper check.
575 const size_t nArgs = interop::GetMethodReqArgs(fMethod);
576 if (!nArgs)
577 return false;
578
579 for (int iarg = 0; iarg < (int)nArgs; ++iarg) {
580 const std::string aname = interop::GetMethodArgTypeAsString(fMethod, iarg);
581 if (aname.find("void *") != 0)
582 return false;
583 }
584 return true;
585}
586
587//----------------------------------------------------------------------------
589 return (int)interop::GetMethodNumArgs(fMethod);
590}
591
592//----------------------------------------------------------------------------
594 // Build a tuple of the argument types/names.
595 int co_argcount = (int)GetMaxArgs() /* +1 for self */;
596
597 // TODO: static methods need no 'self' (but is harmless otherwise)
598
599 PyObject* co_varnames = PyTuple_New(co_argcount + 1 /* self */);
600 PyTuple_SET_ITEM(co_varnames, 0, PyUnicode_FromString("self"));
601 for (int iarg = 0; iarg < co_argcount; ++iarg) {
602 std::string argrep = interop::GetMethodArgTypeAsString(fMethod, iarg);
603 const std::string& parname = interop::GetMethodArgName(fMethod, iarg);
604 if (!parname.empty()) {
605 argrep += " ";
606 argrep += parname;
607 }
608
609 PyObject* pyspec = PyUnicode_FromString(argrep.c_str());
610 PyTuple_SET_ITEM(co_varnames, iarg + 1, pyspec);
611 }
612
613 return co_varnames;
614}
615
617 // get and evaluate the default value (if any) of argument iarg of this method
618 if (iarg >= (int)GetMaxArgs())
619 return nullptr;
620
621 // borrowed reference to cppjit.gbl module to use its dictionary to eval in
622 static PyObject* gbl =
623 PyDict_GetItemString(PySys_GetObject((char*)"modules"), "cppjit.gbl");
624
625 std::string defvalue = interop::GetMethodArgDefault(fMethod, iarg);
626 if (!defvalue.empty()) {
627 PyObject** dctptr = _PyObject_GetDictPtr(gbl);
628 if (!(dctptr && *dctptr))
629 return nullptr;
630
631 PyObject* gdct = *dctptr;
632 PyObject* scope = nullptr;
633
634 if (defvalue.rfind('(') != std::string::npos) { // constructor-style call
635 // try to tickle scope creation, just in case, first look in the scope
636 // where the function lives, then in the global scope
637 std::string possible_scope = defvalue.substr(0, defvalue.rfind('('));
638 if (!interop::IsBuiltin(possible_scope)) {
639 std::string cand_scope =
640 interop::GetScopedFinalName(fScope) + "::" + possible_scope;
641 scope = CreateScopeProxy(cand_scope);
642 if (!scope) {
643 PyErr_Clear();
644 // search within the global scope instead
645 scope = CreateScopeProxy(possible_scope);
646 if (!scope)
647 PyErr_Clear();
648 } else {
649 // re-scope the scope; alternatively, the expression could be
650 // compiled in the dictionary of the function's namespace, but
651 // that would affect arguments passed to the constructor, too
652 defvalue = cand_scope +
653 defvalue.substr(defvalue.rfind('('), std::string::npos);
654 }
655 }
656 }
657
658 // replace '::' -> '.'
660
661 if (!scope) {
662 // a couple of common cases that python doesn't like (technically, 'L' is
663 // okay with older pythons, but C long will always fit in Python int, so
664 // no need to bother)
665 char c = defvalue.back();
666 if (c == 'F' || c == 'D' || c == 'L') {
667 int offset = 1;
668 if (2 < defvalue.size() && defvalue[defvalue.size() - 2] == 'U')
669 offset = 2;
670 defvalue = defvalue.substr(0, defvalue.size() - offset);
671 } else if (defvalue == "true") {
672 defvalue = "True";
673 } else if (defvalue == "false") {
674 defvalue = "False";
675 }
676 }
677
678 // attempt to evaluate the string representation (compilation is first to
679 // code to allow the error message to indicate where it's coming from)
680 PyObject* pyval = nullptr;
681
682 PyObject* pycode = Py_CompileString(
683 (char*)defvalue.c_str(), "cppjit_default_compiler", Py_eval_input);
684 if (pycode) {
685 pyval = PyEval_EvalCode(pycode, gdct, gdct);
686 Py_DECREF(pycode);
687 }
688
689 if (!pyval && PyErr_Occurred() && silent) {
690 PyErr_Clear();
691 pyval = PyUnicode_FromString(
692 defvalue.c_str()); // allows continuation, but is likely to fail
693 }
694
695 Py_XDECREF(scope);
696 return pyval; // may be nullptr
697 }
698
699 PyErr_Format(PyExc_TypeError, "Could not construct default value for: %s",
700 interop::GetMethodArgName(fMethod, iarg).c_str());
701 return nullptr;
702}
703
705
706//----------------------------------------------------------------------------
708 // Get or build the scope of this method.
709 return CreateScopeProxy(fScope);
710}
711
712//----------------------------------------------------------------------------
714 // Return the C++ pointer of this function
715 return interop::GetFunctionAddress(fMethod,
716 false /* don't check fast path envar */);
717}
718
719//----------------------------------------------------------------------------
721 Py_ssize_t n = PyTuple_Size(args_tuple);
722
723 int req_args = interop::GetMethodReqArgs(fMethod);
724
725 // Not enough arguments supplied: no match
726 if (req_args > n)
727 return INT_MAX;
728
729 size_t score = 0;
730 for (int i = 0; i < n; i++) {
731 PyObject* pItem = PyTuple_GetItem(args_tuple, i);
732 if (!PyUnicode_Check(pItem)) {
733 PyErr_SetString(PyExc_TypeError,
734 "argument types should be in string format");
735 return INT_MAX;
736 }
737 std::string req_type(PyUnicode_AsUTF8(pItem));
738
739 size_t arg_score = interop::CompareMethodArgType(fMethod, i, req_type);
740
741 // Method is not compatible if even one argument does not match
742 if (arg_score >= 10) {
743 score = INT_MAX;
744 break;
745 }
746
747 score += arg_score;
748 }
749
750 return score;
751}
752
753//----------------------------------------------------------------------------
755 // done if cache is already setup
756 if (fArgsRequired != -1)
757 return true;
758
759 if (!InitConverters_())
760 return false;
761
762 if (!InitExecutor_(fExecutor, ctxt))
763 return false;
764
765 // minimum number of arguments when calling
766 fArgsRequired =
767 (int)((bool)fMethod == true ? interop::GetMethodReqArgs(fMethod) : 0);
768
769 return true;
770}
771
772//----------------------------------------------------------------------------
774 if (!PyTuple_CheckExact(cargs.fKwds)) {
775 SetPyError_(PyUnicode_FromString("received unknown keyword names object"));
776 return false;
777 }
778 Py_ssize_t nKeys = PyTuple_GET_SIZE(cargs.fKwds);
779
780 if (nKeys == 0 && !self_in)
781 return true;
782
783 if (!fArgIndices) {
784 fArgIndices = new std::unordered_map<std::string, int>{};
785 for (int iarg = 0; iarg < (int)interop::GetMethodNumArgs(fMethod); ++iarg)
786 (*fArgIndices)[interop::GetMethodArgName(fMethod, iarg)] = iarg;
787 }
788
789 Py_ssize_t nArgs =
790 cpyrt_PyArgs_GET_SIZE(cargs.fArgs, cargs.fNArgsf) + (self_in ? 1 : 0);
791 if (!VerifyArgCount_(nArgs + nKeys))
792 return false;
793
794 std::vector<PyObject*> vArgs{fConverters.size()};
795
796 // next, insert the keyword values
797 PyObject *key, *value;
798 Py_ssize_t maxpos = -1;
799
800 Py_ssize_t npos_args = cpyrt_PyArgs_GET_SIZE(cargs.fArgs, cargs.fNArgsf);
801 for (Py_ssize_t ikey = 0; ikey < nKeys; ++ikey) {
802 key = PyTuple_GET_ITEM(cargs.fKwds, ikey);
803 value = cargs.fArgs[npos_args + ikey];
804 const char* ckey = PyUnicode_AsUTF8(key);
805 if (!ckey)
806 return false;
807
808 auto p = fArgIndices->find(ckey);
809 if (p == fArgIndices->end()) {
810 SetPyError_(PyUnicode_FromFormat(
811 "%s::%s got an unexpected keyword argument \'%s\'",
812 interop::GetFinalName(fScope).c_str(),
813 interop::GetName(interop::TCppScope_t(fMethod.data)).c_str(), ckey));
814 return false;
815 }
816
817 maxpos = p->second > maxpos ? p->second : maxpos;
818 vArgs[p->second] =
819 value; // no INCREF yet for simple cleanup in case of error
820 }
821
822 // if maxpos < nArgs, it will be detected & reported as a duplicate below
823 Py_ssize_t maxargs = maxpos + 1;
824 cpyrt_PyArgs_t newArgs = cpyrt_PyArgs_New(maxargs);
825
826 // set all values to zero to be able to check them later (this also guarantees
827 // normal cleanup by the tuple deallocation)
828 for (Py_ssize_t i = 0; i < maxargs; ++i)
829 cpyrt_PyArgs_SET_ITEM(newArgs, i, nullptr);
830
831 // fill out the positional arguments
832 Py_ssize_t start = 0;
833 if (self_in) {
834 Py_INCREF(self_in);
835 cpyrt_PyArgs_SET_ITEM(newArgs, 0, self_in);
836 start = 1;
837 }
838
839 for (Py_ssize_t i = start; i < nArgs; ++i) {
840 if (vArgs[i]) {
841 SetPyError_(PyUnicode_FromFormat(
842 "%s::%s got multiple values for argument %d",
843 interop::GetFinalName(fScope).c_str(),
844 interop::GetName(interop::TCppScope_t(fMethod.data)).c_str(),
845 (int)i + 1));
846 cpyrt_PyArgs_DEL(newArgs);
847 return false;
848 }
849
850 PyObject* item = cpyrt_PyArgs_GET_ITEM(cargs.fArgs, i);
851 Py_INCREF(item);
852 cpyrt_PyArgs_SET_ITEM(newArgs, i, item);
853 }
854
855 // fill out the keyword arguments
856 for (Py_ssize_t i = nArgs; i < maxargs; ++i) {
857 PyObject* item = vArgs[i];
858 if (item) {
859 Py_INCREF(item);
860 cpyrt_PyArgs_SET_ITEM(newArgs, i, item);
861 } else {
862 // try retrieving the default
863 item = GetArgDefault((int)i, false /* i.e. not silent */);
864 if (!item) {
865 cpyrt_PyArgs_DEL(newArgs);
866 return false;
867 }
868 cpyrt_PyArgs_SET_ITEM(newArgs, i, item);
869 }
870 }
871
872 if (cargs.fFlags & PyCallArgs::kDoFree) {
873 if (cargs.fFlags & PyCallArgs::kIsOffset)
874 cargs.fArgs -= 1;
875 cpyrt_PyArgs_DEL(cargs.fArgs);
876 }
877
878 cargs.fArgs = newArgs;
879 cargs.fNArgsf = maxargs;
881
882 return true;
883}
884
885//----------------------------------------------------------------------------
887 // verify existence of self, return if ok
888 if (cargs.fSelf) {
889 if (cargs.fKwds) {
890 return ProcessKwds(nullptr, cargs);
891 }
892 return true;
893 }
894
895 // otherwise, check for a suitable 'self' in args and update accordingly
896 if (cpyrt_PyArgs_GET_SIZE(cargs.fArgs, cargs.fNArgsf) != 0) {
898
899 // demand cpyrt object, and an argument that may match down the road
900 if (CPPInstance_Check(pyobj)) {
901 interop::TCppScope_t oisa = pyobj->ObjectIsA();
902 if (fScope == interop::GetGlobalScope() || // free global
903 oisa == nullptr || // null pointer or ctor call
904 oisa == fScope || // matching types
905 interop::IsSubclass(oisa, fScope)) { // id.
906
907 // reset self
908 Py_INCREF(pyobj); // corresponding Py_DECREF is in CPPOverload
909 cargs.fSelf = pyobj;
910
911 // offset args by 1
912 cargs.fArgs += 1;
914 cargs.fNArgsf -= 1;
915
916 // put the keywords, if any, in their places in the arguments array
917 if (cargs.fKwds)
918 return ProcessKwds(nullptr, cargs);
919 return true;
920 }
921 }
922 }
923
924 // no self, set error and lament
925 SetPyError_(PyUnicode_FromFormat(
926 "unbound method %s::%s must be called with a %s instance as first "
927 "argument",
928 interop::GetFinalName(fScope).c_str(),
929 interop::GetName(interop::TCppScope_t(fMethod.data)).c_str(),
930 interop::GetFinalName(fScope).c_str()));
931 return false;
932}
933
934//----------------------------------------------------------------------------
936 CallContext* ctxt) {
937 Py_ssize_t argc = cpyrt_PyArgs_GET_SIZE(args, nargsf);
938 if (!VerifyArgCount_(argc))
939 return false;
940
941 // pass current scope for which the call is made
942 ctxt->fCurScope = fScope;
943
944 if (argc == 0)
945 return true;
946
947 // convert the arguments to the method call array
948 bool isOK = true;
949 Parameter* cppArgs = ctxt->GetArgs(argc);
950 for (int i = 0; i < (int)argc; ++i) {
951 if (!fConverters[i]->SetArg(cpyrt_PyArgs_GET_ITEM(args, i), cppArgs[i],
952 ctxt)) {
953 SetPyError_(
954 PyUnicode_FromFormat("could not convert argument %d: %s", i + 1,
955 fConverters[i]->GetFailureMsg().c_str()));
956 isOK = false;
957 break;
958 }
959 }
960
961 return isOK;
962}
963
964//----------------------------------------------------------------------------
966 CallContext* ctxt) {
967 // call the interface method; the kProtected signal policy is accepted for
968 // API compatibility but has no separate path: without a signal handler
969 // that longjmps back into the call there is nothing to protect
970 PyObject* result = ExecuteFast(self, offset, ctxt);
971
972 if (!result && PyErr_Occurred())
973 SetPyError_(0);
974
975 return result;
976}
977
978//----------------------------------------------------------------------------
980 size_t nargsf, PyObject* kwds,
981 CallContext* ctxt) {
982 // setup as necessary
983 if (fArgsRequired == -1 && !Initialize(ctxt))
984 return nullptr;
985
986 // fetch self, verify, and put the arguments in usable order
987 PyCallArgs cargs{self, args, nargsf, kwds};
988 if (!ProcessArgs(cargs))
989 return nullptr;
990
991 // self provides the python context for lifelines
992 if (!ctxt->fPyContext)
993 ctxt->fPyContext = (PyObject*)cargs.fSelf; // no Py_INCREF as no ownership
994
995 // translate the arguments
996 if (fArgsRequired || cpyrt_PyArgs_GET_SIZE(args, cargs.fNArgsf)) {
997 if (!ConvertAndSetArgs(cargs.fArgs, cargs.fNArgsf, ctxt))
998 return nullptr;
999 }
1000
1001 // get the C++ object that this object proxy is a handle for
1002 void* object = self->GetObject();
1003
1004 // validity check that should not fail
1005 if (!object) {
1006 PyErr_SetString(PyExc_ReferenceError, "no C++ object available");
1008 return nullptr;
1009 }
1010
1011 // get its class
1012 interop::TCppScope_t derived = self->ObjectIsA();
1013
1014 // calculate offset: the generated wrapper casts 'this' to the method's actual
1015 // declaring class, which may differ from fScope. In particular, a method
1016 // brought into fScope through a using-declaration (e.g. `using Base::meth;`)
1017 // is still declared in the base, so 'this' has to be adjusted to that base's
1018 // subobject. Using fScope here would yield a zero offset and corrupt memory.
1019 if (!fDeclaringScope) {
1020 fDeclaringScope =
1022 if (!fDeclaringScope)
1023 fDeclaringScope = fScope;
1024 }
1025
1026 ptrdiff_t offset = 0;
1027 if (derived && derived != fDeclaringScope)
1028 offset = interop::GetBaseOffset(derived, fDeclaringScope, object,
1029 1 /* up-cast */);
1030
1031 // actual call; recycle self instead of returning new object for same address
1032 // objects
1033 CPPInstance* pyobj = (CPPInstance*)Execute(object, offset, ctxt);
1034 if (CPPInstance_Check(pyobj) && derived && pyobj->ObjectIsA() == derived &&
1035 pyobj->GetObject() == object) {
1036 Py_INCREF((PyObject*)self);
1037 Py_DECREF(pyobj);
1038 return (PyObject*)self;
1039 }
1040
1041 return (PyObject*)pyobj;
1042}
1043
1044//- protected members --------------------------------------------------------
1046 // construct python string from the method's signature
1047 return PyUnicode_FromString(GetSignatureString(fa).c_str());
1048}
1049
1050/**
1051 * @brief Returns a tuple with the names of the input parameters of this method.
1052 *
1053 * For example given a function with prototype:
1054 *
1055 * double foo(int a, float b, double c)
1056 *
1057 * this function returns:
1058 *
1059 * ('a', 'b', 'c')
1060 */
1062 // Build a tuple of the argument names for this signature.
1063 int argcount = GetMaxArgs();
1064 PyObject* signature_names = PyTuple_New(argcount);
1065
1066 for (int iarg = 0; iarg < argcount; ++iarg) {
1067 const std::string& argname_cpp = interop::GetMethodArgName(fMethod, iarg);
1068 PyObject* argname_py = PyUnicode_FromString(argname_cpp.c_str());
1069 PyTuple_SET_ITEM(signature_names, iarg, argname_py);
1070 }
1071
1072 return signature_names;
1073}
1074
1075/**
1076 * @brief Returns a dictionary with the types of the signature of this method.
1077 *
1078 * This dictionary will store both the return type and the input parameter
1079 * types of this method, respectively with keys "return_type" and
1080 * "input_types", for example given a function with prototype:
1081 *
1082 * double foo(int a, float b, double c)
1083 *
1084 * this function returns:
1085 *
1086 * {'input_types': ('int', 'float', 'double'), 'return_type': 'double'}
1087 */
1089
1090 PyObject* signature_types_dict = PyDict_New();
1091
1092 // Insert the return type first
1093 std::string return_type = GetReturnTypeName();
1094 PyObject* return_type_py = PyUnicode_FromString(return_type.c_str());
1095 PyDict_SetItem(signature_types_dict, PyUnicode_FromString("return_type"),
1096 return_type_py);
1097
1098 // Build a tuple of the argument types for this signature.
1099 int argcount = GetMaxArgs();
1100 PyObject* parameter_types = PyTuple_New(argcount);
1101
1102 for (int iarg = 0; iarg < argcount; ++iarg) {
1103 const std::string& argtype_cpp =
1104 interop::GetMethodArgTypeAsString(fMethod, iarg);
1105 PyObject* argtype_py = PyUnicode_FromString(argtype_cpp.c_str());
1106 PyTuple_SET_ITEM(parameter_types, iarg, argtype_py);
1107 }
1108
1109 PyDict_SetItem(signature_types_dict, PyUnicode_FromString("input_types"),
1110 parameter_types);
1111
1112 // Const-qualification of the method itself (always false for free
1113 // functions and static methods).
1114 PyDict_SetItem(signature_types_dict, PyUnicode_FromString("is_const"),
1115 PyBool_FromLong(IsConst()));
1116
1117 return signature_types_dict;
1118}
1119
1120//----------------------------------------------------------------------------
std::unordered_map< interop::TCppType_t, interop::TCppType_t > TypeReductionMap
std::vector< Converter * > fConverters
_object PyObject
#define c(i)
Definition RSha256.hxx:101
#define e(i)
Definition RSha256.hxx:103
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
interop::TCppScope_t ObjectIsA(bool check_smart=true) const
PyObject * GetTypeName() override
virtual bool InitExecutor_(Executor *&, CallContext *ctxt=nullptr)
int GetArgMatchScore(PyObject *args_tuple) override
bool Initialize(CallContext *ctxt=nullptr)
std::string GetReturnTypeName()
void SetPyError_(PyObject *msg)
PyObject * GetScopeProxy() override
PyObject * GetPrototype(bool show_formalargs=true) override
Construct a Python string from the method's prototype.
bool IsConst() override
PyObject * GetSignatureTypes() override
Returns a dictionary with the types of the signature of this method.
PyObject * Call(CPPInstance *&self, cpyrt_PyArgs_t args, size_t nargsf, PyObject *kwds, CallContext *ctxt=nullptr) override
virtual bool ProcessArgs(PyCallArgs &args)
CPPMethod(interop::TCppScope_t scope, interop::TCppMethod_t method)
PyObject * Execute(void *self, ptrdiff_t offset, CallContext *ctxt=nullptr)
PyObject * Reflex(interop::Reflex::RequestId_t request, interop::Reflex::FormatId_t=interop::Reflex::OPTIMAL) override
int GetMaxArgs() override
std::string GetSignatureString(bool show_formalargs=true)
PyObject * GetCoVarNames() override
interop::TCppFuncAddr_t GetFunctionAddress() override
interop::TCppMethod_t fMethod
Definition CPPMethod.h:117
PyObject * GetSignature(bool show_formalargs=true) override
PyObject * ExecuteFast(void *, ptrdiff_t, CallContext *)
bool IsGreedy() override
CPPMethod & operator=(const CPPMethod &)
bool VerifyArgCount_(Py_ssize_t)
Definition CPPMethod.cxx:61
interop::TCppScope_t fScope
Definition CPPMethod.h:118
PyObject * GetArgDefault(int iarg, bool silent=true) override
int GetPriority() override
bool ProcessKwds(PyObject *self_in, PyCallArgs &args)
bool ConvertAndSetArgs(cpyrt_PyArgs_t, size_t nargsf, CallContext *ctxt=nullptr)
PyObject * GetSignatureNames() override
Returns a tuple with the names of the input parameters of this method.
void Copy_(const CPPMethod &)
Definition CPPMethod.cxx:80
cpyrt_PyArgs_t fArgs
Definition CPPMethod.h:35
CPPInstance *& fSelf
Definition CPPMethod.h:34
virtual PyObject * Reflex(interop::Reflex::RequestId_t request, interop::Reflex::FormatId_t format=interop::Reflex::OPTIMAL)
Definition PyCallable.h:27
static PyObject * cpyrt_PyArgs_SET_ITEM(cpyrt_PyArgs_t args, Py_ssize_t i, PyObject *item)
Definition cpyrt.h:86
static void cpyrt_PyArgs_DEL(cpyrt_PyArgs_t args)
Definition cpyrt.h:96
PyObject *const * cpyrt_PyArgs_t
Definition cpyrt.h:81
static Py_ssize_t cpyrt_PyArgs_GET_SIZE(cpyrt_PyArgs_t, size_t nargsf)
Definition cpyrt.h:90
static cpyrt_PyArgs_t cpyrt_PyArgs_New(Py_ssize_t N)
Definition cpyrt.h:93
static PyObject * cpyrt_PyArgs_GET_ITEM(cpyrt_PyArgs_t args, Py_ssize_t i)
Definition cpyrt.h:82
const Int_t n
Definition legend1.C:16
std::string extract_namespace(const std::string &name)
std::string clean_type(const std::string &cppname, bool template_strip=true, bool const_strip=true)
void cppscope_to_pyscope(std::string &cppscope)
CPYRT_EXTERN Converter * CreateConverter(const std::string &name, cdims_t=0)
PyObject * gSegvException
PyObject * gThisModule
Definition CPPMethod.cxx:30
PyObject * GetScopeProxy(interop::TCppScope_t)
PyObject * CreateScopeProxy(PyObject *, PyObject *args)
CPYRT_EXTERN Executor * CreateExecutor(const std::string &name, cdims_t=0)
PyObject * gAbrtException
PyTypeObject CPPExcInstance_Type
PyObject * BindCppObjectNoCast(interop::TCppObject_t object, interop::TCppScope_t klass, const unsigned flags=0)
bool CPPInstance_Check(T *object)
PyObject * gBusException
PyObject * gIllException
const FormatId_t OPTIMAL
Definition Reflex.h:22
const FormatId_t AS_STRING
Definition Reflex.h:24
const RequestId_t RETURN_TYPE
Definition Reflex.h:18
const FormatId_t AS_TYPE
Definition Reflex.h:23
Cpp::TypeRef TCppType_t
Definition API.h:42
TCppScope_t GetFullScope(const std::string &scope_name)
Cpp::ObjectRef TCppObject_t
Definition API.h:43
std::string GetTypeAsString(TCppType_t type)
bool IsBuiltin(const std::string &type_name)
std::string GetName(TCppScope_t)
TCppMethod_t AdaptFunctionForLambdaReturn(TCppMethod_t fn)
bool IsConstructor(TCppMethod_t method)
TCppScope_t GetActualClass(TCppScope_t klass, TCppObject_t obj)
std::string GetMethodArgTypeAsString(TCppMethod_t method, TCppIndex_t iarg)
TCppIndex_t GetMethodReqArgs(TCppMethod_t)
TCppScope_t GetParentScope(TCppScope_t scope)
std::string GetMethodReturnTypeAsString(TCppMethod_t)
TCppIndex_t GetMethodNumArgs(TCppMethod_t)
TCppIndex_t CompareMethodArgType(TCppMethod_t, TCppIndex_t iarg, const std::string &req_type)
bool IsSubclass(TCppScope_t derived, TCppScope_t base)
Cpp::FuncRef TCppMethod_t
Definition API.h:44
TCppType_t GetMethodArgType(TCppMethod_t, TCppIndex_t iarg)
bool IsEnumScope(TCppScope_t scope)
TCppMethod_t ReduceReturnType(TCppMethod_t fn, TCppType_t reduce)
std::string GetMethodArgName(TCppMethod_t, TCppIndex_t iarg)
TCppFuncAddr_t GetFunctionAddress(TCppMethod_t method, bool check_enabled=true)
TCppType_t GetMethodReturnType(TCppMethod_t)
TCppType_t ResolveType(TCppType_t cppitem_name)
std::string GetMethodSignature(TCppMethod_t, bool show_formal_args, TCppIndex_t max_args=(TCppIndex_t) -1)
TCppScope_t GetGlobalScope()
TCppIndex_t GetNumBasesLongestBranch(TCppScope_t klass)
TCppScope_t GetScope(const std::string &scope_name, TCppScope_t parent_scope=TCppScope_t{})
std::string GetFinalName(TCppScope_t type)
bool IsLambdaClass(TCppType_t type)
bool IsConstMethod(TCppMethod_t)
ptrdiff_t GetBaseOffset(TCppScope_t derived, TCppScope_t base, TCppObject_t address, int direction, bool rerror=false)
void * TCppFuncAddr_t
Definition API.h:46
std::string GetMethodArgDefault(TCppMethod_t, TCppIndex_t iarg)
bool IsStaticMethod(TCppMethod_t method)
std::string GetScopedFinalName(TCppScope_t type)
Cpp::DeclRef TCppScope_t
Definition API.h:41
bool IsEnumType(TCppType_t type)
bool IsComplete(TCppScope_t type)
Definition API.cxx:34
Parameter * GetArgs(size_t sz)
Definition CallContext.h:62
interop::TCppScope_t fCurScope
Definition CallContext.h:86