Logo ROOT  
Reference Guide
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//- helper for ctypes conversions --------------------------------------------
20static PyObject* TC2CppName(PyObject* pytc, const char* cpd, bool allow_voidp)
21{
22 const char* name = nullptr;
23 if (CPyCppyy_PyText_Check(pytc)) {
24 char tc = ((char*)CPyCppyy_PyText_AsString(pytc))[0];
25 switch (tc) {
26 case '?': name = "bool"; break;
27 case 'c': name = "char"; break;
28 case 'b': name = "char"; break;
29 case 'B': name = "unsigned char"; break;
30 case 'h': name = "short"; break;
31 case 'H': name = "unsigned short"; break;
32 case 'i': name = "int"; break;
33 case 'I': name = "unsigned int"; break;
34 case 'l': name = "long"; break;
35 case 'L': name = "unsigned long"; break;
36 case 'q': name = "long long"; break;
37 case 'Q': name = "unsigned long long"; break;
38 case 'f': name = "float"; break;
39 case 'd': name = "double"; break;
40 case 'g': name = "long double"; break;
41 default: name = (allow_voidp ? "void*" : nullptr); break;
42 }
43 }
44
45 if (name)
46 return CPyCppyy_PyText_FromString((std::string{name}+cpd).c_str());
47 return nullptr;
48}
49
50//----------------------------------------------------------------------------
51TemplateInfo::TemplateInfo() : fCppName(nullptr), fPyName(nullptr), fPyClass(nullptr),
52 fNonTemplated(nullptr), fTemplated(nullptr), fLowPriority(nullptr)
53{
54 /* empty */
55}
56
57//----------------------------------------------------------------------------
59{
60 Py_XDECREF(fCppName);
61 Py_XDECREF(fPyName);
62 Py_XDECREF(fPyClass);
63
64 Py_DECREF(fNonTemplated);
65 Py_DECREF(fTemplated);
66 Py_DECREF(fLowPriority);
67
68 for (const auto& p : fDispatchMap) {
69 for (const auto& c : p.second) {
70 Py_DECREF(c.second);
71 }
72 }
73}
74
75//----------------------------------------------------------------------------
76void TemplateProxy::Set(const std::string& cppname, const std::string& pyname, PyObject* pyclass)
77{
78// Initialize the proxy for the given 'pyclass.'
79 fSelf = nullptr;
80 fTemplateArgs = nullptr;
81
82 fTI->fCppName = CPyCppyy_PyText_FromString(const_cast<char*>(cppname.c_str()));
83 fTI->fPyName = CPyCppyy_PyText_FromString(const_cast<char*>(pyname.c_str()));
84 Py_XINCREF(pyclass);
85 fTI->fPyClass = pyclass;
86
87 std::vector<PyCallable*> dummy;
88 fTI->fNonTemplated = CPPOverload_New(pyname, dummy);
89 fTI->fTemplated = CPPOverload_New(pyname, dummy);
90 fTI->fLowPriority = CPPOverload_New(pyname, dummy);
91}
92
93//----------------------------------------------------------------------------
95// Store overloads of this templated method.
96 bool isGreedy = false;
97 for (auto pc : mp->fMethodInfo->fMethods) {
98 if (pc->IsGreedy()) {
99 isGreedy = true;
100 break;
101 }
102 }
103
104 CPPOverload* cppol = isGreedy ? fTI->fLowPriority : fTI->fNonTemplated;
105 cppol->MergeOverload(mp);
106}
107
109// Store overload of this templated method.
110 CPPOverload* cppol = pc->IsGreedy() ? fTI->fLowPriority : fTI->fNonTemplated;
111 cppol->AdoptMethod(pc);
112}
113
115{
116// Store known template methods.
117 fTI->fTemplated->AdoptMethod(pc);
118}
119
120//----------------------------------------------------------------------------
121PyObject* TemplateProxy::Instantiate(const std::string& fname,
122 PyObject* args, Utility::ArgPreference pref, int* pcnt)
123{
124// Instantiate (and cache) templated methods, return method if any
125 std::string proto = "";
126
127 Py_ssize_t nArgs = PyTuple_GET_SIZE(args);
128 if (nArgs != 0) {
129 PyObject* tpArgs = PyTuple_New(nArgs);
130 for (int i = 0; i < nArgs; ++i) {
131 PyObject* itemi = PyTuple_GET_ITEM(args, i);
132
133 bool bArgSet = false;
134
135 // special case for arrays
136 PyObject* pytc = PyObject_GetAttr(itemi, PyStrings::gTypeCode);
137 if (pytc) {
138 PyObject* pyptrname = TC2CppName(pytc, "*", true);
139 if (pyptrname) {
140 PyTuple_SET_ITEM(tpArgs, i, pyptrname);
141 bArgSet = true;
142 // string added, but not counted towards nStrings
143 }
144 Py_DECREF(pytc); pytc = nullptr;
145 } else
146 PyErr_Clear();
147
148 // if not arg set, try special case for ctypes
149 if (!bArgSet) pytc = PyObject_GetAttr(itemi, PyStrings::gCTypesType);
150
151 if (!bArgSet && pytc) {
152 PyObject* pyactname = TC2CppName(pytc, "&", false);
153 if (!pyactname) {
154 // _type_ of a pointer to c_type is that type, which will have a type
155 PyObject* newpytc = PyObject_GetAttr(pytc, PyStrings::gCTypesType);
156 Py_DECREF(pytc);
157 pytc = newpytc;
158 if (pytc) {
159 pyactname = TC2CppName(pytc, "*", false);
160 } else
161 PyErr_Clear();
162 }
163 Py_XDECREF(pytc); pytc = nullptr;
164 if (pyactname) {
165 PyTuple_SET_ITEM(tpArgs, i, pyactname);
166 bArgSet = true;
167 // string added, but not counted towards nStrings
168 }
169 } else
170 PyErr_Clear();
171
172 if (!bArgSet) {
173 // normal case (may well fail)
174 PyErr_Clear();
175 PyObject* tp = (PyObject*)Py_TYPE(itemi);
176 Py_INCREF(tp);
177 PyTuple_SET_ITEM(tpArgs, i, tp);
178 }
179 }
180
181 const std::string& name_v1 = Utility::ConstructTemplateArgs(nullptr, tpArgs, args, pref, 0, pcnt);
182 Py_DECREF(tpArgs);
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;
189 Cppyy::TCppMethod_t cppmeth = Cppyy::GetMethodTemplate(scope, fname, proto);
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
202 std::string resname = Cppyy::GetMethodFullName(cppmeth);
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;
218 resname = Cppyy::GetMethodFullName(cppmeth);
219 }
220 }
221
222 bool bExactMatch = fname == resname;
223
224 // lookup on existing name in case this was an overload, not a caching, failure
225 PyObject* dct = PyObject_GetAttr(fTI->fPyClass, PyStrings::gDict);
226 PyObject* pycachename = CPyCppyy_PyText_InternFromString(fname.c_str());
227 PyObject* pyol = PyObject_GetItem(dct, pycachename);
228 if (!pyol) PyErr_Clear();
229 bool bIsCppOL = CPPOverload_Check(pyol);
230
231 if (pyol && !bIsCppOL && !TemplateProxy_Check(pyol)) {
232 // unknown object ... leave well alone
233 Py_DECREF(pyol);
234 Py_DECREF(pycachename);
235 Py_DECREF(dct);
236 return nullptr;
237 }
238
239 // find the full name if the requested one was partial
240 PyObject* exact = nullptr;
241 PyObject* pyresname = CPyCppyy_PyText_FromString(resname.c_str());
242 if (!bExactMatch) {
243 exact = PyObject_GetItem(dct, pyresname);
244 if (!exact) PyErr_Clear();
245 }
246 Py_DECREF(dct);
247
248 bool bIsConstructor = false, bNeedsRebind = true;
249
250 PyCallable* meth = nullptr;
251 if (Cppyy::IsNamespace(scope)) {
252 meth = new CPPFunction(scope, cppmeth);
253 bNeedsRebind = false;
254 } else if (Cppyy::IsStaticMethod(cppmeth)) {
255 meth = new CPPClassMethod(scope, cppmeth);
256 bNeedsRebind = false;
257 } else if (Cppyy::IsConstructor(cppmeth)) {
258 bIsConstructor = true;
259 meth = new CPPConstructor(scope, cppmeth);
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)
266 pyol = (PyObject*)CPPOverload_New(fname, meth);
267 if (bIsConstructor) {
268 // TODO: this is an ugly hack :(
269 ((CPPOverload*)pyol)->fMethodInfo->fFlags |= \
270 CallContext::kIsCreator | CallContext::kIsConstructor;
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());
289 Py_DECREF(pyol);
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
299 Py_DECREF(pyresname);
300 Py_DECREF(pycachename);
301
302 // retrieve fresh (for boundedness) and call
303 PyObject* pymeth =
304 CPPOverload_Type.tp_descr_get(pyol, bNeedsRebind ? fSelf : nullptr, (PyObject*)&CPPOverload_Type);
305 Py_DECREF(pyol);
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 =========================
315static TemplateProxy* tpp_new(PyTypeObject*, PyObject*, PyObject*)
316{
317// Create a new empty template method proxy.
318 TemplateProxy* pytmpl = PyObject_GC_New(TemplateProxy, &TemplateProxy_Type);
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
325 PyObject_GC_Track(pytmpl);
326 return pytmpl;
327}
328
329//----------------------------------------------------------------------------
330static int tpp_clear(TemplateProxy* pytmpl)
331{
332// Garbage collector clear of held python member objects.
333 Py_CLEAR(pytmpl->fSelf);
334 Py_CLEAR(pytmpl->fTemplateArgs);
335
336 return 0;
337}
338
339//----------------------------------------------------------------------------
340static void tpp_dealloc(TemplateProxy* pytmpl)
341{
342// Destroy the given template method proxy.
343 if (pytmpl->fWeakrefList)
344 PyObject_ClearWeakRefs((PyObject*)pytmpl);
345 PyObject_GC_UnTrack(pytmpl);
346 tpp_clear(pytmpl);
347 pytmpl->fTI.~TP_TInfo_t();
348 PyObject_GC_Del(pytmpl);
349}
350
351//----------------------------------------------------------------------------
352static int tpp_traverse(TemplateProxy* pytmpl, visitproc visit, void* arg)
353{
354// Garbage collector traverse of held python member objects.
355 Py_VISIT(pytmpl->fSelf);
356 Py_VISIT(pytmpl->fTemplateArgs);
357
358 return 0;
359}
360
361//----------------------------------------------------------------------------
362static PyObject* tpp_doc(TemplateProxy* pytmpl, void*)
363{
364// Forward to method proxies to doc all overloads
365 PyObject* doc = nullptr;
366 if (pytmpl->fTI->fNonTemplated->HasMethods())
367 doc = PyObject_GetAttrString((PyObject*)pytmpl->fTI->fNonTemplated, "__doc__");
368 if (pytmpl->fTI->fTemplated->HasMethods()) {
369 PyObject* doc2 = PyObject_GetAttrString((PyObject*)pytmpl->fTI->fTemplated, "__doc__");
370 if (doc && doc2) {
373 } else if (!doc && doc2) {
374 doc = doc2;
375 }
376 }
377 if (pytmpl->fTI->fLowPriority->HasMethods()) {
378 PyObject* doc2 = PyObject_GetAttrString((PyObject*)pytmpl->fTI->fLowPriority, "__doc__");
379 if (doc && doc2) {
382 } else if (!doc && doc2) {
383 doc = doc2;
384 }
385 }
386
387 if (doc)
388 return doc;
389
391}
392
393//----------------------------------------------------------------------------
395{
396// Simply return the doc string as that's the most useful info (this will appear
397// on clsses on calling help()).
398 return tpp_doc(pytmpl, nullptr);
399}
400
401
402//= CPyCppyy template proxy callable behavior ================================
403static inline std::string targs2str(TemplateProxy* pytmpl)
404{
405 if (!pytmpl || !pytmpl->fTemplateArgs) return "";
407}
408
409static inline void UpdateDispatchMap(TemplateProxy* pytmpl, bool use_targs, uint64_t sighash, CPPOverload* pymeth)
410{
411// memoize a method in the dispatch map after successful call; replace old if need be (may be
412// with the same CPPOverload, just with more methods)
413 bool bInserted = false;
414 auto& v = pytmpl->fTI->fDispatchMap[use_targs ? targs2str(pytmpl) : ""];
415
416 Py_INCREF(pymeth);
417 for (auto& p : v) {
418 if (p.first == sighash) {
419 Py_DECREF(p.second);
420 p.second = pymeth;
421 bInserted = true;
422 }
423 }
424 if (!bInserted) v.push_back(std::make_pair(sighash, pymeth));
425}
426
427static inline PyObject* CallMethodImp(TemplateProxy* pytmpl, PyObject*& pymeth,
428 PyObject* args, PyObject* kwds, bool impOK, uint64_t sighash)
429{
430// Actual call of a given overload: takes care of handlign of "self" and
431// dereferences the overloaded method after use.
432 PyObject* result;
433 if (!impOK) PyDict_SetItem(kwds, PyStrings::gNoImplicit, Py_True);
434 bool isNS = (((CPPScope*)pytmpl->fTI->fPyClass)->fFlags & CPPScope::kIsNamespace);
435 if (isNS && pytmpl->fSelf) {
436 // this is a global method added a posteriori to the class
437 Py_ssize_t sz = PyTuple_GET_SIZE(args);
438 PyObject* newArgs = PyTuple_New(sz+1);
439 for (int i = 0; i < sz; ++i) {
440 PyObject* item = PyTuple_GET_ITEM(args, i);
441 Py_INCREF(item);
442 PyTuple_SET_ITEM(newArgs, i+1, item);
443 }
444 Py_INCREF((PyObject*)pytmpl->fSelf);
445 PyTuple_SET_ITEM(newArgs, 0, (PyObject*)pytmpl->fSelf);
446 result = CPPOverload_Type.tp_call(pymeth, newArgs, kwds);
447 Py_DECREF(newArgs);
448 } else
449 result = CPPOverload_Type.tp_call(pymeth, args, kwds);
450
451 if (result) {
452 Py_XDECREF(((CPPOverload*)pymeth)->fSelf); ((CPPOverload*)pymeth)->fSelf = nullptr; // unbind
453 UpdateDispatchMap(pytmpl, true, sighash, (CPPOverload*)pymeth);
454 }
455
456 Py_DECREF(pymeth); pymeth = nullptr;
457 return result;
458}
459
460#define TPPCALL_RETURN \
461{ if (!errors.empty()) \
462 std::for_each(errors.begin(), errors.end(), Utility::PyError_t::Clear);\
463 Py_DECREF(kwds); \
464 return result; }
465
466static PyObject* tpp_call(TemplateProxy* pytmpl, PyObject* args, PyObject* kwds)
467{
468// Dispatcher to the actual member method, several uses possible; in order:
469//
470// case 1: explicit template previously selected through subscript
471//
472// case 2: select known non-template overload
473//
474// obj.method(a0, a1, ...)
475// => obj->method(a0, a1, ...) // non-template
476//
477// case 3: select known template overload
478//
479// obj.method(a0, a1, ...)
480// => obj->method(a0, a1, ...) // all known templates
481//
482// case 4: auto-instantiation from types of arguments
483//
484// obj.method(a0, a1, ...)
485// => obj->method<type(a0), type(a1), ...>(a0, a1, ...)
486//
487// Note: explicit instantiation needs to use [] syntax:
488//
489// obj.method[type<a0>, type<a1>, ...](a0, a1, ...)
490//
491// case 5: low priority methods, such as ones that take void* arguments
492//
493
494// TODO: should previously instantiated templates be considered first?
495
496// container for collecting errors
497 std::vector<Utility::PyError_t> errors;
498
499 PyObject* pymeth = nullptr, *result = nullptr;
500
501// short-cut through memoization map
502 uint64_t sighash = HashSignature(args);
503
504 if (!pytmpl->fTemplateArgs) {
505 // look for known signatures ...
506 CPPOverload* ol = nullptr;
507 auto& v = pytmpl->fTI->fDispatchMap[targs2str(pytmpl)];
508 for (const auto& p : v) {
509 if (p.first == sighash) {
510 ol = p.second;
511 break;
512 }
513 }
514
515 if (ol != nullptr) {
516 if (!pytmpl->fSelf) {
517 result = CPPOverload_Type.tp_call((PyObject*)ol, args, kwds);
518 } else {
519 pymeth = CPPOverload_Type.tp_descr_get(
520 (PyObject*)ol, pytmpl->fSelf, (PyObject*)&CPPOverload_Type);
521 result = CPPOverload_Type.tp_call(pymeth, args, kwds);
522 Py_DECREF(pymeth); pymeth = nullptr;
523 }
524 if (result)
525 return result;
526 Utility::FetchError(errors);
527 }
528 }
529
530// do not mix template instantiations with implicit conversions
531 if (!kwds) kwds = PyDict_New();
532 else {
533 Py_INCREF(kwds);
534 }
535
536// case 1: explicit template previously selected through subscript
537 if (pytmpl->fTemplateArgs) {
538 // instantiate explicitly
540 CPyCppyy_PyText_AsString(pytmpl->fTI->fCppName));
541 CPyCppyy_PyText_Append(&pyfullname, pytmpl->fTemplateArgs);
542
543 // first, lookup by full name, if previously stored
544 bool isNS = (((CPPScope*)pytmpl->fTI->fPyClass)->fFlags & CPPScope::kIsNamespace);
545 pymeth = PyObject_GetAttr((pytmpl->fSelf && !isNS) ? pytmpl->fSelf : pytmpl->fTI->fPyClass, pyfullname);
546
547 // attempt call if found (this may fail if there are specializations)
548 if (CPPOverload_Check(pymeth)) {
549 // since the template args are fully explicit, allow implicit conversion of arguments
550 result = CallMethodImp(pytmpl, pymeth, args, kwds, true, sighash);
551 if (result) {
552 Py_DECREF(pyfullname);
554 }
555 Utility::FetchError(errors);
556 } else if (pymeth && PyCallable_Check(pymeth)) {
557 // something different (user provided?)
558 result = PyObject_CallObject(pymeth, args);
559 Py_DECREF(pymeth);
560 if (result) {
561 Py_DECREF(pyfullname);
563 }
564 Utility::FetchError(errors);
565 } else if (!pymeth)
566 PyErr_Clear();
567
568 // not cached or failed call; try instantiation
569 pymeth = pytmpl->Instantiate(
570 CPyCppyy_PyText_AsString(pyfullname), args, Utility::kNone);
571 if (pymeth) {
572 // attempt actuall call; same as above, allow implicit conversion of arguments
573 result = CallMethodImp(pytmpl, pymeth, args, kwds, true, sighash);
574 if (result) {
575 Py_DECREF(pyfullname);
577 }
578 }
579
580 // no drop through if failed (if implicit was desired, don't provide template args)
581 Utility::FetchError(errors);
582 PyObject* topmsg = CPyCppyy_PyText_FromFormat("Could not instantiate %s:", CPyCppyy_PyText_AsString(pyfullname));
583 Py_DECREF(pyfullname);
584 Utility::SetDetailedException(errors, topmsg /* steals */, PyExc_TypeError /* default error */);
585
586 Py_DECREF(kwds);
587 return nullptr;
588 }
589
590// case 2: select known non-template overload
591 if (pytmpl->fTI->fNonTemplated->HasMethods()) {
592 // simply forward the call: all non-templated methods are defined on class definition
593 // and thus already available
594 pymeth = CPPOverload_Type.tp_descr_get(
595 (PyObject*)pytmpl->fTI->fNonTemplated, pytmpl->fSelf, (PyObject*)&CPPOverload_Type);
596 // now call the method with the arguments (loops internally and implicit is okay as
597 // these are not templated methods that should match exactly)
598 result = CPPOverload_Type.tp_call(pymeth, args, kwds);
599 Py_DECREF(pymeth); pymeth = nullptr;
600 if (result) {
601 UpdateDispatchMap(pytmpl, false, sighash, pytmpl->fTI->fNonTemplated);
603 }
604 Utility::FetchError(errors);
605 }
606
607// case 3: select known template overload
608 if (pytmpl->fTI->fTemplated->HasMethods()) {
609 // simply forward the call
610 pymeth = CPPOverload_Type.tp_descr_get(
611 (PyObject*)pytmpl->fTI->fTemplated, pytmpl->fSelf, (PyObject*)&CPPOverload_Type);
612 // now call the method with the arguments (loops internally)
613 PyDict_SetItem(kwds, PyStrings::gNoImplicit, Py_True);
614 result = CPPOverload_Type.tp_call(pymeth, args, kwds);
615 Py_DECREF(pymeth); pymeth = nullptr;
616 if (result) {
617 UpdateDispatchMap(pytmpl, true, sighash, pytmpl->fTI->fTemplated);
619 }
620 Utility::FetchError(errors);
621 }
622
623// case 4: auto-instantiation from types of arguments
625 // TODO: no need to loop if there are no non-instance arguments; also, should any
626 // failed lookup be removed?
627 int pcnt = 0;
628 pymeth = pytmpl->Instantiate(
629 CPyCppyy_PyText_AsString(pytmpl->fTI->fCppName), args, pref, &pcnt);
630 if (pymeth) {
631 // attempt actuall call; argument based, so do not allow implicit conversions
632 result = CallMethodImp(pytmpl, pymeth, args, kwds, false, sighash);
633 if (result) TPPCALL_RETURN;
634 }
635 Utility::FetchError(errors);
636 if (!pcnt) break; // preference never used; no point trying others
637 }
638
639// case 5: low priority methods, such as ones that take void* arguments
640 if (pytmpl->fTI->fLowPriority->HasMethods()) {
641 // simply forward the call
642 pymeth = CPPOverload_Type.tp_descr_get(
643 (PyObject*)pytmpl->fTI->fLowPriority, pytmpl->fSelf, (PyObject*)&CPPOverload_Type);
644 // now call the method with the arguments (loops internally)
645 PyDict_SetItem(kwds, PyStrings::gNoImplicit, Py_True);
646 result = CPPOverload_Type.tp_call(pymeth, args, kwds);
647 Py_DECREF(pymeth); pymeth = nullptr;
648 if (result) {
649 UpdateDispatchMap(pytmpl, false, sighash, pytmpl->fTI->fLowPriority);
651 }
652 Utility::FetchError(errors);
653 }
654
655// error reporting is fraud, given the numerous steps taken, but more details seems better
656 if (!errors.empty()) {
657 PyObject* topmsg = CPyCppyy_PyText_FromString("Template method resolution failed:");
658 Utility::SetDetailedException(errors, topmsg /* steals */, PyExc_TypeError /* default error */);
659 } else {
660 PyErr_Format(PyExc_TypeError, "cannot resolve method template call for \'%s\'",
661 CPyCppyy_PyText_AsString(pytmpl->fTI->fPyName));
662 }
663
664 Py_DECREF(kwds);
665 return nullptr;
666}
667
668//----------------------------------------------------------------------------
670{
671// create and use a new template proxy (language requirement)
673
674// new method is to be bound to current object (may be nullptr)
675 Py_XINCREF(pyobj);
676 newPyTmpl->fSelf = pyobj;
677
678 Py_XINCREF(pytmpl->fTemplateArgs);
679 newPyTmpl->fTemplateArgs = pytmpl->fTemplateArgs;
680
681// copy name, class, etc. pointers
682 new (&newPyTmpl->fTI) std::shared_ptr<TemplateInfo>{pytmpl->fTI};
683
684 return newPyTmpl;
685}
686
687//----------------------------------------------------------------------------
689{
690// Explicit template member lookup/instantiation; works by re-bounding. This method can
691// not cache overloads as instantiations need not be unique for the argument types due
692// to template specializations.
693 TemplateProxy* typeBoundMethod = tpp_descrget(pytmpl, pytmpl->fSelf, nullptr);
694 Py_XDECREF(typeBoundMethod->fTemplateArgs);
695 typeBoundMethod->fTemplateArgs = CPyCppyy_PyText_FromString(
696 Utility::ConstructTemplateArgs(nullptr, args).c_str());
697 return (PyObject*)typeBoundMethod;
698}
699
700//-----------------------------------------------------------------------------
702{
703 return PyInt_FromLong(0); // dummy (__useffi__ unused)
704}
705
706//-----------------------------------------------------------------------------
707static int tpp_setuseffi(CPPOverload*, PyObject*, void*)
708{
709 return 0; // dummy (__useffi__ unused)
710}
711
712
713//----------------------------------------------------------------------------
714static PyMappingMethods tpp_as_mapping = {
715 nullptr, (binaryfunc)tpp_subscript, nullptr
716};
717
718static PyGetSetDef tpp_getset[] = {
719 {(char*)"__doc__", (getter)tpp_doc, nullptr, nullptr, nullptr},
720 {(char*)"__useffi__", (getter)tpp_getuseffi, (setter)tpp_setuseffi,
721 (char*)"unused", nullptr},
722 {(char*)nullptr, nullptr, nullptr, nullptr, nullptr}
723};
724
725
726//= CPyCppyy template proxy type =============================================
727PyTypeObject TemplateProxy_Type = {
728 PyVarObject_HEAD_INIT(&PyType_Type, 0)
729 (char*)"cppyy.TemplateProxy", // tp_name
730 sizeof(TemplateProxy), // tp_basicsize
731 0, // tp_itemsize
732 (destructor)tpp_dealloc, // tp_dealloc
733 0, // tp_print
734 0, // tp_getattr
735 0, // tp_setattr
736 0, // tp_compare
737 (reprfunc)tpp_repr, // tp_repr
738 0, // tp_as_number
739 0, // tp_as_sequence
740 &tpp_as_mapping, // tp_as_mapping
741 0, // tp_hash
742 (ternaryfunc)tpp_call, // tp_call
743 0, // tp_str
744 0, // tp_getattro
745 0, // tp_setattro
746 0, // tp_as_buffer
747 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, // tp_flags
748 (char*)"cppyy template proxy (internal)", // tp_doc
749 (traverseproc)tpp_traverse,// tp_traverse
750 (inquiry)tpp_clear, // tp_clear
751 0, // tp_richcompare
752 offsetof(TemplateProxy, fWeakrefList), // tp_weaklistoffset
753 0, // tp_iter
754 0, // tp_iternext
755 0, // tp_methods
756 0, // tp_members
757 tpp_getset, // tp_getset
758 0, // tp_base
759 0, // tp_dict
760 (descrgetfunc)tpp_descrget,// tp_descr_get
761 0, // tp_descr_set
762 0, // tp_dictoffset
763 0, // tp_init
764 0, // tp_alloc
765 (newfunc)tpp_new, // tp_new
766 0, // tp_free
767 0, // tp_is_gc
768 0, // tp_bases
769 0, // tp_mro
770 0, // tp_cache
771 0, // tp_subclasses
772 0 // tp_weaklist
773#if PY_VERSION_HEX >= 0x02030000
774 , 0 // tp_del
775#endif
776#if PY_VERSION_HEX >= 0x02060000
777 , 0 // tp_version_tag
778#endif
779#if PY_VERSION_HEX >= 0x03040000
780 , 0 // tp_finalize
781#endif
782};
783
784} // namespace CPyCppyy
#define Py_TYPE(ob)
Definition: CPyCppyy.h:217
#define CPyCppyy_PyText_InternFromString
Definition: CPyCppyy.h:103
int Py_ssize_t
Definition: CPyCppyy.h:236
#define CPyCppyy_PyText_Append
Definition: CPyCppyy.h:104
#define CPyCppyy_PyText_AsString
Definition: CPyCppyy.h:97
#define CPyCppyy_PyText_AppendAndDel
Definition: CPyCppyy.h:105
#define CPyCppyy_PyText_FromFormat
Definition: CPyCppyy.h:101
#define CPyCppyy_PyText_FromString
Definition: CPyCppyy.h:102
#define CPyCppyy_PyText_Check
Definition: CPyCppyy.h:95
#define PyVarObject_HEAD_INIT(type, size)
Definition: CPyCppyy.h:215
PyInt_FromLong
Definition: Converters.cxx:876
_object PyObject
Definition: PyMethodBase.h:42
#define c(i)
Definition: RSha256.hxx:101
char name[80]
Definition: TGX11.cxx:110
#define pyname
Definition: TMCParticle.cxx:19
#define TPPCALL_RETURN
const char * proto
Definition: civetweb.c:16604
void MergeOverload(CPPOverload *meth)
void AdoptMethod(PyCallable *pc)
MethodInfo_t * fMethodInfo
Definition: CPPOverload.h:68
virtual PyCallable * Clone()=0
CPPOverload * fTemplated
Definition: TemplateProxy.h:39
CPPOverload * fLowPriority
Definition: TemplateProxy.h:40
TP_DispatchMap_t fDispatchMap
Definition: TemplateProxy.h:43
CPPOverload * fNonTemplated
Definition: TemplateProxy.h:38
PyObject * Instantiate(const std::string &fname, PyObject *tmplArgs, Utility::ArgPreference, int *pcnt=nullptr)
void Set(const std::string &cppname, const std::string &pyname, PyObject *pyclass)
PyObject_HEAD PyObject * fSelf
Definition: TemplateProxy.h:58
void AdoptTemplate(PyCallable *pc)
void AdoptMethod(PyCallable *pc)
void MergeOverload(CPPOverload *mp)
PyObject * gDict
Definition: PyStrings.cxx:14
PyObject * gCTypesType
Definition: PyStrings.cxx:29
PyObject * gTypeCode
Definition: PyStrings.cxx:28
PyObject * gNoImplicit
Definition: PyStrings.cxx:55
size_t FetchError(std::vector< PyError_t > &)
Definition: Utility.cxx:879
std::string ConstructTemplateArgs(PyObject *pyname, PyObject *tpArgs, PyObject *args=nullptr, ArgPreference=kNone, int argoff=0, int *pcnt=nullptr)
Definition: Utility.cxx:473
void SetDetailedException(std::vector< PyError_t > &errors, PyObject *topmsg, PyObject *defexc)
Definition: Utility.cxx:891
Set of helper functions that are invoked from the pythonizors, on the Python side.
CPPOverload * CPPOverload_New(const std::string &name, std::vector< PyCallable * > &methods)
Definition: CPPOverload.h:91
static PyObject * tpp_doc(TemplateProxy *pytmpl, void *)
static PyMappingMethods tpp_as_mapping
bool CPPOverload_Check(T *object)
Definition: CPPOverload.h:79
static int tpp_clear(TemplateProxy *pytmpl)
static PyObject * tpp_call(TemplateProxy *pytmpl, PyObject *args, PyObject *kwds)
static PyObject * tpp_repr(TemplateProxy *pytmpl)
static PyObject * tpp_getuseffi(CPPOverload *, void *)
static TemplateProxy * tpp_descrget(TemplateProxy *pytmpl, PyObject *pyobj, PyObject *)
static void tpp_dealloc(TemplateProxy *pytmpl)
static int tpp_traverse(TemplateProxy *pytmpl, visitproc visit, void *arg)
static int tpp_setuseffi(CPPOverload *, PyObject *, void *)
static PyGetSetDef tpp_getset[]
PyTypeObject CPPOverload_Type
PyTypeObject TemplateProxy_Type
uint64_t HashSignature(PyObject *args)
Definition: CPPOverload.h:16
static PyObject * tpp_subscript(TemplateProxy *pytmpl, PyObject *args)
static void UpdateDispatchMap(TemplateProxy *pytmpl, bool use_targs, uint64_t sighash, CPPOverload *pymeth)
static TemplateProxy * tpp_new(PyTypeObject *, PyObject *, PyObject *)
static std::string targs2str(TemplateProxy *pytmpl)
bool TemplateProxy_Check(T *object)
Definition: TemplateProxy.h:79
std::shared_ptr< TemplateInfo > TP_TInfo_t
Definition: TemplateProxy.h:48
static PyObject * CallMethodImp(TemplateProxy *pytmpl, PyObject *&pymeth, PyObject *args, PyObject *kwds, bool impOK, uint64_t sighash)
static PyObject * TC2CppName(PyObject *pytc, const char *cpd, bool allow_voidp)
intptr_t TCppMethod_t
Definition: cpp_cppyy.h:22
RPY_EXPORTED bool IsConstructor(TCppMethod_t method)
RPY_EXPORTED TCppMethod_t GetMethodTemplate(TCppScope_t scope, const std::string &name, const std::string &proto)
RPY_EXPORTED bool IsStaticMethod(TCppMethod_t method)
size_t TCppScope_t
Definition: cpp_cppyy.h:18
RPY_EXPORTED std::string GetMethodFullName(TCppMethod_t)
RPY_EXPORTED bool IsNamespace(TCppScope_t scope)
static constexpr double pc
static constexpr double m2
CPPOverload::Methods_t fMethods
Definition: CPPOverload.h:47