Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
CPPOverload.cxx
Go to the documentation of this file.
1// Bindings
2#include "cpyrt.h"
3
4using namespace cppjit;
5#include "CPPInstance.h"
6#include "CPPOverload.h"
7#include "CallContext.h"
8#include "PyStrings.h"
9#include "Utility.h"
10#include "structmember.h" // from Python
11#include "cpyrt/Reflex.h"
12
13// Standard
14#include <algorithm>
15#include <sstream>
16#include <vector>
17
18namespace cppjit::cpyrt {
19
20namespace {
21
22// from CPython's instancemethod: Free list for method objects to safe
23// malloc/free overhead The fSelf field is used to chain the elements.
24static CPPOverload* free_list;
25static int numfree = 0;
26#ifndef CPPOverload_MAXFREELIST
27#define CPPOverload_MAXFREELIST 32
28#endif
29
30// TODO: only used in pythonizations to add Python-side overloads to existing
31// C++ overloads, but may be better off integrated with Pythonize.cxx callbacks
32class TPythonCallback : public PyCallable {
33public:
35
36 TPythonCallback(PyObject* callable) : fCallable(nullptr) {
37 if (!PyCallable_Check(callable)) {
38 PyErr_SetString(PyExc_TypeError, "parameter must be callable");
39 return;
40 }
41 Py_INCREF(callable);
42 fCallable = callable;
43 }
44
45 ~TPythonCallback() override {
46 Py_DECREF(fCallable);
47 fCallable = nullptr;
48 }
49
50 PyObject* GetSignature(bool /*show_formalargs*/ = true) override {
51 return PyUnicode_FromString("*args, **kwargs");
52 }
53 PyObject* GetSignatureNames() override { return PyTuple_New(0); }
54 PyObject* GetSignatureTypes() override { return PyTuple_New(0); }
55 PyObject* GetPrototype(bool /*show_formalargs*/ = true) override {
56 return PyUnicode_FromString("<callback>");
57 }
58 PyObject* GetDocString() override {
59 if (PyObject_HasAttrString(fCallable, "__doc__")) {
60 return PyObject_GetAttrString(fCallable, "__doc__");
61 } else {
62 return GetPrototype();
63 }
64 }
65
66 int GetPriority() override { return 100; };
67 bool IsGreedy() override { return false; };
68
69 int GetMaxArgs() override { return 100; };
70 PyObject* GetCoVarNames() override { // TODO: pick these up from the callable
72 }
73 PyObject* GetArgDefault(int /* iarg */, bool /* silent */ = true) override {
74 Py_RETURN_NONE; // TODO: pick these up from the callable
75 }
76
77 PyObject* GetScopeProxy() override { // should this be the module ??
79 }
80
82 return (interop::TCppFuncAddr_t) nullptr;
83 }
84
85 PyCallable* Clone() override { return new TPythonCallback(*this); }
86
87 PyObject* Call(CPPInstance*& self, cpyrt_PyArgs_t args, size_t nargsf,
88 PyObject* kwds, CallContext* /* ctxt = 0 */) override {
89
90 if (self) {
91 if (nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET) { // mutation allowed?
92 std::swap(((PyObject**)args - 1)[0], (PyObject*&)self);
93 nargsf &= ~PY_VECTORCALL_ARGUMENTS_OFFSET;
94 args = args - 1;
95 } else {
96 Py_ssize_t nkwargs = kwds ? PyTuple_GET_SIZE(kwds) : 0;
97 Py_ssize_t totalargs = PyVectorcall_NARGS(nargsf) + nkwargs;
98 PyObject** newArgs =
99 (PyObject**)PyMem_Malloc((totalargs + 1) * sizeof(PyObject*));
100 if (!newArgs)
101 return nullptr;
102
103 newArgs[0] = (PyObject*)self;
104 if (0 < totalargs)
105 memcpy((void*)&newArgs[1], args, totalargs * sizeof(PyObject*));
106 args = newArgs;
107 }
108 nargsf += 1;
109 }
110
111 PyObject* result = cpyrt_PyObject_Call(fCallable, args, nargsf, kwds);
112 if (self) {
113 if (nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET)
114 std::swap(((PyObject**)args - 1)[0], (PyObject*&)self);
115 else
116 PyMem_Free((void*)args);
117 }
118 return result;
119 }
120};
121
122// helper to test whether a method is used in a pseudo-function modus
123static inline bool IsPseudoFunc(CPPOverload* pymeth) {
124 return pymeth->fMethodInfo->fFlags & CallContext::kIsPseudoFunc;
125}
126
127// helper to sort on method priority
128static int PriorityCmp(const std::pair<int, PyCallable*>& left,
129 const std::pair<int, PyCallable*>& right) {
130 return left.first > right.first;
131}
132
133// return helper
134static inline void ResetCallState(CPPInstance* descr_self,
135 CPPInstance*& im_self) {
136 // reset self if needed, allowing simple re-use
137 if (descr_self != im_self) {
138 Py_XDECREF(im_self);
139 im_self = descr_self;
140 }
141}
142
143// helper to factor out return logic of mp_call / mp_vectorcall
144static inline PyObject* HandleReturn(CPPOverload* pymeth, CPPInstance* im_self,
145 PyObject* result) {
146 // special case for python exceptions, propagated through C++ layer
147 if (result) {
148 CPPInstance* cppres =
149 (CPPInstance*)(CPPInstance_Check(result) ? result : nullptr);
150
151 // if this method creates new objects, always take ownership
152 if (IsCreator(pymeth->fMethodInfo->fFlags)) {
153
154 // either be a constructor with a fresh object proxy self ...
155 if (IsConstructor(pymeth->fMethodInfo->fFlags)) {
156 if (im_self)
157 im_self->PythonOwns();
158 }
159
160 // ... or be a regular method with an object proxy return value
161 else if (cppres)
162 cppres->PythonOwns();
163 }
164
165 // if this new object falls inside self, make sure its lifetime is proper
166 if (!(pymeth->fMethodInfo->fFlags & CallContext::kNeverLifeLine)) {
167 int ll_action = 0;
168 if ((PyObject*)im_self != result) {
169 if (pymeth->fMethodInfo->fFlags & CallContext::kSetLifeLine)
170 ll_action = 1;
171 else if (cppres && CPPInstance_Check(im_self)) {
172 // if self was a by-value return and result is not, pro-actively
173 // protect result; else if the return value falls within the memory of
174 // 'this', force a lifeline
175 if (!(cppres->fFlags &
176 CPPInstance::kIsValue)) { // no need if the result is temporary
177 if (im_self->fFlags & CPPInstance::kIsValue)
178 ll_action = 2;
179 else if (im_self->fFlags & CPPInstance::kHasLifeLine)
180 ll_action = 3;
181 else {
182 ptrdiff_t offset = (ptrdiff_t)cppres->GetObject() -
183 (ptrdiff_t)im_self->GetObject();
184 if (0 <= offset &&
185 offset < (ptrdiff_t)interop::SizeOf(im_self->ObjectIsA()))
186 ll_action = 4;
187 }
188 }
189 }
190 }
191
192 if (!ll_action)
193 pymeth->fMethodInfo->fFlags |=
194 CallContext::kNeverLifeLine; // assume invariant semantics
195 else {
196 if (PyObject_SetAttr(result, PyStrings::gLifeLine,
197 (PyObject*)im_self) == -1)
198 PyErr_Clear(); // ignored
199 if (cppres)
200 cppres->fFlags |= CPPInstance::kHasLifeLine; // for chaining
201 pymeth->fMethodInfo->fFlags |=
202 CallContext::kSetLifeLine; // for next time
203 }
204 }
205 }
206
207 // reset self as necessary to allow re-use of the CPPOverload
208 ResetCallState(pymeth->fSelf, im_self);
209
210 return result;
211}
212
213//= cpyrt method proxy object behaviour ===================================
214static PyObject* mp_name(CPPOverload* pymeth, void*) {
215 return PyUnicode_FromString(pymeth->GetName().c_str());
216}
217
218//----------------------------------------------------------------------------
219static PyObject* mp_module(CPPOverload* /* pymeth */, void*) {
220 Py_INCREF(PyStrings::gThisModule);
222}
223
224//----------------------------------------------------------------------------
225static PyObject* mp_doc(CPPOverload* pymeth, void*) {
226 if (pymeth->fMethodInfo->fDoc) {
227 Py_INCREF(pymeth->fMethodInfo->fDoc);
228 return pymeth->fMethodInfo->fDoc;
229 }
230
231 // Build python document string ('__doc__') from all C++-side overloads.
232 CPPOverload::Methods_t& methods = pymeth->fMethodInfo->fMethods;
233
234 // collect doc strings
235 CPPOverload::Methods_t::size_type nMethods = methods.size();
236 if (nMethods == 0) // from template proxy with no instantiations
237 return nullptr;
238 PyObject* doc = methods[0]->GetDocString();
239
240 // simple case
241 if (nMethods == 1)
242 return doc;
243
244 // overloaded method
245 PyObject* separator = PyUnicode_FromString("\n");
246 for (CPPOverload::Methods_t::size_type i = 1; i < nMethods; ++i) {
247 PyUnicode_Append(&doc, separator);
248 PyUnicode_AppendAndDel(&doc, methods[i]->GetDocString());
249 }
250 Py_DECREF(separator);
251
252 return doc;
253}
254
255static int mp_doc_set(CPPOverload* pymeth, PyObject* val, void*) {
256 Py_XDECREF(pymeth->fMethodInfo->fDoc);
257 Py_INCREF(val);
258 pymeth->fMethodInfo->fDoc = val;
259 return 0;
260}
261
262/**
263 * @brief Returns a dictionary with the input parameter names for all overloads.
264 *
265 * This dictionary may look like:
266 *
267 * {'double ::foo(int a, float b, double c)': ('a', 'b', 'c'),
268 * 'float ::foo(float b)': ('b',),
269 * 'int ::foo(int a)': ('a',),
270 * 'int ::foo(int a, float b)': ('a', 'b')}
271 */
272static PyObject* mp_func_overloads_names(CPPOverload* pymeth) {
273
274 const CPPOverload::Methods_t& methods = pymeth->fMethodInfo->fMethods;
275
276 PyObject* overloads_names_dict = PyDict_New();
277
278 for (PyCallable* method : methods) {
279 PyDict_SetItem(overloads_names_dict, method->GetPrototype(),
280 method->GetSignatureNames());
281 }
282
283 return overloads_names_dict;
284}
285
286/**
287 * @brief Returns a dictionary with the types of all overloads.
288 *
289 * This dictionary may look like:
290 *
291 * {'double ::foo(int a, float b, double c)': {'input_types': ('int', 'float',
292 * 'double'), 'return_type': 'double'}, 'float ::foo(float b)': {'input_types':
293 * ('float',), 'return_type': 'float'}, 'int ::foo(int a)': {'input_types':
294 * ('int',), 'return_type': 'int'}, 'int ::foo(int a, float b)': {'input_types':
295 * ('int', 'float'), 'return_type': 'int'}}
296 *
297 * Each overload's value dict also carries 'is_const': the method's own
298 * const-qualification (always False for free functions and static methods).
299 */
300static PyObject* mp_func_overloads_types(CPPOverload* pymeth) {
301
302 const CPPOverload::Methods_t& methods = pymeth->fMethodInfo->fMethods;
303
304 PyObject* overloads_types_dict = PyDict_New();
305
306 for (PyCallable* method : methods) {
307 PyDict_SetItem(overloads_types_dict, method->GetPrototype(),
308 method->GetSignatureTypes());
309 }
310
311 return overloads_types_dict;
312}
313
314//----------------------------------------------------------------------------
315static PyObject* mp_meth_func(CPPOverload* pymeth, void*) {
316 // Create a new method proxy to be returned.
317 CPPOverload* newPyMeth =
318 (CPPOverload*)CPPOverload_Type.tp_alloc(&CPPOverload_Type, 0);
319
320 // method info is shared, as it contains the collected overload knowledge
321 *pymeth->fMethodInfo->fRefCount += 1;
322 newPyMeth->fMethodInfo = pymeth->fMethodInfo;
323
324 // new method is unbound, track whether this proxy is used in the capacity of
325 // a method or a function (which normally is a CPPFunction)
326 newPyMeth->fMethodInfo->fFlags |= CallContext::kIsPseudoFunc;
327
328 return (PyObject*)newPyMeth;
329}
330
331//----------------------------------------------------------------------------
332static PyObject* mp_meth_self(CPPOverload* pymeth, void*) {
333 // Return the bound self, if any; in case of pseudo-function role, pretend
334 // that the data member im_self does not exist.
335 if (IsPseudoFunc(pymeth)) {
336 PyErr_Format(PyExc_AttributeError,
337 "function %s has no attribute \'im_self\'",
338 pymeth->fMethodInfo->fName.c_str());
339 return nullptr;
340 } else if (pymeth->fSelf != 0) {
341 Py_INCREF((PyObject*)pymeth->fSelf);
342 return (PyObject*)pymeth->fSelf;
343 }
344
346}
347
348//----------------------------------------------------------------------------
349static PyObject* mp_meth_class(CPPOverload* pymeth, void*) {
350 // Return scoping class; in case of pseudo-function role, pretend that there
351 // is no encompassing class (i.e. global scope).
352 if (!IsPseudoFunc(pymeth) && pymeth->fMethodInfo->fMethods.size()) {
353 PyObject* pyclass = pymeth->fMethodInfo->fMethods[0]->GetScopeProxy();
354 if (!pyclass)
355 PyErr_Format(PyExc_AttributeError,
356 "function %s has no attribute \'im_class\'",
357 pymeth->fMethodInfo->fName.c_str());
358 return pyclass;
359 }
360
362}
363
364//----------------------------------------------------------------------------
365static PyObject* mp_func_closure(CPPOverload* /* pymeth */, void*) {
366 // Stub only, to fill out the python function interface.
368}
369
370//----------------------------------------------------------------------------
371static PyObject* mp_func_code(CPPOverload*, void*) {
372 // Code details are used in module inspect to fill out interactive help()
373 // not important for functioning of most code, so not implemented for p3 for
374 // now (TODO)
376}
377
378//----------------------------------------------------------------------------
379static PyObject* mp_func_defaults(CPPOverload* pymeth, void*) {
380 // Create a tuple of default values, if there is only one method (otherwise
381 // leave undefined: this is only used by inspect for interactive help())
382 CPPOverload::Methods_t& methods = pymeth->fMethodInfo->fMethods;
383
384 if (methods.size() != 1)
385 return PyTuple_New(0);
386
387 int maxarg = methods[0]->GetMaxArgs();
388
389 PyObject* defaults = PyTuple_New(maxarg);
390
391 int itup = 0;
392 for (int iarg = 0; iarg < maxarg; ++iarg) {
393 PyObject* defvalue = methods[0]->GetArgDefault(iarg);
394 if (defvalue)
395 PyTuple_SET_ITEM(defaults, itup++, defvalue);
396 else
397 PyErr_Clear();
398 }
399 _PyTuple_Resize(&defaults, itup);
400
401 return defaults;
402}
403
404//----------------------------------------------------------------------------
405static PyObject* mp_func_globals(CPPOverload* /* pymeth */, void*) {
406 // Return this function's global dict (hard-wired to be the cppjit module);
407 // used for lookup of names from co_code indexing into co_names.
408 PyObject* pyglobal = PyModule_GetDict(PyImport_AddModule((char*)"cppjit"));
409 Py_XINCREF(pyglobal);
410 return pyglobal;
411}
412
413//----------------------------------------------------------------------------
414static inline int set_flag(CPPOverload* pymeth, PyObject* value,
415 CallContext::ECallFlags flag, const char* name) {
416 // Generic setter of a (boolean) flag.
417 if (!value) { // accept as false (delete)
418 pymeth->fMethodInfo->fFlags &= ~flag;
419 return 0;
420 }
421
422 long istrue = PyLong_AsLong(value);
423 if (istrue == -1 && PyErr_Occurred()) {
424 PyErr_Format(PyExc_ValueError, "a boolean 1 or 0 is required for %s", name);
425 return -1;
426 }
427
428 if (istrue)
429 pymeth->fMethodInfo->fFlags |= flag;
430 else
431 pymeth->fMethodInfo->fFlags &= ~flag;
432
433 return 0;
434}
435
436//----------------------------------------------------------------------------
437static PyObject* mp_getcreates(CPPOverload* pymeth, void*) {
438 // Get '__creates__' boolean, which determines ownership of return values.
439 return PyLong_FromLong((long)IsCreator(pymeth->fMethodInfo->fFlags));
440}
441
442//----------------------------------------------------------------------------
443static int mp_setcreates(CPPOverload* pymeth, PyObject* value, void*) {
444 // Set '__creates__' boolean, which determines ownership of return values.
445 return set_flag(pymeth, value, CallContext::kIsCreator, "__creates__");
446}
447
448constexpr const char* mempolicy_error_message =
449 "The __mempolicy__ attribute can't be used, because in the past it was "
450 "reserved to manage the local memory policy. "
451 "If you want to do that now, please implement a pythonization for your "
452 "class that uses SetOwnership() to manage the "
453 "ownership of arguments according to your needs.";
454
455//----------------------------------------------------------------------------
456static PyObject* mp_getmempolicy(CPPOverload*, void*) {
457 PyErr_SetString(PyExc_RuntimeError, mempolicy_error_message);
458 return nullptr;
459}
460
461//----------------------------------------------------------------------------
462static int mp_setmempolicy(CPPOverload*, PyObject*, void*) {
463 PyErr_SetString(PyExc_RuntimeError, mempolicy_error_message);
464 return -1;
465}
466
467//----------------------------------------------------------------------------
468#define CPPJIT_BOOLEAN_PROPERTY(name, flag, label) \
469 static PyObject* mp_get##name(CPPOverload* pymeth, void*) { \
470 if (pymeth->fMethodInfo->fFlags & flag) { \
471 Py_RETURN_TRUE; \
472 } \
473 Py_RETURN_FALSE; \
474 } \
475 \
476 static int mp_set##name(CPPOverload* pymeth, PyObject* value, void*) { \
477 return set_flag(pymeth, value, flag, label); \
478 }
479
480// clang-format off
481CPPJIT_BOOLEAN_PROPERTY(lifeline, CallContext::kSetLifeLine, "__set_lifeline__")
482CPPJIT_BOOLEAN_PROPERTY(threaded, CallContext::kReleaseGIL, "__release_gil__")
483CPPJIT_BOOLEAN_PROPERTY(useffi, CallContext::kUseFFI, "__useffi__")
484CPPJIT_BOOLEAN_PROPERTY(sig2exc, CallContext::kProtected, "__sig2exc__")
485// clang-format on
486
487static PyObject* mp_getcppname(CPPOverload* pymeth, void*) {
488 if ((void*)pymeth == (void*)&CPPOverload_Type)
489 return PyUnicode_FromString("CPPOverload_Type");
490
491 auto& methods = pymeth->fMethodInfo->fMethods;
492 if (methods.empty())
493 return PyUnicode_FromString("void (*)()"); // debatable
494
495 if (methods.size() == 1)
496 return methods[0]->GetTypeName();
497
498 return PyUnicode_FromString("void* (*)(...)"); // id.
499}
500
501//----------------------------------------------------------------------------
502static PyGetSetDef mp_getset[] = {
503 {(char*)"__name__", (getter)mp_name, nullptr, nullptr, nullptr},
504 {(char*)"__module__", (getter)mp_module, nullptr, nullptr, nullptr},
505 {(char*)"__doc__", (getter)mp_doc, (setter)mp_doc_set, nullptr, nullptr},
506
507 // to be more python-like, where these are duplicated as well; to actually
508 // derive from the python method or function type is too memory-expensive,
509 // given that most of the members of those types would not be used
510 {(char*)"im_func", (getter)mp_meth_func, nullptr, nullptr, nullptr},
511 {(char*)"im_self", (getter)mp_meth_self, nullptr, nullptr, nullptr},
512 {(char*)"im_class", (getter)mp_meth_class, nullptr, nullptr, nullptr},
513
514 {(char*)"func_closure", (getter)mp_func_closure, nullptr, nullptr, nullptr},
515 {(char*)"func_code", (getter)mp_func_code, nullptr, nullptr, nullptr},
516 {(char*)"func_defaults", (getter)mp_func_defaults, nullptr, nullptr,
517 nullptr},
518 {(char*)"func_globals", (getter)mp_func_globals, nullptr, nullptr, nullptr},
519 {(char*)"func_doc", (getter)mp_doc, (setter)mp_doc_set, nullptr, nullptr},
520 {(char*)"func_name", (getter)mp_name, nullptr, nullptr, nullptr},
521 {(char*)"func_overloads_types", (getter)mp_func_overloads_types, nullptr,
522 nullptr, nullptr},
523 {(char*)"func_overloads_names", (getter)mp_func_overloads_names, nullptr,
524 nullptr, nullptr},
525
526 // flags to control behavior
527 {(char*)"__creates__", (getter)mp_getcreates, (setter)mp_setcreates,
528 (char*)"For ownership rules of result: if true, objects are python-owned",
529 nullptr},
530 {(char*)"__mempolicy__", (getter)mp_getmempolicy, (setter)mp_setmempolicy,
531 (char*)"Unused", nullptr},
532 {(char*)"__set_lifeline__", (getter)mp_getlifeline, (setter)mp_setlifeline,
533 (char*)"If true, set a lifeline from the return value onto self", nullptr},
534 {(char*)"__release_gil__", (getter)mp_getthreaded, (setter)mp_setthreaded,
535 (char*)"If true, releases GIL on call into C++", nullptr},
536 {(char*)"__useffi__", (getter)mp_getuseffi, (setter)mp_setuseffi,
537 (char*)"not implemented", nullptr},
538 {(char*)"__sig2exc__", (getter)mp_getsig2exc, (setter)mp_setsig2exc,
539 (char*)"If true, turn signals into Python exceptions", nullptr},
540
541 // basic reflection information
542 {(char*)"__cpp_name__", (getter)mp_getcppname, nullptr, nullptr, nullptr},
543
544 {(char*)nullptr, nullptr, nullptr, nullptr, nullptr}};
545
546//= cpyrt method proxy function behavior ==================================
547static PyObject* mp_vectorcall(CPPOverload* pymeth, PyObject* const* args,
548 size_t nargsf, PyObject* kwds) {
549 // Call the appropriate overload of this method.
550
551 // If called from a descriptor, then this could be a bound function with
552 // non-zero self; otherwise pymeth->fSelf is expected to always be nullptr.
553
554 CPPInstance* im_self = pymeth->fSelf;
555
556 // get local handles to proxy internals
557 auto& methods = pymeth->fMethodInfo->fMethods;
558
559 CPPOverload::Methods_t::size_type nMethods = methods.size();
560
561 CallContext ctxt{};
562 const auto mflags = pymeth->fMethodInfo->fFlags;
563 ctxt.fFlags |= (mflags & CallContext::kReleaseGIL);
564 ctxt.fFlags |= (mflags & CallContext::kProtected);
565 if (IsConstructor(pymeth->fMethodInfo->fFlags))
566 ctxt.fFlags |= CallContext::kIsConstructor;
567 ctxt.fFlags |=
569 ctxt.fPyContext = (PyObject*)im_self; // no Py_INCREF as no ownership
570
571 // check implicit conversions status (may be disallowed to prevent recursion)
572 ctxt.fFlags |= (pymeth->fFlags & CallContext::kNoImplicit);
573
574 // simple case
575 if (nMethods == 1) {
576 if (!NoImplicit(&ctxt))
577 ctxt.fFlags |= CallContext::kAllowImplicit; // no two rounds needed
578 PyObject* result = methods[0]->Call(im_self, args, nargsf, kwds, &ctxt);
579 return HandleReturn(pymeth, im_self, result);
580 }
581
582 // otherwise, handle overloading
583 uint64_t sighash = HashSignature(args, nargsf);
584
585 // look for known signatures ...
586 auto& dispatchMap = pymeth->fMethodInfo->fDispatchMap;
587 PyCallable* memoized_pc = nullptr;
588 for (const auto& p : dispatchMap) {
589 if (p.first == sighash) {
590 memoized_pc = p.second;
591 break;
592 }
593 }
594 if (memoized_pc) {
595 // it is necessary to enable implicit conversions as the memoized call may
596 // be from such a conversion case; if the call fails, the implicit flag is
597 // reset below
598 if (!NoImplicit(&ctxt))
599 ctxt.fFlags |= CallContext::kAllowImplicit;
600 PyObject* result = memoized_pc->Call(im_self, args, nargsf, kwds, &ctxt);
601 if (result)
602 return HandleReturn(pymeth, im_self, result);
603
604 // fall through: python is dynamic, and so, the hashing isn't infallible
607 PyErr_Clear();
608 ResetCallState(pymeth->fSelf, im_self);
609 }
610
611 // ... otherwise loop over all methods and find the one that does not fail
612 if (!IsSorted(mflags)) {
613 // sorting is based on priority, which is not stored on the method as it is
614 // used only once, so copy the vector of methods into one where the priority
615 // can be stored during sorting
616 std::vector<std::pair<int, PyCallable*>> pm;
617 pm.reserve(methods.size());
618 for (auto ptr : methods)
619 pm.emplace_back(ptr->GetPriority(), ptr);
620 std::stable_sort(pm.begin(), pm.end(), PriorityCmp);
621 for (CPPOverload::Methods_t::size_type i = 0; i < methods.size(); ++i)
622 methods[i] = pm[i].second;
623 pymeth->fMethodInfo->fFlags |= CallContext::kIsSorted;
624 }
625
626 // three stages, as the C++ ranking: exact matches, standard conversions
627 // (kNoImplicit: no user-defined construction from the argument), then
628 // implicit conversions
629 std::vector<Utility::PyError_t> errors;
630 std::vector<bool> implicit_possible(methods.size());
631 bool bHaveImplicit = false;
632 for (int stage = 0; stage < 3; ++stage) {
633 if (stage == 1)
634 ctxt.fFlags |= CallContext::kNoImplicit;
635 else if (stage == 2) {
636 ctxt.fFlags &= ~CallContext::kNoImplicit;
637 ctxt.fFlags |= CallContext::kAllowImplicit;
638 }
639 for (CPPOverload::Methods_t::size_type i = 0; i < nMethods; ++i) {
640 if (stage && !implicit_possible[i])
641 continue; // did not set implicit conversion, so don't try again
642
643 PyObject* result = methods[i]->Call(im_self, args, nargsf, kwds, &ctxt);
644 if (result) {
645 // success: update the dispatch map for subsequent calls
646 if (!memoized_pc)
647 dispatchMap.push_back(std::make_pair(sighash, methods[i]));
648 else {
649 // debatable: apparently there are two methods that map onto the same
650 // sighash and preferring the latest may result in "ping pong."
651 for (auto& p : dispatchMap) {
652 if (p.first == sighash) {
653 p.second = methods[i];
654 break;
655 }
656 }
657 }
658
659 return HandleReturn(pymeth, im_self, result);
660 }
661
662 // else failure ..
663 if (stage != 0) {
664 PyErr_Clear(); // first stage errors should be the more informative
665 ResetCallState(pymeth->fSelf, im_self);
666 continue;
667 }
668
669 // collect error message/trace (automatically clears exception, too)
670 if (!PyErr_Occurred()) {
671 // this should not happen; set an error to prevent core dump and report
672 PyObject* sig = methods[i]->GetPrototype();
673 PyErr_Format(PyExc_SystemError, "%s =>\n %s", PyUnicode_AsUTF8(sig),
674 (char*)"nullptr result without error in overload call");
675 Py_DECREF(sig);
676 }
677
678 // retrieve, store, and clear errors
679 bool callee_error = ctxt.fFlags & (CallContext::kPyException |
682 Utility::FetchError(errors, callee_error);
683
684 if (HaveImplicit(&ctxt)) {
685 bHaveImplicit = true;
686 implicit_possible[i] = true;
687 ctxt.fFlags &= ~CallContext::kHaveImplicit;
688 } else
689 implicit_possible[i] = false;
690 ResetCallState(pymeth->fSelf, im_self);
691 }
692
693 // only move forward if implicit conversions are available
694 if (!bHaveImplicit)
695 break;
696 }
697
698 // first summarize, then add details
699 PyObject* topmsg = PyUnicode_FromFormat(
700 "none of the %d overloaded methods succeeded. Full details:",
701 (int)nMethods);
702 SetDetailedException(std::move(errors), topmsg /* steals */,
703 PyExc_TypeError /* default error */);
704
705 // report failure
706 return nullptr;
707}
708
709//----------------------------------------------------------------------------
710static PyObject* mp_str(CPPOverload* cppinst) {
711 // Print a description that includes the C++ name
712 std::ostringstream s;
713 s << "<C++ overload \"" << cppinst->fMethodInfo->fName << "\" at "
714 << (void*)cppinst << ">";
715 return PyUnicode_FromString(s.str().c_str());
716}
717
718//----------------------------------------------------------------------------
719static CPPOverload* mp_descr_get(CPPOverload* pymeth, CPPInstance* pyobj,
720 PyObject*) {
721 // Descriptor; create and return a new, possibly bound, method proxy. This
722 // method has evolved with versions of python as follows:
723 //
724 // Python version | Action
725 // <- py2.7 | bound methods need to be first-class objects, so
726 // create a new
727 // | method object if self is not nullptr or Py_None
728 // py3.0-py3.7 | bound methods are no longer a language requirement,
729 // but
730 // | still supported: for convenience, retain old behavior
731 // py3.8 <= | vector calls no longer call the descriptor, so when it
732 // is
733 // | called, the method is likely stored, so should be new
734 // object
735
736 // create a new method object
737 bool gc_track = false;
738 CPPOverload* newPyMeth = free_list;
739 if (newPyMeth != NULL) {
740 free_list = (CPPOverload*)(newPyMeth->fSelf);
741 (void)PyObject_INIT(newPyMeth, &CPPOverload_Type);
742 numfree--;
743 } else {
744 newPyMeth = PyObject_GC_New(CPPOverload, &CPPOverload_Type);
745 if (!newPyMeth)
746 return nullptr;
747 gc_track = true;
748 }
749
750 // method info is shared, as it contains the collected overload knowledge
751 *pymeth->fMethodInfo->fRefCount += 1;
752 newPyMeth->fMethodInfo = pymeth->fMethodInfo;
753
754 newPyMeth->fVectorCall = pymeth->fVectorCall;
755
756 if (pyobj && (PyObject*)pyobj != Py_None) {
757 Py_INCREF((PyObject*)pyobj);
758 newPyMeth->fSelf = pyobj;
759 newPyMeth->fFlags = CallContext::kNone;
760 } else {
761 newPyMeth->fSelf = nullptr;
762 newPyMeth->fFlags = CallContext::kCallDirect;
763 }
764
765 // vector calls don't get here, unless a method is looked up on an instance,
766 // for e.g. class methods (C++ static); notify downstream to expect a 'self'
767 newPyMeth->fFlags |= CallContext::kFromDescr;
768
769 if (gc_track)
770 PyObject_GC_Track(newPyMeth);
771
772 return newPyMeth;
773}
774
775//= cpyrt method proxy construction/destruction ===========================
776static CPPOverload* mp_new(PyTypeObject*, PyObject*, PyObject*) {
777 // Create a new method proxy object.
778 CPPOverload* pymeth = PyObject_GC_New(CPPOverload, &CPPOverload_Type);
779 pymeth->fSelf = nullptr;
780 pymeth->fFlags = CallContext::kNone;
781 pymeth->fMethodInfo = new CPPOverload::MethodInfo_t;
782
783 PyObject_GC_Track(pymeth);
784 return pymeth;
785}
786
787//----------------------------------------------------------------------------
788static void mp_dealloc(CPPOverload* pymeth) {
789 // Deallocate memory held by method proxy object.
790 PyObject_GC_UnTrack(pymeth);
791
792 Py_CLEAR(pymeth->fSelf);
793
794 if (--(*pymeth->fMethodInfo->fRefCount) <= 0) {
795 delete pymeth->fMethodInfo;
796 }
797
799 pymeth->fSelf = (cpyrt::CPPInstance*)free_list;
800 free_list = pymeth;
801 numfree++;
802 } else {
803 PyObject_GC_Del(pymeth);
804 }
805}
806
807//----------------------------------------------------------------------------
808static Py_ssize_t mp_hash(CPPOverload* pymeth) {
809// Hash of method proxy object for insertion into dictionaries; with actual
810// method (fMethodInfo) shared, its address is best suited.
811#if PY_VERSION_HEX >= 0x030d0000
812 return Py_HashPointer(pymeth->fMethodInfo);
813#else
814 return _Py_HashPointer(pymeth->fMethodInfo);
815#endif
816}
817
818//----------------------------------------------------------------------------
819static int mp_traverse(CPPOverload* pymeth, visitproc visit, void* args) {
820 // Garbage collector traverse of held python member objects.
821 if (pymeth->fSelf)
822 return visit((PyObject*)pymeth->fSelf, args);
823
824 return 0;
825}
826
827//----------------------------------------------------------------------------
828static int mp_clear(CPPOverload* pymeth) {
829 // Garbage collector clear of held python member objects.
830 Py_CLEAR(pymeth->fSelf);
831
832 return 0;
833}
834
835//----------------------------------------------------------------------------
836static PyObject* mp_richcompare(CPPOverload* self, CPPOverload* other, int op) {
837 // Rich set of comparison objects; only equals is defined.
838 if (op != Py_EQ)
839 return PyType_Type.tp_richcompare((PyObject*)self, (PyObject*)other, op);
840
841 // defined by type + (shared) MethodInfo + bound self, with special case for
842 // fSelf (i.e. pseudo-function)
843 if ((Py_TYPE(self) == Py_TYPE(other) &&
844 self->fMethodInfo == other->fMethodInfo) &&
845 ((IsPseudoFunc(self) && IsPseudoFunc(other)) ||
846 self->fSelf == other->fSelf)) {
848 }
850}
851
852//= cpyrt method proxy access to internals ================================
853static PyObject* mp_overload(CPPOverload* pymeth, PyObject* args) {
854 // Select and call a specific C++ overload, based on its signature.
855 const char* sigarg = nullptr;
856 PyObject* sigarg_tuple = nullptr;
857 int want_const = -1;
858 Py_ssize_t args_size = PyTuple_GET_SIZE(args);
859 if (args_size && PyArg_ParseTuple(args, const_cast<char*>("s|i:__overload__"),
860 &sigarg, &want_const)) {
861 want_const = args_size == 1 ? -1 : want_const;
862 return pymeth->FindOverload(sigarg ? sigarg : "", want_const);
863 } else if (args_size &&
864 PyArg_ParseTuple(args, const_cast<char*>("O|i:__overload__"),
865 &sigarg_tuple, &want_const)) {
866 PyErr_Clear();
867 want_const = args_size == 1 ? -1 : want_const;
868 return pymeth->FindOverload(sigarg_tuple, want_const);
869 } else {
870 PyErr_Format(PyExc_TypeError, "Unexpected arguments to __overload__");
871 return nullptr;
872 }
873}
874
875static PyObject* mp_add_overload(CPPOverload* pymeth, PyObject* new_overload) {
876 TPythonCallback* cb = new TPythonCallback(new_overload);
877 pymeth->AdoptMethod(cb);
879}
880
881static PyObject* mp_reflex(CPPOverload* pymeth, PyObject* args) {
882 // Provide the requested reflection information.
883 interop::Reflex::RequestId_t request = -1;
885 if (!PyArg_ParseTuple(args, const_cast<char*>("i|i:__cpp_reflex__"), &request,
886 &format))
887 return nullptr;
888
889 return pymeth->fMethodInfo->fMethods[0]->Reflex(request, format);
890}
891
892//----------------------------------------------------------------------------
893static PyMethodDef mp_methods[] = {
894 {(char*)"__overload__", (PyCFunction)mp_overload, METH_VARARGS,
895 (char*)"select overload for dispatch"},
896 {(char*)"__add_overload__", (PyCFunction)mp_add_overload, METH_O,
897 (char*)"add a new overload"},
898 {(char*)"__cpp_reflex__", (PyCFunction)mp_reflex, METH_VARARGS,
899 (char*)"C++ overload reflection information"},
900 {(char*)nullptr, nullptr, 0, nullptr}};
901
902} // unnamed namespace
903
904//= cpyrt method proxy type ===============================================
905PyTypeObject CPPOverload_Type = {
906 PyVarObject_HEAD_INIT(&PyType_Type,
907 0)(char*) "cppjit.CPPOverload", // tp_name
908 sizeof(CPPOverload), // tp_basicsize
909 0, // tp_itemsize
910 (destructor)mp_dealloc, // tp_dealloc
911 offsetof(CPPOverload, fVectorCall),
912 0, // tp_getattr
913 0, // tp_setattr
914 0, // tp_as_async / tp_compare
915 0, // tp_repr
916 0, // tp_as_number
917 0, // tp_as_sequence
918 0, // tp_as_mapping
919 (hashfunc)mp_hash, // tp_hash
920 (ternaryfunc)PyVectorcall_Call, // tp_call
921 (reprfunc)mp_str, // tp_str
922 0, // tp_getattro
923 0, // tp_setattro
924 0, // tp_as_buffer
925 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_VECTORCALL |
926 Py_TPFLAGS_METHOD_DESCRIPTOR, // tp_flags
927 (char*)"cppjit method proxy (internal)", // tp_doc
928 (traverseproc)mp_traverse, // tp_traverse
929 (inquiry)mp_clear, // tp_clear
930 (richcmpfunc)mp_richcompare, // tp_richcompare
931 0, // tp_weaklistoffset
932 0, // tp_iter
933 0, // tp_iternext
934 mp_methods, // tp_methods
935 0, // tp_members
936 mp_getset, // tp_getset
937 0, // tp_base
938 0, // tp_dict
939 (descrgetfunc)mp_descr_get, // tp_descr_get
940 0, // tp_descr_set
941 0, // tp_dictoffset
942 0, // tp_init
943 0, // tp_alloc
944 (newfunc)mp_new, // tp_new
945 0, // tp_free
946 0, // tp_is_gc
947 0, // tp_bases
948 0, // tp_mro
949 0, // tp_cache
950 0, // tp_subclasses
951 0, // tp_weaklist
952 0, // tp_del
953 0, // tp_version_tag
954 0, // tp_finalize
955 0 // tp_vectorcall
957
958} // namespace cppjit::cpyrt
959
960//- public members -----------------------------------------------------------
961void cpyrt::CPPOverload::Set(const std::string& name,
962 std::vector<PyCallable*>& methods) {
963 // Fill in the data of a freshly created method proxy.
965 fMethodInfo->fMethods.swap(methods);
966 fMethodInfo->fFlags &= ~CallContext::kIsSorted;
967
968 // special case: all constructors are considered creators by default
969 if (name == "__init__")
972
973 // special case, in heuristics mode also tag *Clone* methods as creators. Only
974 // check that Clone is present in the method name, not in the template
975 // argument list.
977 std::string_view name_maybe_template = name;
978 auto begin_template = name_maybe_template.find_first_of('<');
979 if (begin_template <= name_maybe_template.size()) {
980 name_maybe_template = name_maybe_template.substr(0, begin_template);
981 }
982 if (name_maybe_template.find("Clone") != std::string_view::npos) {
984 }
985 }
986
987 fVectorCall = (vectorcallfunc)mp_vectorcall;
988}
989
990//----------------------------------------------------------------------------
992 // Fill in the data of a freshly created method proxy.
993 fMethodInfo->fMethods.push_back(pc);
994 fMethodInfo->fFlags &= ~CallContext::kIsSorted;
995}
996
997//----------------------------------------------------------------------------
999 if (!HasMethods()) // if fresh method being filled: also copy flags
1000 fMethodInfo->fFlags = meth->fMethodInfo->fFlags;
1001 fMethodInfo->fMethods.insert(fMethodInfo->fMethods.end(),
1002 meth->fMethodInfo->fMethods.begin(),
1003 meth->fMethodInfo->fMethods.end());
1004 fMethodInfo->fFlags &= ~CallContext::kIsSorted;
1005 meth->fMethodInfo->fDispatchMap.clear();
1006 meth->fMethodInfo->fMethods.clear();
1007}
1008
1009//----------------------------------------------------------------------------
1010PyObject* cpyrt::CPPOverload::FindOverload(const std::string& signature,
1011 int want_const) {
1012 bool accept_any = signature == ":any:";
1013 CPPOverload* newmeth = nullptr;
1014
1015 std::string sig1{"("};
1016 if (!accept_any) {
1017 sig1.append(signature);
1018 sig1.append(")");
1019 sig1.erase(std::remove(sig1.begin(), sig1.end(), ' '), std::end(sig1));
1020 }
1021
1022 CPPOverload::Methods_t& methods = fMethodInfo->fMethods;
1023 for (auto& meth : methods) {
1024 bool found = accept_any;
1025 if (!found) {
1026 PyObject* pysig2 = meth->GetSignature(false);
1027 std::string sig2(PyUnicode_AsUTF8(pysig2));
1028 sig2.erase(std::remove(sig2.begin(), sig2.end(), ' '), std::end(sig2));
1029 Py_DECREF(pysig2);
1030 if (sig1 == sig2)
1031 found = true;
1032
1033 if (!found) {
1034 pysig2 = meth->GetSignature(true);
1035 std::string sig3(PyUnicode_AsUTF8(pysig2));
1036 sig3.erase(std::remove(sig3.begin(), sig3.end(), ' '), std::end(sig3));
1037 Py_DECREF(pysig2);
1038 if (sig1 == sig3)
1039 found = true;
1040 }
1041 }
1042
1043 if (found && 0 <= want_const) {
1044 bool isconst = meth->IsConst();
1045 if (!((want_const && isconst) || (!want_const && !isconst)))
1046 found = false;
1047 }
1048
1049 if (found) {
1050 if (!newmeth) {
1051 newmeth = mp_new(nullptr, nullptr, nullptr);
1053 vec.push_back(meth->Clone());
1054 newmeth->Set(fMethodInfo->fName, vec);
1055
1056 if (fSelf) {
1057 Py_INCREF(fSelf);
1058 newmeth->fSelf = fSelf;
1059 }
1060 newmeth->fMethodInfo->fFlags = fMethodInfo->fFlags;
1061 } else
1062 newmeth->AdoptMethod(meth->Clone());
1063
1064 if (!accept_any)
1065 return (PyObject*)newmeth;
1066 }
1067 }
1068
1069 if (!newmeth)
1070 PyErr_Format(PyExc_LookupError, "signature \"%s\" not found",
1071 signature.c_str());
1072
1073 return (PyObject*)newmeth;
1074}
1075
1077 int want_const) {
1078 Py_ssize_t n = PyTuple_Size(args_tuple);
1079
1080 CPPOverload::Methods_t& methods = fMethodInfo->fMethods;
1081
1082 // This value is set based on the maximum penalty in
1083 // interop::CompareMethodArgType
1084 Py_ssize_t min_score = INT_MAX;
1085 bool found = false;
1086 size_t best_method = 0, method_index = 0;
1087
1088 for (auto& meth : methods) {
1089 if (0 <= want_const) {
1090 bool isconst = meth->IsConst();
1091 if (!((want_const && isconst) || (!want_const && !isconst)))
1092 continue;
1093 }
1094
1095 int score = meth->GetArgMatchScore(args_tuple);
1096
1097 if (score < min_score) {
1098 found = true;
1099 min_score = score;
1100 best_method = method_index;
1101 }
1102
1103 method_index++;
1104 }
1105
1106 if (!found) {
1107 std::string sigargs("(");
1108
1109 for (int i = 0; i < n; i++) {
1110 PyObject* pItem = PyTuple_GetItem(args_tuple, i);
1111 if (!PyUnicode_Check(pItem)) {
1112 PyErr_Format(PyExc_LookupError,
1113 "argument types should be in string format");
1114 return (PyObject*)nullptr;
1115 }
1116 std::string arg_type(PyUnicode_AsUTF8(pItem));
1117 sigargs += arg_type + ", ";
1118 }
1119 sigargs += ")";
1120
1121 PyErr_Format(PyExc_LookupError, "signature with arguments \"%s\" not found",
1122 sigargs.c_str());
1123 return (PyObject*)nullptr;
1124 }
1125
1126 CPPOverload* newmeth = mp_new(nullptr, nullptr, nullptr);
1128 vec.push_back(methods[best_method]->Clone());
1129 newmeth->Set(fMethodInfo->fName, vec);
1130
1131 if (fSelf) {
1132 Py_INCREF(fSelf);
1133 newmeth->fSelf = fSelf;
1134 }
1135 newmeth->fMethodInfo->fFlags = fMethodInfo->fFlags;
1136
1137 return (PyObject*)newmeth;
1138}
1139
1140//----------------------------------------------------------------------------
1142 // Destructor (this object is reference counted).
1143 for (Methods_t::iterator it = fMethods.begin(); it != fMethods.end(); ++it) {
1144 delete *it;
1145 }
1146 fMethods.clear();
1147 delete fRefCount;
1148 Py_XDECREF(fDoc);
1149}
1150
1151// TODO: something like PyMethod_Fini to clear up the free_list
#define CPPOverload_MAXFREELIST
#define CPPJIT_BOOLEAN_PROPERTY(name, flag, label)
PyObject * fCallable
_object PyObject
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 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 Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t format
char name[80]
Definition TGX11.cxx:142
void AdoptMethod(PyCallable *pc)
void MergeOverload(CPPOverload *meth)
MethodInfo_t * fMethodInfo
Definition CPPOverload.h:83
std::vector< PyCallable * > Methods_t
Definition CPPOverload.h:47
PyObject * FindOverload(const std::string &signature, int want_const=-1)
vectorcallfunc fVectorCall
Definition CPPOverload.h:85
void Set(const std::string &name, std::vector< PyCallable * > &methods)
PyObject_HEAD CPPInstance * fSelf
Definition CPPOverload.h:82
#define Py_RETURN_TRUE
Definition cpyrt.h:73
#define Py_RETURN_FALSE
Definition cpyrt.h:77
PyObject *const * cpyrt_PyArgs_t
Definition cpyrt.h:81
#define Py_TPFLAGS_HAVE_VECTORCALL
Definition cpyrt.h:108
#define CPYRT_PYTYPE_TAIL
Definition cpyrt.h:159
#define Py_RETURN_NONE
Definition cpyrt.h:69
#define cpyrt_PyObject_Call
Definition cpyrt.h:99
const Int_t n
Definition legend1.C:16
size_t FetchError(std::vector< PyError_t > &, bool is_cpp=false)
Definition Utility.cxx:1502
void SetDetailedException(std::vector< PyError_t > &&errors, PyObject *topmsg, PyObject *defexc)
Definition Utility.cxx:1512
bool IsConstructor(uint64_t flags)
bool IsSorted(uint64_t flags)
uint64_t HashSignature(cpyrt_PyArgs_t args, size_t nargsf)
Definition CPPOverload.h:16
static PyMethodObject * free_list
PyObject * GetScopeProxy(interop::TCppScope_t)
PyTypeObject CPPOverload_Type
bool IsCreator(uint64_t flags)
bool NoImplicit(CallContext *ctxt)
bool HaveImplicit(CallContext *ctxt)
static int numfree
bool CPPInstance_Check(T *object)
const FormatId_t OPTIMAL
Definition Reflex.h:22
TCppFuncAddr_t GetFunctionAddress(TCppMethod_t method, bool check_enabled=true)
size_t SizeOf(TCppScope_t klass)
void * TCppFuncAddr_t
Definition API.h:46
Definition API.cxx:34
CPPOverload::DispatchMap_t fDispatchMap
Definition CPPOverload.h:56
static uint32_t & GlobalPolicyFlags()