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//- 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 |= \
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//----------------------------------------------------------------------------
331{
332 return (Py_hash_t)self;
333}
334
335//----------------------------------------------------------------------------
336static PyObject* tpp_richcompare(TemplateProxy* self, PyObject* other, int op)
337{
338 if (op == Py_EQ || op == Py_NE) {
339 if (!TemplateProxy_CheckExact(other))
341
342 if (self->fTI == ((TemplateProxy*)other)->fTI)
344
346 }
347
348 Py_INCREF(Py_NotImplemented);
349 return Py_NotImplemented;
350}
351
352//----------------------------------------------------------------------------
353static int tpp_clear(TemplateProxy* pytmpl)
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//----------------------------------------------------------------------------
363static void tpp_dealloc(TemplateProxy* pytmpl)
364{
365// Destroy the given template method proxy.
366 if (pytmpl->fWeakrefList)
367 PyObject_ClearWeakRefs((PyObject*)pytmpl);
368 PyObject_GC_UnTrack(pytmpl);
369 tpp_clear(pytmpl);
370 pytmpl->fTI.~TP_TInfo_t();
371 PyObject_GC_Del(pytmpl);
372}
373
374//----------------------------------------------------------------------------
375static int tpp_traverse(TemplateProxy* pytmpl, visitproc visit, void* arg)
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//----------------------------------------------------------------------------
385static PyObject* tpp_doc(TemplateProxy* pytmpl, void*)
386{
387// Forward to method proxies to doc all overloads
388 PyObject* doc = nullptr;
389 if (pytmpl->fTI->fNonTemplated->HasMethods())
390 doc = PyObject_GetAttrString((PyObject*)pytmpl->fTI->fNonTemplated, "__doc__");
391 if (pytmpl->fTI->fTemplated->HasMethods()) {
392 PyObject* doc2 = PyObject_GetAttrString((PyObject*)pytmpl->fTI->fTemplated, "__doc__");
393 if (doc && doc2) {
396 } else if (!doc && doc2) {
397 doc = doc2;
398 }
399 }
400 if (pytmpl->fTI->fLowPriority->HasMethods()) {
401 PyObject* doc2 = PyObject_GetAttrString((PyObject*)pytmpl->fTI->fLowPriority, "__doc__");
402 if (doc && doc2) {
405 } else if (!doc && doc2) {
406 doc = doc2;
407 }
408 }
409
410 if (doc)
411 return doc;
412
414}
415
416//----------------------------------------------------------------------------
418{
419// Simply return the doc string as that's the most useful info (this will appear
420// on clsses on calling help()).
421 return tpp_doc(pytmpl, nullptr);
422}
423
424
425//= CPyCppyy template proxy callable behavior ================================
426static inline std::string targs2str(TemplateProxy* pytmpl)
427{
428 if (!pytmpl || !pytmpl->fTemplateArgs) return "";
430}
431
432static inline void UpdateDispatchMap(TemplateProxy* pytmpl, bool use_targs, uint64_t sighash, CPPOverload* pymeth)
433{
434// memoize a method in the dispatch map after successful call; replace old if need be (may be
435// with the same CPPOverload, just with more methods)
436 bool bInserted = false;
437 auto& v = pytmpl->fTI->fDispatchMap[use_targs ? targs2str(pytmpl) : ""];
438
439 Py_INCREF(pymeth);
440 for (auto& p : v) {
441 if (p.first == sighash) {
442 Py_DECREF(p.second);
443 p.second = pymeth;
444 bInserted = true;
445 }
446 }
447 if (!bInserted) v.push_back(std::make_pair(sighash, pymeth));
448}
449
450static inline PyObject* CallMethodImp(TemplateProxy* pytmpl, PyObject*& pymeth,
451 PyObject* args, PyObject* kwds, bool impOK, uint64_t sighash)
452{
453// Actual call of a given overload: takes care of handlign of "self" and
454// dereferences the overloaded method after use.
456 if (!impOK) PyDict_SetItem(kwds, PyStrings::gNoImplicit, Py_True);
457 bool isNS = (((CPPScope*)pytmpl->fTI->fPyClass)->fFlags & CPPScope::kIsNamespace);
458 if (isNS && pytmpl->fSelf) {
459 // this is a global method added a posteriori to the class
460 Py_ssize_t sz = PyTuple_GET_SIZE(args);
461 PyObject* newArgs = PyTuple_New(sz+1);
462 for (int i = 0; i < sz; ++i) {
463 PyObject* item = PyTuple_GET_ITEM(args, i);
464 Py_INCREF(item);
465 PyTuple_SET_ITEM(newArgs, i+1, item);
466 }
467 Py_INCREF((PyObject*)pytmpl->fSelf);
468 PyTuple_SET_ITEM(newArgs, 0, (PyObject*)pytmpl->fSelf);
469 result = CPPOverload_Type.tp_call(pymeth, newArgs, kwds);
470 Py_DECREF(newArgs);
471 } else
472 result = CPPOverload_Type.tp_call(pymeth, args, kwds);
473
474 if (result) {
475 Py_XDECREF(((CPPOverload*)pymeth)->fSelf); ((CPPOverload*)pymeth)->fSelf = nullptr; // unbind
476 UpdateDispatchMap(pytmpl, true, sighash, (CPPOverload*)pymeth);
477 }
478
479 Py_DECREF(pymeth); pymeth = nullptr;
480 return result;
481}
482
483#define TPPCALL_RETURN \
484{ if (!errors.empty()) \
485 std::for_each(errors.begin(), errors.end(), Utility::PyError_t::Clear);\
486 Py_DECREF(kwds); \
487 return result; }
488
489static PyObject* tpp_call(TemplateProxy* pytmpl, PyObject* args, PyObject* kwds)
490{
491// Dispatcher to the actual member method, several uses possible; in order:
492//
493// case 1: explicit template previously selected through subscript
494//
495// case 2: select known non-template overload
496//
497// obj.method(a0, a1, ...)
498// => obj->method(a0, a1, ...) // non-template
499//
500// case 3: select known template overload
501//
502// obj.method(a0, a1, ...)
503// => obj->method(a0, a1, ...) // all known templates
504//
505// case 4: auto-instantiation from types of arguments
506//
507// obj.method(a0, a1, ...)
508// => obj->method<type(a0), type(a1), ...>(a0, a1, ...)
509//
510// Note: explicit instantiation needs to use [] syntax:
511//
512// obj.method[type<a0>, type<a1>, ...](a0, a1, ...)
513//
514// case 5: low priority methods, such as ones that take void* arguments
515//
516
517// TODO: should previously instantiated templates be considered first?
518
519// container for collecting errors
520 std::vector<Utility::PyError_t> errors;
521
522 PyObject* pymeth = nullptr, *result = nullptr;
523
524// short-cut through memoization map
525 uint64_t sighash = HashSignature(args);
526
527 if (!pytmpl->fTemplateArgs) {
528 // look for known signatures ...
529 CPPOverload* ol = nullptr;
530 auto& v = pytmpl->fTI->fDispatchMap[targs2str(pytmpl)];
531 for (const auto& p : v) {
532 if (p.first == sighash) {
533 ol = p.second;
534 break;
535 }
536 }
537
538 if (ol != nullptr) {
539 if (!pytmpl->fSelf) {
540 result = CPPOverload_Type.tp_call((PyObject*)ol, args, kwds);
541 } else {
542 pymeth = CPPOverload_Type.tp_descr_get(
543 (PyObject*)ol, pytmpl->fSelf, (PyObject*)&CPPOverload_Type);
544 result = CPPOverload_Type.tp_call(pymeth, args, kwds);
545 Py_DECREF(pymeth); pymeth = nullptr;
546 }
547 if (result)
548 return result;
549 Utility::FetchError(errors);
550 }
551 }
552
553// do not mix template instantiations with implicit conversions
554 if (!kwds) kwds = PyDict_New();
555 else {
556 Py_INCREF(kwds);
557 }
558
559// case 1: explicit template previously selected through subscript
560 if (pytmpl->fTemplateArgs) {
561 // instantiate explicitly
563 CPyCppyy_PyText_AsString(pytmpl->fTI->fCppName));
564 CPyCppyy_PyText_Append(&pyfullname, pytmpl->fTemplateArgs);
565
566 // first, lookup by full name, if previously stored
567 bool isNS = (((CPPScope*)pytmpl->fTI->fPyClass)->fFlags & CPPScope::kIsNamespace);
568 pymeth = PyObject_GetAttr((pytmpl->fSelf && !isNS) ? pytmpl->fSelf : pytmpl->fTI->fPyClass, pyfullname);
569
570 // attempt call if found (this may fail if there are specializations)
571 if (CPPOverload_Check(pymeth)) {
572 // since the template args are fully explicit, allow implicit conversion of arguments
573 result = CallMethodImp(pytmpl, pymeth, args, kwds, true, sighash);
574 if (result) {
575 Py_DECREF(pyfullname);
577 }
578 Utility::FetchError(errors);
579 } else if (pymeth && PyCallable_Check(pymeth)) {
580 // something different (user provided?)
581 result = PyObject_CallObject(pymeth, args);
582 Py_DECREF(pymeth);
583 if (result) {
584 Py_DECREF(pyfullname);
586 }
587 Utility::FetchError(errors);
588 } else if (!pymeth)
589 PyErr_Clear();
590
591 // not cached or failed call; try instantiation
592 pymeth = pytmpl->Instantiate(
593 CPyCppyy_PyText_AsString(pyfullname), args, Utility::kNone);
594 if (pymeth) {
595 // attempt actuall call; same as above, allow implicit conversion of arguments
596 result = CallMethodImp(pytmpl, pymeth, args, kwds, true, sighash);
597 if (result) {
598 Py_DECREF(pyfullname);
600 }
601 }
602
603 // no drop through if failed (if implicit was desired, don't provide template args)
604 Utility::FetchError(errors);
605 PyObject* topmsg = CPyCppyy_PyText_FromFormat("Could not instantiate %s:", CPyCppyy_PyText_AsString(pyfullname));
606 Py_DECREF(pyfullname);
607 Utility::SetDetailedException(errors, topmsg /* steals */, PyExc_TypeError /* default error */);
608
609 Py_DECREF(kwds);
610 return nullptr;
611 }
612
613// case 2: select known non-template overload
614 if (pytmpl->fTI->fNonTemplated->HasMethods()) {
615 // simply forward the call: all non-templated methods are defined on class definition
616 // and thus already available
617 pymeth = CPPOverload_Type.tp_descr_get(
618 (PyObject*)pytmpl->fTI->fNonTemplated, pytmpl->fSelf, (PyObject*)&CPPOverload_Type);
619 // now call the method with the arguments (loops internally and implicit is okay as
620 // these are not templated methods that should match exactly)
621 result = CPPOverload_Type.tp_call(pymeth, args, kwds);
622 Py_DECREF(pymeth); pymeth = nullptr;
623 if (result) {
624 UpdateDispatchMap(pytmpl, false, sighash, pytmpl->fTI->fNonTemplated);
626 }
627 Utility::FetchError(errors);
628 }
629
630// case 3: select known template overload
631 if (pytmpl->fTI->fTemplated->HasMethods()) {
632 // simply forward the call
633 pymeth = CPPOverload_Type.tp_descr_get(
634 (PyObject*)pytmpl->fTI->fTemplated, pytmpl->fSelf, (PyObject*)&CPPOverload_Type);
635 // now call the method with the arguments (loops internally)
636 PyDict_SetItem(kwds, PyStrings::gNoImplicit, Py_True);
637 result = CPPOverload_Type.tp_call(pymeth, args, kwds);
638 Py_DECREF(pymeth); pymeth = nullptr;
639 if (result) {
640 UpdateDispatchMap(pytmpl, true, sighash, pytmpl->fTI->fTemplated);
642 }
643 Utility::FetchError(errors);
644 }
645
646// case 4: auto-instantiation from types of arguments
648 // TODO: no need to loop if there are no non-instance arguments; also, should any
649 // failed lookup be removed?
650 int pcnt = 0;
651 pymeth = pytmpl->Instantiate(
652 CPyCppyy_PyText_AsString(pytmpl->fTI->fCppName), args, pref, &pcnt);
653 if (pymeth) {
654 // attempt actuall call; argument based, so do not allow implicit conversions
655 result = CallMethodImp(pytmpl, pymeth, args, kwds, false, sighash);
657 }
658 Utility::FetchError(errors);
659 if (!pcnt) break; // preference never used; no point trying others
660 }
661
662// case 5: low priority methods, such as ones that take void* arguments
663 if (pytmpl->fTI->fLowPriority->HasMethods()) {
664 // simply forward the call
665 pymeth = CPPOverload_Type.tp_descr_get(
666 (PyObject*)pytmpl->fTI->fLowPriority, pytmpl->fSelf, (PyObject*)&CPPOverload_Type);
667 // now call the method with the arguments (loops internally)
668 PyDict_SetItem(kwds, PyStrings::gNoImplicit, Py_True);
669 result = CPPOverload_Type.tp_call(pymeth, args, kwds);
670 Py_DECREF(pymeth); pymeth = nullptr;
671 if (result) {
672 UpdateDispatchMap(pytmpl, false, sighash, pytmpl->fTI->fLowPriority);
674 }
675 Utility::FetchError(errors);
676 }
677
678// error reporting is fraud, given the numerous steps taken, but more details seems better
679 if (!errors.empty()) {
680 PyObject* topmsg = CPyCppyy_PyText_FromString("Template method resolution failed:");
681 Utility::SetDetailedException(errors, topmsg /* steals */, PyExc_TypeError /* default error */);
682 } else {
683 PyErr_Format(PyExc_TypeError, "cannot resolve method template call for \'%s\'",
684 CPyCppyy_PyText_AsString(pytmpl->fTI->fPyName));
685 }
686
687 Py_DECREF(kwds);
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 Py_XINCREF(pyobj);
699 newPyTmpl->fSelf = pyobj;
700
701 Py_XINCREF(pytmpl->fTemplateArgs);
702 newPyTmpl->fTemplateArgs = pytmpl->fTemplateArgs;
703
704// copy name, class, etc. pointers
705 new (&newPyTmpl->fTI) std::shared_ptr<TemplateInfo>{pytmpl->fTI};
706
707 return newPyTmpl;
708}
709
710//----------------------------------------------------------------------------
712{
713// Explicit template member lookup/instantiation; works by re-bounding. This method can
714// not cache overloads as instantiations need not be unique for the argument types due
715// to template specializations.
716 TemplateProxy* typeBoundMethod = tpp_descrget(pytmpl, pytmpl->fSelf, nullptr);
717 Py_XDECREF(typeBoundMethod->fTemplateArgs);
718 typeBoundMethod->fTemplateArgs = CPyCppyy_PyText_FromString(
719 Utility::ConstructTemplateArgs(nullptr, args).c_str());
720 return (PyObject*)typeBoundMethod;
721}
722
723//-----------------------------------------------------------------------------
725{
726 return PyInt_FromLong(0); // dummy (__useffi__ unused)
727}
728
729//-----------------------------------------------------------------------------
730static int tpp_setuseffi(CPPOverload*, PyObject*, void*)
731{
732 return 0; // dummy (__useffi__ unused)
733}
734
735
736//----------------------------------------------------------------------------
737static PyMappingMethods tpp_as_mapping = {
738 nullptr, (binaryfunc)tpp_subscript, nullptr
739};
740
741static PyGetSetDef tpp_getset[] = {
742 {(char*)"__doc__", (getter)tpp_doc, nullptr, nullptr, nullptr},
743 {(char*)"__useffi__", (getter)tpp_getuseffi, (setter)tpp_setuseffi,
744 (char*)"unused", nullptr},
745 {(char*)nullptr, nullptr, nullptr, nullptr, nullptr}
746};
747
748
749//= CPyCppyy method proxy access to internals ================================
751{
752// Select and call a specific C++ overload, based on its signature.
753 const char* sigarg = nullptr;
754 PyObject* sigarg_tuple = nullptr;
755 int want_const = -1;
756
757 std::string proto;
758
759 if (PyArg_ParseTuple(args, const_cast<char*>("s|i:__overload__"), &sigarg, &want_const)) {
760 want_const = PyTuple_GET_SIZE(args) == 1 ? -1 : want_const;
761
762 // check existing overloads in order
763 PyObject* ol = pytmpl->fTI->fNonTemplated->FindOverload(sigarg, want_const);
764 if (ol) return ol;
765 PyErr_Clear();
766 ol = pytmpl->fTI->fTemplated->FindOverload(sigarg, want_const);
767 if (ol) return ol;
768 PyErr_Clear();
769 ol = pytmpl->fTI->fLowPriority->FindOverload(sigarg, want_const);
770 if (ol) return ol;
771
772 proto = Utility::ConstructTemplateArgs(nullptr, args);
773 } else if (PyArg_ParseTuple(args, const_cast<char*>("O|i:__overload__"), &sigarg_tuple, &want_const)) {
774 PyErr_Clear();
775 want_const = PyTuple_GET_SIZE(args) == 1 ? -1 : want_const;
776
777 // check existing overloads in order
778 PyObject* ol = pytmpl->fTI->fNonTemplated->FindOverload(sigarg_tuple, want_const);
779 if (ol) return ol;
780 PyErr_Clear();
781 ol = pytmpl->fTI->fTemplated->FindOverload(sigarg_tuple, want_const);
782 if (ol) return ol;
783 PyErr_Clear();
784 ol = pytmpl->fTI->fLowPriority->FindOverload(sigarg_tuple, want_const);
785 if (ol) return ol;
786
787 proto.reserve(128);
788 proto.push_back('<');
789 Py_ssize_t n = PyTuple_Size(sigarg_tuple);
790 for (int i = 0; i < n; i++) {
791 PyObject *pItem = PyTuple_GetItem(sigarg_tuple, i);
792 if(!CPyCppyy_PyText_Check(pItem)) {
793 PyErr_Format(PyExc_LookupError, "argument types should be in string format");
794 return (PyObject*) nullptr;
795 }
796 proto.append(CPyCppyy_PyText_AsString(pItem));
797 if (i < n - 1)
798 proto.push_back(',');
799 }
800 proto.push_back('>');
801 } else {
802 PyErr_Format(PyExc_TypeError, "Unexpected arguments to __overload__");
803 return nullptr;
804 }
805
806// else attempt instantiation
807 PyObject* pytype = 0, *pyvalue = 0, *pytrace = 0;
808 PyErr_Fetch(&pytype, &pyvalue, &pytrace);
809
810 Cppyy::TCppScope_t scope = ((CPPClass*)pytmpl->fTI->fPyClass)->fCppType;
812 scope, CPyCppyy_PyText_AsString(pytmpl->fTI->fCppName),
813 proto.substr(1, proto.size()-2));
814
815 if (!cppmeth) {
816 PyErr_Restore(pytype, pyvalue, pytrace);
817 return nullptr;
818 }
819
820 Py_XDECREF(pytype);
821 Py_XDECREF(pyvalue);
822 Py_XDECREF(pytrace);
823
824 // TODO: the next step should be consolidated with Instantiate()
825 PyCallable* meth = nullptr;
826 if (Cppyy::IsNamespace(scope)) {
827 meth = new CPPFunction(scope, cppmeth);
828 } else if (Cppyy::IsStaticMethod(cppmeth)) {
829 meth = new CPPClassMethod(scope, cppmeth);
830 } else if (Cppyy::IsConstructor(cppmeth)) {
831 meth = new CPPConstructor(scope, cppmeth);
832 } else
833 meth = new CPPMethod(scope, cppmeth);
834
835 return (PyObject*)CPPOverload_New(CPyCppyy_PyText_AsString(pytmpl->fTI->fCppName) + proto, meth);
836}
837
838static PyMethodDef tpp_methods[] = {
839 {(char*)"__overload__", (PyCFunction)tpp_overload, METH_VARARGS,
840 (char*)"select overload for dispatch" },
841 {(char*)nullptr, nullptr, 0, nullptr }
842};
843
844//= CPyCppyy template proxy type =============================================
845PyTypeObject TemplateProxy_Type = {
846 PyVarObject_HEAD_INIT(&PyType_Type, 0)
847 (char*)"cppyy.TemplateProxy", // tp_name
848 sizeof(TemplateProxy), // tp_basicsize
849 0, // tp_itemsize
850 (destructor)tpp_dealloc, // tp_dealloc
851 0, // tp_print
852 0, // tp_getattr
853 0, // tp_setattr
854 0, // tp_compare
855 (reprfunc)tpp_repr, // tp_repr
856 0, // tp_as_number
857 0, // tp_as_sequence
858 &tpp_as_mapping, // tp_as_mapping
859 (hashfunc)tpp_hash, // tp_hash
860 (ternaryfunc)tpp_call, // tp_call
861 0, // tp_str
862 0, // tp_getattro
863 0, // tp_setattro
864 0, // tp_as_buffer
865 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, // tp_flags
866 (char*)"cppyy template proxy (internal)", // tp_doc
867 (traverseproc)tpp_traverse,// tp_traverse
868 (inquiry)tpp_clear, // tp_clear
869 (richcmpfunc)tpp_richcompare,// tp_richcompare
870 offsetof(TemplateProxy, fWeakrefList), // tp_weaklistoffset
871 0, // tp_iter
872 0, // tp_iternext
873 tpp_methods, // tp_methods
874 0, // tp_members
875 tpp_getset, // tp_getset
876 0, // tp_base
877 0, // tp_dict
878 (descrgetfunc)tpp_descrget,// tp_descr_get
879 0, // tp_descr_set
880 0, // tp_dictoffset
881 0, // tp_init
882 0, // tp_alloc
883 (newfunc)tpp_new, // tp_new
884 0, // tp_free
885 0, // tp_is_gc
886 0, // tp_bases
887 0, // tp_mro
888 0, // tp_cache
889 0, // tp_subclasses
890 0 // tp_weaklist
891#if PY_VERSION_HEX >= 0x02030000
892 , 0 // tp_del
893#endif
894#if PY_VERSION_HEX >= 0x02060000
895 , 0 // tp_version_tag
896#endif
897#if PY_VERSION_HEX >= 0x03040000
898 , 0 // tp_finalize
899#endif
900};
901
902} // namespace CPyCppyy
#define Py_TYPE(ob)
Definition CPyCppyy.h:217
#define Py_RETURN_TRUE
Definition CPyCppyy.h:293
#define CPyCppyy_PyText_InternFromString
Definition CPyCppyy.h:103
#define Py_RETURN_FALSE
Definition CPyCppyy.h:297
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
long Py_hash_t
Definition CPyCppyy.h:135
#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
_object PyObject
#define c(i)
Definition RSha256.hxx:101
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
char name[80]
Definition TGX11.cxx:110
#define pyname
#define TPPCALL_RETURN
const char * proto
Definition civetweb.c:17536
void MergeOverload(CPPOverload *meth)
void AdoptMethod(PyCallable *pc)
MethodInfo_t * fMethodInfo
Definition CPPOverload.h:72
virtual PyCallable * Clone()=0
virtual bool IsGreedy()=0
CPPOverload * fTemplated
CPPOverload * fLowPriority
TP_DispatchMap_t fDispatchMap
CPPOverload * fNonTemplated
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
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:31
PyObject * gTypeCode
Definition PyStrings.cxx:30
PyObject * gNoImplicit
Definition PyStrings.cxx:57
size_t FetchError(std::vector< PyError_t > &)
Definition Utility.cxx:945
std::string ConstructTemplateArgs(PyObject *pyname, PyObject *tpArgs, PyObject *args=nullptr, ArgPreference=kNone, int argoff=0, int *pcnt=nullptr)
Definition Utility.cxx:474
void SetDetailedException(std::vector< PyError_t > &errors, PyObject *topmsg, PyObject *defexc)
Definition Utility.cxx:957
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:95
static PyObject * tpp_richcompare(TemplateProxy *self, PyObject *other, int op)
static PyObject * tpp_doc(TemplateProxy *pytmpl, void *)
static PyMappingMethods tpp_as_mapping
bool CPPOverload_Check(T *object)
Definition CPPOverload.h:83
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 *)
bool TemplateProxy_CheckExact(T *object)
static void tpp_dealloc(TemplateProxy *pytmpl)
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
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 PyMethodDef tpp_methods[]
static Py_hash_t tpp_hash(TemplateProxy *self)
static std::string targs2str(TemplateProxy *pytmpl)
bool TemplateProxy_Check(T *object)
std::shared_ptr< TemplateInfo > TP_TInfo_t
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 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)
size_t TCppScope_t
Definition cpp_cppyy.h:18
RPY_EXPORTED std::string GetMethodFullName(TCppMethod_t)
CPPOverload::Methods_t fMethods
Definition CPPOverload.h:47