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 
17 namespace CPyCppyy {
18 
19 //- helper for ctypes conversions --------------------------------------------
20 static 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 //----------------------------------------------------------------------------
51 TemplateInfo::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 //----------------------------------------------------------------------------
76 void 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 //----------------------------------------------------------------------------
121 PyObject* 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_DECREF(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 =========================
315 static 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 //----------------------------------------------------------------------------
330 static 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 //----------------------------------------------------------------------------
340 static 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 //----------------------------------------------------------------------------
352 static 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 //----------------------------------------------------------------------------
362 static 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) {
372  CPyCppyy_PyText_AppendAndDel(&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) {
381  CPyCppyy_PyText_AppendAndDel(&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 ================================
403 static inline std::string targs2str(TemplateProxy* pytmpl)
404 {
405  if (!pytmpl || !pytmpl->fTemplateArgs) return "";
406  return CPyCppyy_PyText_AsString(pytmpl->fTemplateArgs);
407 }
408 
409 static 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 
427 static 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 
466 static 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
539  PyObject* pyfullname = CPyCppyy_PyText_FromString(
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 //-----------------------------------------------------------------------------
707 static int tpp_setuseffi(CPPOverload*, PyObject*, void*)
708 {
709  return 0; // dummy (__useffi__ unused)
710 }
711 
712 
713 //----------------------------------------------------------------------------
714 static PyMappingMethods tpp_as_mapping = {
715  nullptr, (binaryfunc)tpp_subscript, nullptr
716 };
717 
718 static 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 =============================================
727 PyTypeObject 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 CPyCppyy_PyText_InternFromString
Definition: CPyCppyy.h:103
static std::string targs2str(TemplateProxy *pytmpl)
#define pyname
Definition: TMCParticle.cxx:19
PyTypeObject CPPOverload_Type
#define TPPCALL_RETURN
PyInt_FromLong
Definition: Converters.cxx:858
static PyGetSetDef tpp_getset[]
size_t TCppScope_t
Definition: cpp_cppyy.h:18
TP_DispatchMap_t fDispatchMap
Definition: TemplateProxy.h:43
static PyObject * TC2CppName(PyObject *pytc, const char *cpd, bool allow_voidp)
CPPOverload * fLowPriority
Definition: TemplateProxy.h:40
RPY_EXPORTED bool IsStaticMethod(TCppMethod_t method)
static PyMappingMethods tpp_as_mapping
void SetDetailedException(std::vector< PyError_t > &errors, PyObject *topmsg, PyObject *defexc)
Definition: Utility.cxx:891
PyObject * gCTypesType
Definition: PyStrings.cxx:29
PyObject_HEAD PyObject * fSelf
Definition: TemplateProxy.h:58
virtual PyCallable * Clone()=0
std::string ConstructTemplateArgs(PyObject *pyname, PyObject *tpArgs, PyObject *args=nullptr, ArgPreference=kNone, int argoff=0, int *pcnt=nullptr)
Definition: Utility.cxx:473
#define Py_TYPE(ob)
Definition: CPyCppyy.h:209
void MergeOverload(CPPOverload *meth)
PyObject * gNoImplicit
Definition: PyStrings.cxx:55
#define PyVarObject_HEAD_INIT(type, size)
Definition: CPyCppyy.h:207
static PyObject * tpp_call(TemplateProxy *pytmpl, PyObject *args, PyObject *kwds)
static PyObject * tpp_doc(TemplateProxy *pytmpl, void *)
static TemplateProxy * tpp_descrget(TemplateProxy *pytmpl, PyObject *pyobj, PyObject *)
static PyObject * CallMethodImp(TemplateProxy *pytmpl, PyObject *&pymeth, PyObject *args, PyObject *kwds, bool impOK, uint64_t sighash)
RPY_EXPORTED bool IsConstructor(TCppMethod_t method)
PyObject * gDict
Definition: PyStrings.cxx:14
static void tpp_dealloc(TemplateProxy *pytmpl)
void AdoptTemplate(PyCallable *pc)
void AdoptMethod(PyCallable *pc)
PyTypeObject TemplateProxy_Type
bool CPPOverload_Check(T *object)
Definition: CPPOverload.h:79
CPPOverload * CPPOverload_New(const std::string &name, std::vector< PyCallable *> &methods)
Definition: CPPOverload.h:91
static constexpr double m2
std::shared_ptr< TemplateInfo > TP_TInfo_t
Definition: TemplateProxy.h:48
RPY_EXPORTED std::string GetMethodFullName(TCppMethod_t)
static constexpr double pc
PyObject * gTypeCode
Definition: PyStrings.cxx:28
_object PyObject
Definition: PyMethodBase.h:41
static PyObject * tpp_subscript(TemplateProxy *pytmpl, PyObject *args)
intptr_t TCppMethod_t
Definition: cpp_cppyy.h:22
void AdoptMethod(PyCallable *pc)
uint64_t HashSignature(PyObject *args)
Definition: CPPOverload.h:16
#define CPyCppyy_PyText_Append
Definition: CPyCppyy.h:104
int Py_ssize_t
Definition: CPyCppyy.h:228
static TemplateProxy * tpp_new(PyTypeObject *, PyObject *, PyObject *)
static int tpp_setuseffi(CPPOverload *, PyObject *, void *)
void MergeOverload(CPPOverload *mp)
#define CPyCppyy_PyText_FromString
Definition: CPyCppyy.h:102
#define CPyCppyy_PyText_FromFormat
Definition: CPyCppyy.h:101
#define CPyCppyy_PyText_AppendAndDel
Definition: CPyCppyy.h:105
void Set(const std::string &cppname, const std::string &pyname, PyObject *pyclass)
static RooMathCoreReg dummy
RPY_EXPORTED bool IsNamespace(TCppScope_t scope)
static PyObject * tpp_repr(TemplateProxy *pytmpl)
bool TemplateProxy_Check(T *object)
Definition: TemplateProxy.h:79
CPPOverload::Methods_t fMethods
Definition: CPPOverload.h:47
CPPOverload * fTemplated
Definition: TemplateProxy.h:39
PyObject * Instantiate(const std::string &fname, PyObject *tmplArgs, Utility::ArgPreference, int *pcnt=nullptr)
MethodInfo_t * fMethodInfo
Definition: CPPOverload.h:68
static int tpp_clear(TemplateProxy *pytmpl)
size_t FetchError(std::vector< PyError_t > &)
Definition: Utility.cxx:879
RPY_EXPORTED TCppMethod_t GetMethodTemplate(TCppScope_t scope, const std::string &name, const std::string &proto)
static void UpdateDispatchMap(TemplateProxy *pytmpl, bool use_targs, uint64_t sighash, CPPOverload *pymeth)
CPPOverload * fNonTemplated
Definition: TemplateProxy.h:38
const char * proto
Definition: civetweb.c:16604
#define c(i)
Definition: RSha256.hxx:101
static int tpp_traverse(TemplateProxy *pytmpl, visitproc visit, void *arg)
static PyObject * tpp_getuseffi(CPPOverload *, void *)
char name[80]
Definition: TGX11.cxx:109
#define CPyCppyy_PyText_Check
Definition: CPyCppyy.h:95
#define CPyCppyy_PyText_AsString
Definition: CPyCppyy.h:97