Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TemplateProxy.cxx
Go to the documentation of this file.
1// Bindings
2#include "CPyCppyy.h"
3#include "TemplateProxy.h"
4#include "CPPClassMethod.h"
5#include "CPPConstructor.h"
6#include "CPPFunction.h"
7#include "CPPMethod.h"
8#include "CPPOverload.h"
9#include "PyCallable.h"
10#include "PyStrings.h"
11#include "Utility.h"
12
13// Standard
14#include <algorithm>
15
16
17namespace CPyCppyy {
18
19//----------------------------------------------------------------------------
20TemplateInfo::TemplateInfo() : fPyClass(nullptr), fNonTemplated(nullptr),
21 fTemplated(nullptr), fLowPriority(nullptr), fDoc(nullptr)
22{
23 /* empty */
24}
25
26//----------------------------------------------------------------------------
28{
30
35
36 for (const auto& p : fDispatchMap) {
37 for (const auto& c : p.second) {
38 Py_DECREF(c.second);
39 }
40 }
41}
42
43
44//----------------------------------------------------------------------------
46// Store overloads of this templated method.
47 bool isGreedy = false;
48 for (auto pc : mp->fMethodInfo->fMethods) {
49 if (pc->IsGreedy()) {
50 isGreedy = true;
51 break;
52 }
53 }
54
55 CPPOverload* cppol = isGreedy ? fTI->fLowPriority : fTI->fNonTemplated;
56 cppol->MergeOverload(mp);
57}
58
60// Store overload of this templated method.
61 CPPOverload* cppol = pc->IsGreedy() ? fTI->fLowPriority : fTI->fNonTemplated;
62 cppol->AdoptMethod(pc);
63}
64
66{
67// Store known template methods.
68 fTI->fTemplated->AdoptMethod(pc);
69}
70
71//----------------------------------------------------------------------------
74{
75// Instantiate (and cache) templated methods, return method if any
76 std::string proto = "";
77
78#if PY_VERSION_HEX >= 0x03080000
79// adjust arguments for self if this is a rebound (global) function
80 bool isNS = (((CPPScope*)fTI->fPyClass)->fFlags & CPPScope::kIsNamespace);
81 if (!isNS && CPyCppyy_PyArgs_GET_SIZE(args, nargsf) && \
82 (!fSelf ||
83 (fSelf == Py_None && !Cppyy::IsStaticTemplate(((CPPScope*)fTI->fPyClass)->fCppType, fname)))) {
84 args += 1;
85 nargsf -= 1;
86 }
87#endif
88
90 if (argc != 0) {
92 for (Py_ssize_t i = 0; i < argc; ++i) {
94
95 bool bArgSet = false;
96
97 // special case for arrays
99 if (pytc) {
101 memset(&bufinfo, 0, sizeof(Py_buffer));
102 std::string ptrdef;
104 for (int j = 0; j < bufinfo.ndim; ++j) ptrdef += "*";
106 } else {
107 ptrdef += "*";
108 PyErr_Clear();
109 }
110
112 if (pyptrname) {
114 bArgSet = true;
115 // string added, but not counted towards nStrings
116 }
117 Py_DECREF(pytc); pytc = nullptr;
118 } else
119 PyErr_Clear();
120
121 // if not arg set, try special case for ctypes
123
124 if (!bArgSet && pytc) {
126 if (!pyactname) {
127 // _type_ of a pointer to c_type is that type, which will have a type
130 pytc = newpytc;
131 if (pytc) {
132 pyactname = Utility::CT2CppName(pytc, "*", false);
133 } else
134 PyErr_Clear();
135 }
136 Py_XDECREF(pytc); pytc = nullptr;
137 if (pyactname) {
139 bArgSet = true;
140 // string added, but not counted towards nStrings
141 }
142 } else
143 PyErr_Clear();
144
145 if (!bArgSet && (Py_TYPE(itemi) == &TemplateProxy_Type)) {
147 PyObject *tmpl_name = CPyCppyy_PyText_FromFormat("decltype(%s%s)", tp->fTI->fCppName.c_str(), tp->fTemplateArgs ? CPyCppyy_PyText_AsString(tp->fTemplateArgs) : "");
149 bArgSet = true;
150 }
151 if (!bArgSet) {
152 // normal case (may well fail)
153 PyErr_Clear();
155 Py_INCREF(tp);
157 }
158 }
159
160#if PY_VERSION_HEX >= 0x03080000
162 for (Py_ssize_t i = 0; i < argc; ++i) {
166 }
167#else
168 Py_INCREF(args);
169 PyObject* pyargs = args;
170#endif
171 const std::string& name_v1 = \
172 Utility::ConstructTemplateArgs(nullptr, tpArgs, pyargs, pref, 0, pcnt);
173
176
177 // Propagate the error that occurs if we can't construct the C++ name
178 // from the provided template argument
179 if (PyErr_Occurred()) {
180 return nullptr;
181 }
182
183 if (name_v1.size())
184 proto = name_v1.substr(1, name_v1.size()-2);
185 }
186
187// the following causes instantiation as necessary
188 Cppyy::TCppScope_t scope = ((CPPClass*)fTI->fPyClass)->fCppType;
190 if (cppmeth) { // overload stops here
191 // A successful instantiation needs to be cached to pre-empt future instantiations. There
192 // are two names involved, the original asked (which may be partial) and the received.
193 //
194 // Caching scheme: if the match is exact, simply add the overload to the pre-existing
195 // one, or create a new overload for later lookups. If the match is not exact, do the
196 // same, but also create an alias. Only add exact matches to the set of known template
197 // instantiations, to prevent piling on from different partial instantiations.
198 //
199 // TODO: this caches the lookup method before the call, meaning that failing overloads
200 // can add already existing overloads to the set of methods.
201
203
204 // An initializer_list is preferred for the argument types, but should not leak into
205 // the argument types. If it did, replace with vector and lookup anew.
206 if (resname.find("initializer_list") != std::string::npos) {
207 auto pos = proto.find("initializer_list");
208 while (pos != std::string::npos) {
209 proto.replace(pos, 16, "vector");
210 pos = proto.find("initializer_list", pos + 6);
211 }
212
214 if (m2 && m2 != cppmeth) {
215 // replace if the new method with vector was found; otherwise just continue
216 // with the previously found method with initializer_list.
217 cppmeth = m2;
219 }
220 }
221
222 bool bExactMatch = fname == resname;
223
224 // lookup on existing name in case this was an overload, not a caching, failure
228 if (!pyol) PyErr_Clear();
230
231 if (pyol && !bIsCppOL && !TemplateProxy_Check(pyol)) {
232 // unknown object ... leave well alone
235 Py_DECREF(dct);
236 return nullptr;
237 }
238
239 // find the full name if the requested one was partial
240 PyObject* exact = nullptr;
242 if (!bExactMatch) {
244 if (!exact) PyErr_Clear();
245 }
246 Py_DECREF(dct);
247
248 bool bIsConstructor = false, bNeedsRebind = true;
249
250 PyCallable* meth = nullptr;
253 bNeedsRebind = false;
254 } else if (Cppyy::IsStaticMethod(cppmeth)) {
256 bNeedsRebind = false;
257 } else if (Cppyy::IsConstructor(cppmeth)) {
258 bIsConstructor = true;
260 } else
261 meth = new CPPMethod(scope, cppmeth);
262
263 // Case 1/2: method simply did not exist before
264 if (!pyol) {
265 // actual overload to use (now owns meth)
267 if (bIsConstructor) {
268 // TODO: this is an ugly hack :(
271 }
272
273 // add to class dictionary
274 PyType_Type.tp_setattro(fTI->fPyClass, pycachename, pyol);
275 }
276
277 // Case 3/4: pre-existing method that was either not found b/c the full
278 // templated name was constructed in this call or it failed as overload
279 else if (bIsCppOL) {
280 // TODO: see above, since the call hasn't happened yet, this overload may
281 // already exist and fail again.
282 ((CPPOverload*)pyol)->AdoptMethod(meth); // takes ownership
283 }
284
285 // Case 5: must be a template proxy, meaning that current template name is not
286 // a template overload
287 else {
288 ((TemplateProxy*)pyol)->AdoptTemplate(meth->Clone());
290 pyol = (PyObject*)CPPOverload_New(fname, meth); // takes ownership
291 }
292
293 // Special Case if name was aliased (e.g. typedef in template instantiation)
294 if (!exact && !bExactMatch) {
295 PyType_Type.tp_setattro(fTI->fPyClass, pyresname, pyol);
296 }
297
298 // cleanup
302
303 // retrieve fresh (for boundedness) and call
305 CPPOverload_Type.tp_descr_get(pyol, bNeedsRebind ? fSelf : nullptr, (PyObject*)&CPPOverload_Type);
307 return pymeth;
308 }
309
310 PyErr_Format(PyExc_TypeError, "Failed to instantiate \"%s(%s)\"", fname.c_str(), proto.c_str());
311 return nullptr;
312}
313
314
315//= CPyCppyy template proxy construction/destruction =========================
317{
318// Create a new empty template method proxy.
320 pytmpl->fSelf = nullptr;
321 pytmpl->fTemplateArgs = nullptr;
322 pytmpl->fWeakrefList = nullptr;
323 new (&pytmpl->fTI) TP_TInfo_t{};
324 pytmpl->fTI = std::make_shared<TemplateInfo>();
325
327 return pytmpl;
328}
329
330//----------------------------------------------------------------------------
332{
333 return (Py_hash_t)self;
334}
335
336//----------------------------------------------------------------------------
338{
339 if (op == Py_EQ || op == Py_NE) {
342
343 if (self->fTI == ((TemplateProxy*)other)->fTI)
345
347 }
348
350 return Py_NotImplemented;
351}
352
353//----------------------------------------------------------------------------
355{
356// Garbage collector clear of held python member objects.
357 Py_CLEAR(pytmpl->fSelf);
358 Py_CLEAR(pytmpl->fTemplateArgs);
359
360 return 0;
361}
362
363//----------------------------------------------------------------------------
365{
366// Destroy the given template method proxy.
367 if (pytmpl->fWeakrefList)
371 pytmpl->fTI.~TP_TInfo_t();
373}
374
375//----------------------------------------------------------------------------
377{
378// Garbage collector traverse of held python member objects.
379 Py_VISIT(pytmpl->fSelf);
380 Py_VISIT(pytmpl->fTemplateArgs);
381
382 return 0;
383}
384
385//----------------------------------------------------------------------------
387{
388 if (pytmpl->fTI->fDoc) {
389 Py_INCREF(pytmpl->fTI->fDoc);
390 return pytmpl->fTI->fDoc;
391 }
392
393// Forward to method proxies to doc all overloads
394 PyObject* doc = nullptr;
395 if (pytmpl->fTI->fNonTemplated->HasMethods())
396 doc = PyObject_GetAttrString((PyObject*)pytmpl->fTI->fNonTemplated, "__doc__");
397 if (pytmpl->fTI->fTemplated->HasMethods()) {
398 PyObject* doc2 = PyObject_GetAttrString((PyObject*)pytmpl->fTI->fTemplated, "__doc__");
399 if (doc && doc2) {
402 } else if (!doc && doc2) {
403 doc = doc2;
404 }
405 }
406 if (pytmpl->fTI->fLowPriority->HasMethods()) {
407 PyObject* doc2 = PyObject_GetAttrString((PyObject*)pytmpl->fTI->fLowPriority, "__doc__");
408 if (doc && doc2) {
411 } else if (!doc && doc2) {
412 doc = doc2;
413 }
414 }
415
416 if (doc)
417 return doc;
418
420}
421
422static int tpp_doc_set(TemplateProxy* pytmpl, PyObject *val, void *)
423{
424 Py_XDECREF(pytmpl->fTI->fDoc);
425 Py_INCREF(val);
426 pytmpl->fTI->fDoc = val;
427 return 0;
428}
429
430//----------------------------------------------------------------------------
431
432//= CPyCppyy template proxy callable behavior ================================
433
434#define TPPCALL_RETURN \
435{ errors.clear(); \
436 return result; }
437
438static inline std::string targs2str(TemplateProxy* pytmpl)
439{
440 if (!pytmpl || !pytmpl->fTemplateArgs) return "";
441 return CPyCppyy_PyText_AsString(pytmpl->fTemplateArgs);
442}
443
445{
446// Memoize a method in the dispatch map after successful call; replace old if need be (may be
447// with the same CPPOverload, just with more methods).
448 bool bInserted = false;
449 auto& v = pytmpl->fTI->fDispatchMap[use_targs ? targs2str(pytmpl) : ""];
450
452 for (auto& p : v) {
453 if (p.first == sighash) {
454 Py_DECREF(p.second);
455 p.second = pymeth;
456 bInserted = true;
457 }
458 }
459 if (!bInserted) v.push_back(std::make_pair(sighash, pymeth));
460}
461
463 CPyCppyy_PyArgs_t args, size_t nargsf, PyObject* kwds,
464 bool implicitOkay, bool use_targs, uint64_t sighash, std::vector<Utility::PyError_t>& errors)
465{
466// Forward a call to known overloads, if any.
467 if (pymeth->HasMethods()) {
468 PyObject* pycall = CPPOverload_Type.tp_descr_get(
470
471 if (!implicitOkay)
473
474 // now call the method with the arguments (loops internally)
477 if (result) {
480 }
482 }
483
484 return nullptr;
485}
486
488 CPyCppyy_PyArgs_t args, size_t nargsf, PyObject* kwds, bool impOK, uint64_t sighash)
489{
490// Actual call of a given overload: takes care of handlign of "self" and
491// dereferences the overloaded method after use.
492
496 bool isNS = (((CPPScope*)pytmpl->fTI->fPyClass)->fFlags & CPPScope::kIsNamespace);
497 if (isNS && pytmpl->fSelf && pytmpl->fSelf != Py_None) {
498 // this is a global method added a posteriori to the class
499 PyCallArgs cargs{(CPPInstance*&)pytmpl->fSelf, args, nargsf, kwds};
501 result = CPyCppyy_tp_call(pymeth, cargs.fArgs, cargs.fNArgsf, cargs.fKwds);
502 } else {
503 if (!pytmpl->fSelf && CPPOverload_Check(pymeth))
504 ((CPPOverload*)pymeth)->fFlags &= ~CallContext::kFromDescr;
506 }
507
508 if (result) {
509 Py_XDECREF(((CPPOverload*)pymeth)->fSelf); ((CPPOverload*)pymeth)->fSelf = nullptr; // unbind
511 }
512
513 Py_DECREF(pymeth); pymeth = nullptr;
514 return result;
515}
516
517#if PY_VERSION_HEX >= 0x03080000
519 TemplateProxy* pytmpl, PyObject* const *args, size_t nargsf, PyObject* kwds)
520#else
522#endif
523{
524// Dispatcher to the actual member method, several uses possible; in order:
525//
526// case 1: explicit template previously selected through subscript
527//
528// case 2: select known non-template overload
529//
530// obj.method(a0, a1, ...)
531// => obj->method(a0, a1, ...) // non-template
532//
533// case 3: select known template overload
534//
535// obj.method(a0, a1, ...)
536// => obj->method(a0, a1, ...) // all known templates
537//
538// case 4: auto-instantiation from types of arguments
539//
540// obj.method(a0, a1, ...)
541// => obj->method<type(a0), type(a1), ...>(a0, a1, ...)
542//
543// Note: explicit instantiation needs to use [] syntax:
544//
545// obj.method[type<a0>, type<a1>, ...](a0, a1, ...)
546//
547// case 5: low priority methods, such as ones that take void* arguments
548//
549
550// TODO: should previously instantiated templates be considered first?
551
552#if PY_VERSION_HEX < 0x03080000
553 size_t nargsf = PyTuple_GET_SIZE(args);
554#endif
555
556 PyObject *pymeth = nullptr, *result = nullptr;
557
558// short-cut through memoization map
560 uint64_t sighash = HashSignature(args, argc);
561
562 CPPOverload* ol = nullptr;
563 if (!pytmpl->fTemplateArgs) {
564 // look for known signatures ...
565 auto& v = pytmpl->fTI->fDispatchMap[""];
566 for (const auto& p : v) {
567 if (p.first == sighash) {
568 ol = p.second;
569 break;
570 }
571 }
572
573 if (ol != nullptr) {
574 if (!pytmpl->fSelf || pytmpl->fSelf == Py_None) {
576 } else {
577 pymeth = CPPOverload_Type.tp_descr_get(
580 Py_DECREF(pymeth); pymeth = nullptr;
581 }
582 if (result)
583 return result;
584 }
585 }
586
587// container for collecting errors
588 std::vector<Utility::PyError_t> errors;
590
591// case 1: explicit template previously selected through subscript
592 if (pytmpl->fTemplateArgs) {
593 // instantiate explicitly
594 PyObject* pyfullname = CPyCppyy_PyText_FromString(pytmpl->fTI->fCppName.c_str());
595 CPyCppyy_PyText_Append(&pyfullname, pytmpl->fTemplateArgs);
596
597 // first, lookup by full name, if previously stored
598 bool isNS = (((CPPScope*)pytmpl->fTI->fPyClass)->fFlags & CPPScope::kIsNamespace);
599 if (pytmpl->fSelf && pytmpl->fSelf != Py_None && !isNS)
601 else // by-passes custom scope getattr that searches into Cling
602 pymeth = PyType_Type.tp_getattro(pytmpl->fTI->fPyClass, pyfullname);
603
604 // attempt call if found (this may fail if there are specializations)
606 // since the template args are fully explicit, allow implicit conversion of arguments
608 if (result) {
611 }
613 } else if (pymeth && PyCallable_Check(pymeth)) {
614 // something different (user provided?)
617 if (result) {
620 }
622 } else if (!pymeth)
623 PyErr_Clear();
624
625 // not cached or failed call; try instantiation
626 pymeth = pytmpl->Instantiate(
628 if (pymeth) {
629 // attempt actual call; same as above, allow implicit conversion of arguments
631 if (result) {
634 }
635 }
636
637 // no drop through if failed (if implicit was desired, don't provide template args)
640 "Could not find \"%s\" (set cppyy.set_debug() for C++ errors):", CPyCppyy_PyText_AsString(pyfullname));
642 Utility::SetDetailedException(std::move(errors), topmsg /* steals */, PyExc_TypeError /* default error */);
643
644 return nullptr;
645 }
646
647// case 2: select known non-template overload
648 result = SelectAndForward(pytmpl, pytmpl->fTI->fNonTemplated, args, nargsf, kwds,
649 true /* implicitOkay */, false /* use_targs */, sighash, errors);
650 if (result)
652
653// case 3: select known template overload
654 result = SelectAndForward(pytmpl, pytmpl->fTI->fTemplated, args, nargsf, kwds,
655 false /* implicitOkay */, true /* use_targs */, sighash, errors);
656 if (result)
658
659// case 4: auto-instantiation from types of arguments
661 // TODO: no need to loop if there are no non-instance arguments; also, should any
662 // failed lookup be removed?
663 int pcnt = 0;
664 pymeth = pytmpl->Instantiate(pytmpl->fTI->fCppName, args, nargsf, pref, &pcnt);
665 if (pymeth) {
666 // attempt actual call; even if argument based, allow implicit conversions, for example for non-template arguments
669 }
671 if (!pcnt) break; // preference never used; no point trying others
672 }
673
674// case 5: low priority methods, such as ones that take void* arguments
675 result = SelectAndForward(pytmpl, pytmpl->fTI->fLowPriority, args, nargsf, kwds,
676 false /* implicitOkay */, false /* use_targs */, sighash, errors);
677 if (result)
679
680// error reporting is fraud, given the numerous steps taken, but more details seems better
681 if (!errors.empty()) {
682 PyObject* topmsg = CPyCppyy_PyText_FromString("Template method resolution failed:");
683 Utility::SetDetailedException(std::move(errors), topmsg /* steals */, PyExc_TypeError /* default error */);
684 } else {
685 PyErr_Format(PyExc_TypeError, "cannot resolve method template call for \'%s\'",
686 pytmpl->fTI->fCppName.c_str());
687 }
688
689 return nullptr;
690}
691
692//----------------------------------------------------------------------------
694{
695// create and use a new template proxy (language requirement)
697
698// new method is to be bound to current object (may be nullptr)
699 if (pyobj) {
701 newPyTmpl->fSelf = pyobj;
702 } else {
704 newPyTmpl->fSelf = Py_None;
705 }
706
707 Py_XINCREF(pytmpl->fTemplateArgs);
708 newPyTmpl->fTemplateArgs = pytmpl->fTemplateArgs;
709
710// copy name, class, etc. pointers
711 new (&newPyTmpl->fTI) std::shared_ptr<TemplateInfo>{pytmpl->fTI};
712
713#if PY_VERSION_HEX >= 0x03080000
714 newPyTmpl->fVectorCall = pytmpl->fVectorCall;
715#endif
716
717 return newPyTmpl;
718}
719
720
721//----------------------------------------------------------------------------
723{
724// Explicit template member lookup/instantiation; works by re-bounding. This method can
725// not cache overloads as instantiations need not be unique for the argument types due
726// to template specializations.
728 Py_XDECREF(typeBoundMethod->fTemplateArgs);
730 Utility::ConstructTemplateArgs(nullptr, args).c_str());
731// Propagate the error that occurs if we can't construct the C++ name
732// from the provided template argument
733 if (PyErr_Occurred()) {
734 return nullptr;
735 }
736 return (PyObject*)typeBoundMethod;
737}
738
739//-----------------------------------------------------------------------------
741{
742 return PyInt_FromLong(0); // dummy (__useffi__ unused)
743}
744
745//-----------------------------------------------------------------------------
746static int tpp_setuseffi(CPPOverload*, PyObject*, void*)
747{
748 return 0; // dummy (__useffi__ unused)
749}
750
751//-----------------------------------------------------------------------------
753 if (!self->fTemplateArgs) {
755 }
756
757 Py_INCREF(self->fTemplateArgs);
758 return self->fTemplateArgs;
759}
760
761//-----------------------------------------------------------------------------
763 PyErr_SetString(PyExc_AttributeError, "__template_args__ is read-only");
764 return -1;
765}
766
767//----------------------------------------------------------------------------
769 nullptr, (binaryfunc)tpp_subscript, nullptr
770};
771
773 {(char*)"__doc__", (getter)tpp_doc, (setter)tpp_doc_set, nullptr, nullptr},
774 {(char*)"__useffi__", (getter)tpp_getuseffi, (setter)tpp_setuseffi,
775 (char*)"unused", nullptr},
776 {(char*)"__template_args__", (getter)tpp_gettemplateargs, (setter)tpp_settemplateargs,
777 (char*)"the template arguments for this method", nullptr},
778 {(char*)nullptr, nullptr, nullptr, nullptr, nullptr},
779};
780
781
782//----------------------------------------------------------------------------
783void TemplateProxy::Set(const std::string& cppname, const std::string& pyname, PyObject* pyclass)
784{
785// Initialize the proxy for the given 'pyclass.'
786 fSelf = nullptr;
787 fTemplateArgs = nullptr;
788
789 fTI->fCppName = cppname;
791 fTI->fPyClass = pyclass;
792
793 std::vector<PyCallable*> dummy;
794 fTI->fNonTemplated = CPPOverload_New(pyname, dummy);
795 fTI->fTemplated = CPPOverload_New(pyname, dummy);
796 fTI->fLowPriority = CPPOverload_New(pyname, dummy);
797
798#if PY_VERSION_HEX >= 0x03080000
800#endif
801}
802
803
804//= CPyCppyy method proxy access to internals ================================
806{
807// Select and call a specific C++ overload, based on its signature.
808 const char* sigarg = nullptr;
809 const char* tmplarg = nullptr;
810 PyObject* sigarg_tuple = nullptr;
811 int want_const = -1;
812
815 std::string proto;
816
817 if (PyArg_ParseTuple(args, const_cast<char*>("s|i:__overload__"), &sigarg, &want_const)) {
818 want_const = PyTuple_GET_SIZE(args) == 1 ? -1 : want_const;
819
820 // check existing overloads in order
821 PyObject* ol = pytmpl->fTI->fNonTemplated->FindOverload(sigarg, want_const);
822 if (ol) return ol;
823 PyErr_Clear();
824 ol = pytmpl->fTI->fTemplated->FindOverload(sigarg, want_const);
825 if (ol) return ol;
826 PyErr_Clear();
827 ol = pytmpl->fTI->fLowPriority->FindOverload(sigarg, want_const);
828 if (ol) return ol;
829
830 proto = Utility::ConstructTemplateArgs(nullptr, args);
831 // Propagate the error that occurs if we can't construct the C++ name
832 // from the provided template argument
833 if (PyErr_Occurred()) {
834 return nullptr;
835 }
836
837 scope = ((CPPClass*)pytmpl->fTI->fPyClass)->fCppType;
839 scope, pytmpl->fTI->fCppName, proto.substr(1, proto.size()-2));
840 } else if (PyArg_ParseTuple(args, const_cast<char*>("ss:__overload__"), &sigarg, &tmplarg)) {
841 scope = ((CPPClass*)pytmpl->fTI->fPyClass)->fCppType;
842 std::string full_name = std::string(pytmpl->fTI->fCppName) + "<" + tmplarg + ">";
843
845 } else if (PyArg_ParseTuple(args, const_cast<char*>("O|i:__overload__"), &sigarg_tuple, &want_const)) {
846 PyErr_Clear();
847 want_const = PyTuple_GET_SIZE(args) == 1 ? -1 : want_const;
848
849 // check existing overloads in order
850 PyObject* ol = pytmpl->fTI->fNonTemplated->FindOverload(sigarg_tuple, want_const);
851 if (ol) return ol;
852 PyErr_Clear();
853 ol = pytmpl->fTI->fTemplated->FindOverload(sigarg_tuple, want_const);
854 if (ol) return ol;
855 PyErr_Clear();
856 ol = pytmpl->fTI->fLowPriority->FindOverload(sigarg_tuple, want_const);
857 if (ol) return ol;
858
859 proto.reserve(128);
860 proto.push_back('<');
862 for (int i = 0; i < n; i++) {
865 PyErr_Format(PyExc_LookupError, "argument types should be in string format");
866 return (PyObject*) nullptr;
867 }
869 if (i < n - 1)
870 proto.push_back(',');
871 }
872 proto.push_back('>');
873
874 scope = ((CPPClass*)pytmpl->fTI->fPyClass)->fCppType;
876 scope, pytmpl->fTI->fCppName, proto.substr(1, proto.size()-2));
877 } else {
878 PyErr_Format(PyExc_TypeError, "Unexpected arguments to __overload__");
879 return nullptr;
880 }
881
882// else attempt instantiation
883 if (!cppmeth) {
884 return nullptr;
885 }
886
887 PyErr_Clear();
888
889 // TODO: the next step should be consolidated with Instantiate()
890 PyCallable* meth = nullptr;
893 } else if (Cppyy::IsStaticMethod(cppmeth)) {
895 } else if (Cppyy::IsConstructor(cppmeth)) {
897 } else
898 meth = new CPPMethod(scope, cppmeth);
899
900 return (PyObject*)CPPOverload_New(pytmpl->fTI->fCppName+proto, meth);
901}
902
904 {(char*)"__overload__", (PyCFunction)tpp_overload, METH_VARARGS,
905 (char*)"select overload for dispatch" },
906 {(char*)nullptr, nullptr, 0, nullptr }
907};
908
909
910//= CPyCppyy template proxy type =============================================
913 (char*)"cppyy.TemplateProxy", // tp_name
914 sizeof(TemplateProxy), // tp_basicsize
915 0, // tp_itemsize
916 (destructor)tpp_dealloc, // tp_dealloc
917#if PY_VERSION_HEX >= 0x03080000
919#else
920 0, // tp_vectorcall_offset / tp_print
921#endif
922 0, // tp_getattr
923 0, // tp_setattr
924 0, // tp_as_async / tp_compare
925 0, // tp_repr
926 0, // tp_as_number
927 0, // tp_as_sequence
928 &tpp_as_mapping, // tp_as_mapping
929 (hashfunc)tpp_hash, // tp_hash
930#if PY_VERSION_HEX >= 0x03080000
931 (ternaryfunc)PyVectorcall_Call, // tp_call
932#else
933 (ternaryfunc)tpp_call, // tp_call
934#endif
935 0, // tp_str
936 0, // tp_getattro
937 0, // tp_setattro
938 0, // tp_as_buffer
940#if PY_VERSION_HEX >= 0x03080000
942#endif
943 , // tp_flags
944 (char*)"cppyy template proxy (internal)", // tp_doc
945 (traverseproc)tpp_traverse, // tp_traverse
946 (inquiry)tpp_clear, // tp_clear
947 (richcmpfunc)tpp_richcompare, // tp_richcompare
948 offsetof(TemplateProxy, fWeakrefList), // tp_weaklistoffset
949 0, // tp_iter
950 0, // tp_iternext
951 tpp_methods, // tp_methods
952 0, // tp_members
953 tpp_getset, // tp_getset
954 0, // tp_base
955 0, // tp_dict
956 (descrgetfunc)tpp_descr_get, // tp_descr_get
957 0, // tp_descr_set
958 0, // tp_dictoffset
959 0, // tp_init
960 0, // tp_alloc
961 (newfunc)tpp_new, // tp_new
962 0, // tp_free
963 0, // tp_is_gc
964 0, // tp_bases
965 0, // tp_mro
966 0, // tp_cache
967 0, // tp_subclasses
968 0 // tp_weaklist
969#if PY_VERSION_HEX >= 0x02030000
970 , 0 // tp_del
971#endif
972#if PY_VERSION_HEX >= 0x02060000
973 , 0 // tp_version_tag
974#endif
975#if PY_VERSION_HEX >= 0x03040000
976 , 0 // tp_finalize
977#endif
978#if PY_VERSION_HEX >= 0x03080000
979 , 0 // tp_vectorcall
980#endif
981#if PY_VERSION_HEX >= 0x030c0000
982 , 0 // tp_watched
983#endif
984#if PY_VERSION_HEX >= 0x030d0000
985 , 0 // tp_versions_used
986#endif
987};
988
989} // namespace CPyCppyy
#define Py_TYPE(ob)
Definition CPyCppyy.h:196
#define Py_RETURN_TRUE
Definition CPyCppyy.h:272
#define CPyCppyy_PyText_InternFromString
Definition CPyCppyy.h:82
#define Py_RETURN_FALSE
Definition CPyCppyy.h:276
int Py_ssize_t
Definition CPyCppyy.h:215
#define CPyCppyy_PyText_Append
Definition CPyCppyy.h:83
#define CPyCppyy_PyText_AsString
Definition CPyCppyy.h:76
static Py_ssize_t CPyCppyy_PyArgs_GET_SIZE(CPyCppyy_PyArgs_t args, size_t)
Definition CPyCppyy.h:337
PyObject * CPyCppyy_PyArgs_t
Definition CPyCppyy.h:330
#define CPyCppyy_PyText_AppendAndDel
Definition CPyCppyy.h:84
long Py_hash_t
Definition CPyCppyy.h:114
void CPyCppyy_PyBuffer_Release(PyObject *, Py_buffer *view)
Definition CPyCppyy.h:282
PyObject * CPyCppyy_PyObject_Call(PyObject *cb, PyObject *args, size_t, PyObject *kwds)
Definition CPyCppyy.h:346
#define CPyCppyy_PyText_FromFormat
Definition CPyCppyy.h:80
#define Py_RETURN_NONE
Definition CPyCppyy.h:268
PyObject * CPyCppyy_tp_call(PyObject *cb, PyObject *args, size_t, PyObject *kwds)
Definition CPyCppyy.h:349
#define CPyCppyy_PyText_FromString
Definition CPyCppyy.h:81
static PyObject * CPyCppyy_PyArgs_GET_ITEM(CPyCppyy_PyArgs_t args, Py_ssize_t i)
Definition CPyCppyy.h:331
#define CPyCppyy_PyText_Check
Definition CPyCppyy.h:74
#define PyVarObject_HEAD_INIT(type, size)
Definition CPyCppyy.h:194
_object PyObject
#define c(i)
Definition RSha256.hxx:101
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
#define TPPCALL_RETURN
const char * proto
Definition civetweb.c:18822
MethodInfo_t * fMethodInfo
Definition CPPOverload.h:79
virtual bool IsGreedy()=0
CPPOverload * fTemplated
CPPOverload * fLowPriority
TP_DispatchMap_t fDispatchMap
CPPOverload * fNonTemplated
PyObject * Instantiate(const std::string &fname, CPyCppyy_PyArgs_t tmplArgs, size_t nargsf, Utility::ArgPreference, int *pcnt=nullptr)
void Set(const std::string &cppname, const std::string &pyname, PyObject *pyclass)
PyObject_HEAD PyObject * fSelf
void AdoptTemplate(PyCallable *pc)
void AdoptMethod(PyCallable *pc)
void MergeOverload(CPPOverload *mp)
const Int_t n
Definition legend1.C:16
PyObject * gCTypesType
Definition PyStrings.cxx:40
PyObject * gTypeCode
Definition PyStrings.cxx:39
PyObject * CT2CppName(PyObject *pytc, const char *cpd, bool allow_voidp)
Definition Utility.h:44
void SetDetailedException(std::vector< PyError_t > &&errors, PyObject *topmsg, PyObject *defexc)
Definition Utility.cxx:1204
std::string ConstructTemplateArgs(PyObject *pyname, PyObject *tpArgs, PyObject *args=nullptr, ArgPreference=kNone, int argoff=0, int *pcnt=nullptr)
Definition Utility.cxx:636
size_t FetchError(std::vector< PyError_t > &, bool is_cpp=false)
Definition Utility.cxx:1193
CPPOverload * CPPOverload_New(const std::string &name, std::vector< PyCallable * > &methods)
static PyObject * tpp_richcompare(TemplateProxy *self, PyObject *other, int op)
bool AdjustSelf(PyCallArgs &cargs)
static PyObject * tpp_doc(TemplateProxy *pytmpl, void *)
static PyMappingMethods tpp_as_mapping
bool CPPOverload_Check(T *object)
Definition CPPOverload.h:94
static int tpp_clear(TemplateProxy *pytmpl)
static PyObject * tpp_call(TemplateProxy *pytmpl, PyObject *args, PyObject *kwds)
static PyObject * tpp_getuseffi(CPPOverload *, void *)
bool TemplateProxy_CheckExact(T *object)
static void tpp_dealloc(TemplateProxy *pytmpl)
uint64_t HashSignature(CPyCppyy_PyArgs_t args, size_t nargsf)
Definition CPPOverload.h:17
static int tpp_traverse(TemplateProxy *pytmpl, visitproc visit, void *arg)
static PyObject * tpp_overload(TemplateProxy *pytmpl, PyObject *args)
static int tpp_setuseffi(CPPOverload *, PyObject *, void *)
static PyGetSetDef tpp_getset[]
PyTypeObject CPPOverload_Type
PyTypeObject TemplateProxy_Type
static PyObject * tpp_subscript(TemplateProxy *pytmpl, PyObject *args)
static PyObject * CallMethodImp(TemplateProxy *pytmpl, PyObject *&pymeth, CPyCppyy_PyArgs_t args, size_t nargsf, PyObject *kwds, bool impOK, uint64_t sighash)
static void UpdateDispatchMap(TemplateProxy *pytmpl, bool use_targs, uint64_t sighash, CPPOverload *pymeth)
static int tpp_settemplateargs(TemplateProxy *, PyObject *, void *)
static TemplateProxy * tpp_new(PyTypeObject *, PyObject *, PyObject *)
static PyMethodDef tpp_methods[]
static int tpp_doc_set(TemplateProxy *pytmpl, PyObject *val, void *)
static Py_hash_t tpp_hash(TemplateProxy *self)
static PyObject * SelectAndForward(TemplateProxy *pytmpl, CPPOverload *pymeth, CPyCppyy_PyArgs_t args, size_t nargsf, PyObject *kwds, bool implicitOkay, bool use_targs, uint64_t sighash, std::vector< Utility::PyError_t > &errors)
static std::string targs2str(TemplateProxy *pytmpl)
static TemplateProxy * tpp_descr_get(TemplateProxy *pytmpl, PyObject *pyobj, PyObject *)
static PyObject * tpp_gettemplateargs(TemplateProxy *self, void *)
bool TemplateProxy_Check(T *object)
std::shared_ptr< TemplateInfo > TP_TInfo_t
intptr_t TCppMethod_t
Definition cpp_cppyy.h:38
RPY_EXPORTED TCppMethod_t GetMethodTemplate(TCppScope_t scope, const std::string &name, const std::string &proto)
RPY_EXPORTED bool IsConstructor(TCppMethod_t method)
RPY_EXPORTED bool IsNamespace(TCppScope_t scope)
RPY_EXPORTED bool IsStaticMethod(TCppMethod_t method)
RPY_EXPORTED bool IsStaticTemplate(TCppScope_t scope, const std::string &name)
size_t TCppScope_t
Definition cpp_cppyy.h:34
RPY_EXPORTED std::string GetMethodFullName(TCppMethod_t)