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
301
302 // retrieve fresh (for boundedness) and call
304 CPPOverload_Type.tp_descr_get(pyol, bNeedsRebind ? fSelf : nullptr, (PyObject*)&CPPOverload_Type);
306 return pymeth;
307 }
308
309 PyErr_Format(PyExc_TypeError, "Failed to instantiate \"%s(%s)\"", fname.c_str(), proto.c_str());
310 return nullptr;
311}
312
313
314//= CPyCppyy template proxy construction/destruction =========================
316{
317// Create a new empty template method proxy.
319 pytmpl->fSelf = nullptr;
320 pytmpl->fTemplateArgs = nullptr;
321 pytmpl->fWeakrefList = nullptr;
322 new (&pytmpl->fTI) TP_TInfo_t{};
323 pytmpl->fTI = std::make_shared<TemplateInfo>();
324
326 return pytmpl;
327}
328
329//----------------------------------------------------------------------------
331{
332 return (Py_hash_t)self;
333}
334
335//----------------------------------------------------------------------------
337{
338 if (op == Py_EQ || op == Py_NE) {
341
342 if (self->fTI == ((TemplateProxy*)other)->fTI)
344
346 }
347
349 return Py_NotImplemented;
350}
351
352//----------------------------------------------------------------------------
354{
355// Garbage collector clear of held python member objects.
356 Py_CLEAR(pytmpl->fSelf);
357 Py_CLEAR(pytmpl->fTemplateArgs);
358
359 return 0;
360}
361
362//----------------------------------------------------------------------------
364{
365// Destroy the given template method proxy.
366 if (pytmpl->fWeakrefList)
370 pytmpl->fTI.~TP_TInfo_t();
372}
373
374//----------------------------------------------------------------------------
376{
377// Garbage collector traverse of held python member objects.
378 Py_VISIT(pytmpl->fSelf);
379 Py_VISIT(pytmpl->fTemplateArgs);
380
381 return 0;
382}
383
384//----------------------------------------------------------------------------
386{
387 if (pytmpl->fTI->fDoc) {
388 Py_INCREF(pytmpl->fTI->fDoc);
389 return pytmpl->fTI->fDoc;
390 }
391
392// Forward to method proxies to doc all overloads
393 PyObject* doc = nullptr;
394 if (pytmpl->fTI->fNonTemplated->HasMethods())
395 doc = PyObject_GetAttrString((PyObject*)pytmpl->fTI->fNonTemplated, "__doc__");
396 if (pytmpl->fTI->fTemplated->HasMethods()) {
397 PyObject* doc2 = PyObject_GetAttrString((PyObject*)pytmpl->fTI->fTemplated, "__doc__");
398 if (doc && doc2) {
401 } else if (!doc && doc2) {
402 doc = doc2;
403 }
404 }
405 if (pytmpl->fTI->fLowPriority->HasMethods()) {
406 PyObject* doc2 = PyObject_GetAttrString((PyObject*)pytmpl->fTI->fLowPriority, "__doc__");
407 if (doc && doc2) {
410 } else if (!doc && doc2) {
411 doc = doc2;
412 }
413 }
414
415 if (doc)
416 return doc;
417
419}
420
421static int tpp_doc_set(TemplateProxy* pytmpl, PyObject *val, void *)
422{
423 Py_XDECREF(pytmpl->fTI->fDoc);
424 Py_INCREF(val);
425 pytmpl->fTI->fDoc = val;
426 return 0;
427}
428
429//----------------------------------------------------------------------------
430
431//= CPyCppyy template proxy callable behavior ================================
432
433#define TPPCALL_RETURN \
434{ errors.clear(); \
435 return result; }
436
437static inline std::string targs2str(TemplateProxy* pytmpl)
438{
439 if (!pytmpl || !pytmpl->fTemplateArgs) return "";
440 return CPyCppyy_PyText_AsString(pytmpl->fTemplateArgs);
441}
442
444{
445// Memoize a method in the dispatch map after successful call; replace old if need be (may be
446// with the same CPPOverload, just with more methods).
447 bool bInserted = false;
448 auto& v = pytmpl->fTI->fDispatchMap[use_targs ? targs2str(pytmpl) : ""];
449
451 for (auto& p : v) {
452 if (p.first == sighash) {
453 Py_DECREF(p.second);
454 p.second = pymeth;
455 bInserted = true;
456 }
457 }
458 if (!bInserted) v.push_back(std::make_pair(sighash, pymeth));
459}
460
462 CPyCppyy_PyArgs_t args, size_t nargsf, PyObject* kwds,
463 bool implicitOkay, bool use_targs, uint64_t sighash, std::vector<Utility::PyError_t>& errors)
464{
465// Forward a call to known overloads, if any.
466 if (pymeth->HasMethods()) {
467 PyObject* pycall = CPPOverload_Type.tp_descr_get(
469
470 if (!implicitOkay)
472
473 // now call the method with the arguments (loops internally)
476 if (result) {
479 }
481 }
482
483 return nullptr;
484}
485
487 CPyCppyy_PyArgs_t args, size_t nargsf, PyObject* kwds, bool impOK, uint64_t sighash)
488{
489// Actual call of a given overload: takes care of handlign of "self" and
490// dereferences the overloaded method after use.
491
495 bool isNS = (((CPPScope*)pytmpl->fTI->fPyClass)->fFlags & CPPScope::kIsNamespace);
496 if (isNS && pytmpl->fSelf && pytmpl->fSelf != Py_None) {
497 // this is a global method added a posteriori to the class
498 PyCallArgs cargs{(CPPInstance*&)pytmpl->fSelf, args, nargsf, kwds};
500 result = CPyCppyy_tp_call(pymeth, cargs.fArgs, cargs.fNArgsf, cargs.fKwds);
501 } else {
502 if (!pytmpl->fSelf && CPPOverload_Check(pymeth))
503 ((CPPOverload*)pymeth)->fFlags &= ~CallContext::kFromDescr;
505 }
506
507 if (result) {
508 Py_XDECREF(((CPPOverload*)pymeth)->fSelf); ((CPPOverload*)pymeth)->fSelf = nullptr; // unbind
510 }
511
512 Py_DECREF(pymeth); pymeth = nullptr;
513 return result;
514}
515
516#if PY_VERSION_HEX >= 0x03080000
518 TemplateProxy* pytmpl, PyObject* const *args, size_t nargsf, PyObject* kwds)
519#else
521#endif
522{
523// Dispatcher to the actual member method, several uses possible; in order:
524//
525// case 1: explicit template previously selected through subscript
526//
527// case 2: select known non-template overload
528//
529// obj.method(a0, a1, ...)
530// => obj->method(a0, a1, ...) // non-template
531//
532// case 3: select known template overload
533//
534// obj.method(a0, a1, ...)
535// => obj->method(a0, a1, ...) // all known templates
536//
537// case 4: auto-instantiation from types of arguments
538//
539// obj.method(a0, a1, ...)
540// => obj->method<type(a0), type(a1), ...>(a0, a1, ...)
541//
542// Note: explicit instantiation needs to use [] syntax:
543//
544// obj.method[type<a0>, type<a1>, ...](a0, a1, ...)
545//
546// case 5: low priority methods, such as ones that take void* arguments
547//
548
549// TODO: should previously instantiated templates be considered first?
550
551#if PY_VERSION_HEX < 0x03080000
552 size_t nargsf = PyTuple_GET_SIZE(args);
553#endif
554
555 PyObject *pymeth = nullptr, *result = nullptr;
556
557// short-cut through memoization map
559 uint64_t sighash = HashSignature(args, argc);
560
561 CPPOverload* ol = nullptr;
562 if (!pytmpl->fTemplateArgs) {
563 // look for known signatures ...
564 auto& v = pytmpl->fTI->fDispatchMap[""];
565 for (const auto& p : v) {
566 if (p.first == sighash) {
567 ol = p.second;
568 break;
569 }
570 }
571
572 if (ol != nullptr) {
573 if (!pytmpl->fSelf || pytmpl->fSelf == Py_None) {
575 } else {
576 pymeth = CPPOverload_Type.tp_descr_get(
579 Py_DECREF(pymeth); pymeth = nullptr;
580 }
581 if (result)
582 return result;
583 }
584 }
585
586// container for collecting errors
587 std::vector<Utility::PyError_t> errors;
589
590// case 1: explicit template previously selected through subscript
591 if (pytmpl->fTemplateArgs) {
592 // instantiate explicitly
593 PyObject* pyfullname = CPyCppyy_PyText_FromString(pytmpl->fTI->fCppName.c_str());
594 CPyCppyy_PyText_Append(&pyfullname, pytmpl->fTemplateArgs);
595
596 // first, lookup by full name, if previously stored
597 bool isNS = (((CPPScope*)pytmpl->fTI->fPyClass)->fFlags & CPPScope::kIsNamespace);
598 if (pytmpl->fSelf && pytmpl->fSelf != Py_None && !isNS)
600 else // by-passes custom scope getattr that searches into Cling
601 pymeth = PyType_Type.tp_getattro(pytmpl->fTI->fPyClass, pyfullname);
602
603 // attempt call if found (this may fail if there are specializations)
605 // since the template args are fully explicit, allow implicit conversion of arguments
607 if (result) {
610 }
612 } else if (pymeth && PyCallable_Check(pymeth)) {
613 // something different (user provided?)
616 if (result) {
619 }
621 } else if (!pymeth)
622 PyErr_Clear();
623
624 // not cached or failed call; try instantiation
625 pymeth = pytmpl->Instantiate(
627 if (pymeth) {
628 // attempt actual call; same as above, allow implicit conversion of arguments
630 if (result) {
633 }
634 }
635
636 // no drop through if failed (if implicit was desired, don't provide template args)
639 "Could not find \"%s\" (set cppyy.set_debug() for C++ errors):", CPyCppyy_PyText_AsString(pyfullname));
641 Utility::SetDetailedException(std::move(errors), topmsg /* steals */, PyExc_TypeError /* default error */);
642
643 return nullptr;
644 }
645
646// case 2: select known non-template overload
647 result = SelectAndForward(pytmpl, pytmpl->fTI->fNonTemplated, args, nargsf, kwds,
648 true /* implicitOkay */, false /* use_targs */, sighash, errors);
649 if (result)
651
652// case 3: select known template overload
653 result = SelectAndForward(pytmpl, pytmpl->fTI->fTemplated, args, nargsf, kwds,
654 false /* implicitOkay */, true /* use_targs */, sighash, errors);
655 if (result)
657
658// case 4: auto-instantiation from types of arguments
660 // TODO: no need to loop if there are no non-instance arguments; also, should any
661 // failed lookup be removed?
662 int pcnt = 0;
663 pymeth = pytmpl->Instantiate(pytmpl->fTI->fCppName, args, nargsf, pref, &pcnt);
664 if (pymeth) {
665 // attempt actual call; argument based, so do not allow implicit conversions
666 result = CallMethodImp(pytmpl, pymeth, args, nargsf, kwds, false, sighash);
668 }
670 if (!pcnt) break; // preference never used; no point trying others
671 }
672
673// case 5: low priority methods, such as ones that take void* arguments
674 result = SelectAndForward(pytmpl, pytmpl->fTI->fLowPriority, args, nargsf, kwds,
675 false /* implicitOkay */, false /* use_targs */, sighash, errors);
676 if (result)
678
679// error reporting is fraud, given the numerous steps taken, but more details seems better
680 if (!errors.empty()) {
681 PyObject* topmsg = CPyCppyy_PyText_FromString("Template method resolution failed:");
682 Utility::SetDetailedException(std::move(errors), topmsg /* steals */, PyExc_TypeError /* default error */);
683 } else {
684 PyErr_Format(PyExc_TypeError, "cannot resolve method template call for \'%s\'",
685 pytmpl->fTI->fCppName.c_str());
686 }
687
688 return nullptr;
689}
690
691//----------------------------------------------------------------------------
693{
694// create and use a new template proxy (language requirement)
696
697// new method is to be bound to current object (may be nullptr)
698 if (pyobj) {
700 newPyTmpl->fSelf = pyobj;
701 } else {
703 newPyTmpl->fSelf = Py_None;
704 }
705
706 Py_XINCREF(pytmpl->fTemplateArgs);
707 newPyTmpl->fTemplateArgs = pytmpl->fTemplateArgs;
708
709// copy name, class, etc. pointers
710 new (&newPyTmpl->fTI) std::shared_ptr<TemplateInfo>{pytmpl->fTI};
711
712#if PY_VERSION_HEX >= 0x03080000
713 newPyTmpl->fVectorCall = pytmpl->fVectorCall;
714#endif
715
716 return newPyTmpl;
717}
718
719
720//----------------------------------------------------------------------------
722{
723// Explicit template member lookup/instantiation; works by re-bounding. This method can
724// not cache overloads as instantiations need not be unique for the argument types due
725// to template specializations.
727 Py_XDECREF(typeBoundMethod->fTemplateArgs);
729 Utility::ConstructTemplateArgs(nullptr, args).c_str());
730// Propagate the error that occurs if we can't construct the C++ name
731// from the provided template argument
732 if (PyErr_Occurred()) {
733 return nullptr;
734 }
735 return (PyObject*)typeBoundMethod;
736}
737
738//-----------------------------------------------------------------------------
740{
741 return PyInt_FromLong(0); // dummy (__useffi__ unused)
742}
743
744//-----------------------------------------------------------------------------
745static int tpp_setuseffi(CPPOverload*, PyObject*, void*)
746{
747 return 0; // dummy (__useffi__ unused)
748}
749
750//-----------------------------------------------------------------------------
752 if (!self->fTemplateArgs) {
754 }
755
756 Py_INCREF(self->fTemplateArgs);
757 return self->fTemplateArgs;
758}
759
760//-----------------------------------------------------------------------------
762 PyErr_SetString(PyExc_AttributeError, "__template_args__ is read-only");
763 return -1;
764}
765
766//----------------------------------------------------------------------------
768 nullptr, (binaryfunc)tpp_subscript, nullptr
769};
770
772 {(char*)"__doc__", (getter)tpp_doc, (setter)tpp_doc_set, nullptr, nullptr},
773 {(char*)"__useffi__", (getter)tpp_getuseffi, (setter)tpp_setuseffi,
774 (char*)"unused", nullptr},
775 {(char*)"__template_args__", (getter)tpp_gettemplateargs, (setter)tpp_settemplateargs,
776 (char*)"the template arguments for this method", nullptr},
777 {(char*)nullptr, nullptr, nullptr, nullptr, nullptr},
778};
779
780
781//----------------------------------------------------------------------------
782void TemplateProxy::Set(const std::string& cppname, const std::string& pyname, PyObject* pyclass)
783{
784// Initialize the proxy for the given 'pyclass.'
785 fSelf = nullptr;
786 fTemplateArgs = nullptr;
787
788 fTI->fCppName = cppname;
790 fTI->fPyClass = pyclass;
791
792 std::vector<PyCallable*> dummy;
793 fTI->fNonTemplated = CPPOverload_New(pyname, dummy);
794 fTI->fTemplated = CPPOverload_New(pyname, dummy);
795 fTI->fLowPriority = CPPOverload_New(pyname, dummy);
796
797#if PY_VERSION_HEX >= 0x03080000
799#endif
800}
801
802
803//= CPyCppyy method proxy access to internals ================================
805{
806// Select and call a specific C++ overload, based on its signature.
807 const char* sigarg = nullptr;
808 const char* tmplarg = nullptr;
809 PyObject* sigarg_tuple = nullptr;
810 int want_const = -1;
811
814 std::string proto;
815
816 if (PyArg_ParseTuple(args, const_cast<char*>("s|i:__overload__"), &sigarg, &want_const)) {
817 want_const = PyTuple_GET_SIZE(args) == 1 ? -1 : want_const;
818
819 // check existing overloads in order
820 PyObject* ol = pytmpl->fTI->fNonTemplated->FindOverload(sigarg, want_const);
821 if (ol) return ol;
822 PyErr_Clear();
823 ol = pytmpl->fTI->fTemplated->FindOverload(sigarg, want_const);
824 if (ol) return ol;
825 PyErr_Clear();
826 ol = pytmpl->fTI->fLowPriority->FindOverload(sigarg, want_const);
827 if (ol) return ol;
828
829 proto = Utility::ConstructTemplateArgs(nullptr, args);
830 // Propagate the error that occurs if we can't construct the C++ name
831 // from the provided template argument
832 if (PyErr_Occurred()) {
833 return nullptr;
834 }
835
836 scope = ((CPPClass*)pytmpl->fTI->fPyClass)->fCppType;
838 scope, pytmpl->fTI->fCppName, proto.substr(1, proto.size()-2));
839 } else if (PyArg_ParseTuple(args, const_cast<char*>("ss:__overload__"), &sigarg, &tmplarg)) {
840 scope = ((CPPClass*)pytmpl->fTI->fPyClass)->fCppType;
841 std::string full_name = std::string(pytmpl->fTI->fCppName) + "<" + tmplarg + ">";
842
844 } else if (PyArg_ParseTuple(args, const_cast<char*>("O|i:__overload__"), &sigarg_tuple, &want_const)) {
845 PyErr_Clear();
846 want_const = PyTuple_GET_SIZE(args) == 1 ? -1 : want_const;
847
848 // check existing overloads in order
849 PyObject* ol = pytmpl->fTI->fNonTemplated->FindOverload(sigarg_tuple, want_const);
850 if (ol) return ol;
851 PyErr_Clear();
852 ol = pytmpl->fTI->fTemplated->FindOverload(sigarg_tuple, want_const);
853 if (ol) return ol;
854 PyErr_Clear();
855 ol = pytmpl->fTI->fLowPriority->FindOverload(sigarg_tuple, want_const);
856 if (ol) return ol;
857
858 proto.reserve(128);
859 proto.push_back('<');
861 for (int i = 0; i < n; i++) {
864 PyErr_Format(PyExc_LookupError, "argument types should be in string format");
865 return (PyObject*) nullptr;
866 }
868 if (i < n - 1)
869 proto.push_back(',');
870 }
871 proto.push_back('>');
872
873 scope = ((CPPClass*)pytmpl->fTI->fPyClass)->fCppType;
875 scope, pytmpl->fTI->fCppName, proto.substr(1, proto.size()-2));
876 } else {
877 PyErr_Format(PyExc_TypeError, "Unexpected arguments to __overload__");
878 return nullptr;
879 }
880
881// else attempt instantiation
882 if (!cppmeth) {
883 return nullptr;
884 }
885
886 PyErr_Clear();
887
888 // TODO: the next step should be consolidated with Instantiate()
889 PyCallable* meth = nullptr;
892 } else if (Cppyy::IsStaticMethod(cppmeth)) {
894 } else if (Cppyy::IsConstructor(cppmeth)) {
896 } else
897 meth = new CPPMethod(scope, cppmeth);
898
899 return (PyObject*)CPPOverload_New(pytmpl->fTI->fCppName+proto, meth);
900}
901
903 {(char*)"__overload__", (PyCFunction)tpp_overload, METH_VARARGS,
904 (char*)"select overload for dispatch" },
905 {(char*)nullptr, nullptr, 0, nullptr }
906};
907
908
909//= CPyCppyy template proxy type =============================================
912 (char*)"cppyy.TemplateProxy", // tp_name
913 sizeof(TemplateProxy), // tp_basicsize
914 0, // tp_itemsize
915 (destructor)tpp_dealloc, // tp_dealloc
916#if PY_VERSION_HEX >= 0x03080000
918#else
919 0, // tp_vectorcall_offset / tp_print
920#endif
921 0, // tp_getattr
922 0, // tp_setattr
923 0, // tp_as_async / tp_compare
924 0, // tp_repr
925 0, // tp_as_number
926 0, // tp_as_sequence
927 &tpp_as_mapping, // tp_as_mapping
928 (hashfunc)tpp_hash, // tp_hash
929#if PY_VERSION_HEX >= 0x03080000
930 (ternaryfunc)PyVectorcall_Call, // tp_call
931#else
932 (ternaryfunc)tpp_call, // tp_call
933#endif
934 0, // tp_str
935 0, // tp_getattro
936 0, // tp_setattro
937 0, // tp_as_buffer
939#if PY_VERSION_HEX >= 0x03080000
941#endif
942 , // tp_flags
943 (char*)"cppyy template proxy (internal)", // tp_doc
944 (traverseproc)tpp_traverse, // tp_traverse
945 (inquiry)tpp_clear, // tp_clear
946 (richcmpfunc)tpp_richcompare, // tp_richcompare
947 offsetof(TemplateProxy, fWeakrefList), // tp_weaklistoffset
948 0, // tp_iter
949 0, // tp_iternext
950 tpp_methods, // tp_methods
951 0, // tp_members
952 tpp_getset, // tp_getset
953 0, // tp_base
954 0, // tp_dict
955 (descrgetfunc)tpp_descr_get, // tp_descr_get
956 0, // tp_descr_set
957 0, // tp_dictoffset
958 0, // tp_init
959 0, // tp_alloc
960 (newfunc)tpp_new, // tp_new
961 0, // tp_free
962 0, // tp_is_gc
963 0, // tp_bases
964 0, // tp_mro
965 0, // tp_cache
966 0, // tp_subclasses
967 0 // tp_weaklist
968#if PY_VERSION_HEX >= 0x02030000
969 , 0 // tp_del
970#endif
971#if PY_VERSION_HEX >= 0x02060000
972 , 0 // tp_version_tag
973#endif
974#if PY_VERSION_HEX >= 0x03040000
975 , 0 // tp_finalize
976#endif
977#if PY_VERSION_HEX >= 0x03080000
978 , 0 // tp_vectorcall
979#endif
980#if PY_VERSION_HEX >= 0x030c0000
981 , 0 // tp_watched
982#endif
983#if PY_VERSION_HEX >= 0x030d0000
984 , 0 // tp_versions_used
985#endif
986};
987
988} // 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)