Logo ROOT   6.14/05
Reference Guide
TemplateProxy.cxx
Go to the documentation of this file.
1 // Author: Wim Lavrijsen, Jan 2005
2 
3 // Bindings
4 #include "PyROOT.h"
5 #include "TemplateProxy.h"
6 #include "MethodProxy.h"
7 #include "TFunctionHolder.h"
8 #include "TMethodHolder.h"
9 #include "PyCallable.h"
10 #include "PyStrings.h"
11 #include "Utility.h"
12 
13 // ROOT
14 #include "TClass.h"
15 #include "TMethod.h"
16 
17 
18 namespace PyROOT {
19 
20 ////////////////////////////////////////////////////////////////////////////////
21 /// Initialize the proxy for the given 'pyclass.'
22 
23 void TemplateProxy::Set( const std::string& name, PyObject* pyclass )
24 {
25  fPyName = PyROOT_PyUnicode_FromString( const_cast< char* >( name.c_str() ) );
26  Py_XINCREF( pyclass );
27  fPyClass = pyclass;
28  fSelf = NULL;
29  std::vector< PyCallable* > dummy;
30  fNonTemplated = MethodProxy_New( name, dummy );
31  fTemplated = MethodProxy_New( name, dummy );
32 }
33 
34 ////////////////////////////////////////////////////////////////////////////////
35 /// Store overloads of this templated method.
36 
38  fNonTemplated->AddMethod( mp );
39 }
40 
42 // Store overload of this templated method.
43  fNonTemplated->AddMethod( pc );
44 }
45 
47 {
48 // Store know template methods.
49  fTemplated->AddMethod( pc );
50 }
51 
52 
53 namespace {
54 
55 //= PyROOT template proxy construction/destruction ===========================
56  TemplateProxy* tpp_new( PyTypeObject*, PyObject*, PyObject* )
57  {
58  // Create a new empty template method proxy.
59  TemplateProxy* pytmpl = PyObject_GC_New( TemplateProxy, &TemplateProxy_Type );
60  pytmpl->fPyName = NULL;
61  pytmpl->fPyClass = NULL;
62  pytmpl->fSelf = NULL;
63  pytmpl->fNonTemplated = NULL;
64  pytmpl->fTemplated = NULL;
65 
66  PyObject_GC_Track( pytmpl );
67  return pytmpl;
68  }
69 
70 ////////////////////////////////////////////////////////////////////////////////
71 /// Garbage collector clear of held python member objects.
72 
73  int tpp_clear( TemplateProxy* pytmpl )
74  {
75  Py_CLEAR( pytmpl->fPyName );
76  Py_CLEAR( pytmpl->fPyClass );
77  Py_CLEAR( pytmpl->fSelf );
78  Py_CLEAR( pytmpl->fNonTemplated );
79  Py_CLEAR( pytmpl->fTemplated );
80 
81  return 0;
82  }
83 
84 ////////////////////////////////////////////////////////////////////////////////
85 /// Destroy the given template method proxy.
86 
87  void tpp_dealloc( TemplateProxy* pytmpl )
88  {
89  PyObject_GC_UnTrack( pytmpl );
90  tpp_clear( pytmpl );
91  PyObject_GC_Del( pytmpl );
92  }
93 
94 ////////////////////////////////////////////////////////////////////////////////
95 /// Forward to method proxies to doc all overloads
96 
97  PyObject* tpp_doc( TemplateProxy* pytmpl, void* )
98  {
99  PyObject* doc = nullptr;
100  if ( pytmpl->fNonTemplated )
101  doc = PyObject_GetAttrString( (PyObject*)pytmpl->fNonTemplated, "__doc__" );
102  if ( pytmpl->fTemplated ) {
103  PyObject* doc2 = PyObject_GetAttrString( (PyObject*)pytmpl->fTemplated, "__doc__" );
104  if ( doc && doc2 ) {
106  PyROOT_PyUnicode_AppendAndDel( &doc, doc2 );
107  } else if ( !doc && doc2 ) {
108  doc = doc2;
109  }
110  }
111 
112  if ( doc )
113  return doc;
114 
116  }
117 
118 ////////////////////////////////////////////////////////////////////////////////
119 /// Garbage collector traverse of held python member objects.
120 
121  int tpp_traverse( TemplateProxy* pytmpl, visitproc visit, void* arg )
122  {
123  Py_VISIT( pytmpl->fPyName );
124  Py_VISIT( pytmpl->fPyClass );
125  Py_VISIT( pytmpl->fSelf );
126  Py_VISIT( pytmpl->fNonTemplated );
127  Py_VISIT( pytmpl->fTemplated );
128 
129  return 0;
130  }
131 
132 //= PyROOT template proxy callable behavior ==================================
133  PyObject* tpp_call( TemplateProxy* pytmpl, PyObject* args, PyObject* kwds )
134  {
135  // Dispatcher to the actual member method, several uses possible; in order:
136  //
137  // case 1:
138  //
139  // obj.method( a0, a1, ... ) # non-template
140  // => obj->method( a0, a1, ... ) // non-template
141  //
142  // case 2:
143  //
144  // obj.method( t0, t1, ... )( a0, a1, ... )
145  // -> getattr( obj, 'method< t0, t1, ... >' )( a0, a1, ... )
146  // => obj->method< t0, t1, ... >( a0, a1, ... )
147  //
148  // case 3:
149  //
150  // obj.method( a0, a1, ... ) # all known templates
151  // => obj->method( a0, a1, ... ) // all known templates
152  //
153  // case 4:
154  //
155  // collect types of arguments unless they are a type themselves (the idea
156  // here is it is more likely for e.g. (1, 3.14) to be real arguments and
157  // e.g. (int, 'double') to be template arguments:
158  // a) if ! is_type(arg)
159  // template<> method< T0, T1, ... >( type(a0), type(a1), ... )
160  // b) else
161  // drop to case 5
162  // TODO: there is ambiguity in the above if the arguments are strings
163  //
164  // case 5:
165  //
166  // instantiate template<> method< t0, t1, ... >
167 
168  // case 1: obj->method( a0, a1, ... )
169 
170  // simply forward the call: all non-templated methods are defined on class definition
171  // and thus already available
172  PyObject* pymeth = MethodProxy_Type.tp_descr_get(
173  (PyObject*)pytmpl->fNonTemplated, pytmpl->fSelf, (PyObject*)&MethodProxy_Type );
174  if ( MethodProxy_Check( pymeth ) ) {
175  // now call the method with the arguments
176  PyObject* result = MethodProxy_Type.tp_call( pymeth, args, kwds );
177  Py_DECREF( pymeth ); pymeth = 0;
178  if ( result )
179  return result;
180  // TODO: collect error here, as the failure may be either an overload
181  // failure after which we should continue; or a real failure, which should
182  // be reported.
183  }
184  Py_XDECREF( pymeth ); pymeth = 0;
185  PyErr_Clear();
186 
187  // error check on method() which can not be derived if non-templated case fails
188  Py_ssize_t nArgs = PyTuple_GET_SIZE( args );
189  if ( nArgs == 0 ) {
190  PyErr_Format( PyExc_TypeError, "template method \'%s\' with no arguments must be explicit",
191  PyROOT_PyUnicode_AsString( pytmpl->fPyName ) );
192  return 0;
193  }
194 
195  // case 2: non-instantiating obj->method< t0, t1, ... >( a0, a1, ... )
196 
197  // build "< type, type, ... >" part of method name
198  PyObject* pyname_v1 = Utility::BuildTemplateName( pytmpl->fPyName, args, 0 );
199  if ( pyname_v1 ) {
200  // lookup method on self (to make sure it propagates), which is readily callable
201  pymeth = PyObject_GetAttr( pytmpl->fSelf ? pytmpl->fSelf : pytmpl->fPyClass, pyname_v1 );
202  if ( pymeth ) { // overloads stop here, as this is an explicit match
203  Py_DECREF( pyname_v1 );
204  return pymeth; // callable method, next step is by user
205  }
206  }
207  PyErr_Clear();
208 
209  // case 3: loop over all previously instantiated templates
210  pymeth = MethodProxy_Type.tp_descr_get(
211  (PyObject*)pytmpl->fTemplated, pytmpl->fSelf, (PyObject*)&MethodProxy_Type );
212  if ( MethodProxy_Check( pymeth ) ) {
213  // now call the method with the arguments
214  PyObject* result = MethodProxy_Type.tp_call( pymeth, args, kwds );
215  Py_DECREF( pymeth ); pymeth = 0;
216  if ( result ) {
217  Py_XDECREF( pyname_v1 );
218  return result;
219  }
220  // TODO: collect error here, as the failure may be either an overload
221  // failure after which we should continue; or a real failure, which should
222  // be reported.
223  }
224  Py_XDECREF( pymeth ); pymeth = 0;
225  PyErr_Clear();
226 
227  // still here? try instantiating methods
228 
229  Bool_t isType = kFALSE;
230  Int_t nStrings = 0;
231  PyObject* tpArgs = PyTuple_New( nArgs );
232  for ( Int_t i = 0; i < nArgs; ++i ) {
233  PyObject* itemi = PyTuple_GET_ITEM( args, i );
234  if ( PyType_Check( itemi ) ) isType = kTRUE;
235 #if PY_VERSION_HEX >= 0x03000000
236  else if ( ! isType && PyUnicode_Check( itemi ) ) nStrings += 1;
237 #else
238  else if ( ! isType && PyBytes_Check( itemi ) ) nStrings += 1;
239 #endif
240  // special case for arrays
241  PyObject* pytc = PyObject_GetAttr( itemi, PyStrings::gTypeCode );
242  if ( ! ( pytc && PyROOT_PyUnicode_Check( pytc ) ) ) {
243  // normal case (not an array)
244  PyErr_Clear();
245  PyObject* tp = (PyObject*)Py_TYPE( itemi );
246  Py_INCREF( tp );
247  PyTuple_SET_ITEM( tpArgs, i, tp );
248  } else {
249  // array, build up a pointer type
250  char tc = ((char*)PyROOT_PyUnicode_AsString( pytc ))[0];
251  const char* ptrname = 0;
252  switch ( tc ) {
253  case 'b': ptrname = "char*"; break;
254  case 'h': ptrname = "short*"; break;
255  case 'H': ptrname = "unsigned short*"; break;
256  case 'i': ptrname = "int*"; break;
257  case 'I': ptrname = "unsigned int*"; break;
258  case 'l': ptrname = "long*"; break;
259  case 'L': ptrname = "unsigned long*"; break;
260  case 'f': ptrname = "float*"; break;
261  case 'd': ptrname = "double*"; break;
262  default: ptrname = "void*"; // TODO: verify if this is right
263  }
264  if ( ptrname ) {
265  PyObject* pyptrname = PyBytes_FromString( ptrname );
266  PyTuple_SET_ITEM( tpArgs, i, pyptrname );
267  // string added, but not counted towards nStrings
268  } else {
269  // this will cleanly fail instantiation
270  Py_INCREF( pytc );
271  PyTuple_SET_ITEM( tpArgs, i, pytc );
272  }
273  }
274  Py_XDECREF( pytc );
275  }
276 
277  PyObject* clName = PyObject_GetAttr( pytmpl->fPyClass, PyStrings::gCppName );
278  if ( ! clName ) clName = PyObject_GetAttr( pytmpl->fPyClass, PyStrings::gName );
279  TClass* klass = TClass::GetClass( PyROOT_PyUnicode_AsString( clName ) );
280  Py_DECREF( clName );
281  const std::string& tmplname = pytmpl->fNonTemplated->fMethodInfo->fName;
282 
283  // case 4a: instantiating obj->method< T0, T1, ... >( type(a0), type(a1), ... )( a0, a1, ... )
284  if ( ! isType && ! ( nStrings == nArgs ) ) { // no types among args and not all strings
285  PyObject* pyname_v2 = Utility::BuildTemplateName( NULL, tpArgs, 0 );
286  if ( pyname_v2 ) {
287  std::string mname = PyROOT_PyUnicode_AsString( pyname_v2 );
288  Py_DECREF( pyname_v2 );
289  std::string proto = mname.substr( 1, mname.size() - 2 );
290  // the following causes instantiation as necessary
291  TMethod* cppmeth = klass ? klass->GetMethodWithPrototype( tmplname.c_str(), proto.c_str() ) : 0;
292  if ( cppmeth ) { // overload stops here
293  Py_XDECREF( pyname_v1 );
294  Cppyy::TCppScope_t scope = Cppyy::GetScope( klass->GetName() );
295  if ( (klass->Property() & kIsNamespace) || (cppmeth->Property() & kIsStatic) ) {
296  pytmpl->fTemplated->AddMethod( new TFunctionHolder( scope, (Cppyy::TCppMethod_t)cppmeth ) );
297  pymeth = (PyObject*)MethodProxy_New(
298  cppmeth->GetName(), new TFunctionHolder( scope, (Cppyy::TCppMethod_t)cppmeth ) );
299  } else {
300  pytmpl->fTemplated->AddMethod( new TMethodHolder( scope, (Cppyy::TCppMethod_t)cppmeth ) );
301  pymeth = (PyObject*)MethodProxy_New(
302  cppmeth->GetName(), new TMethodHolder( scope, (Cppyy::TCppMethod_t)cppmeth ) );
303  }
304  PyObject_SetAttrString( pytmpl->fPyClass, (char*)cppmeth->GetName(), (PyObject*)pymeth );
305  Py_DECREF( pymeth );
306  pymeth = PyObject_GetAttrString(
307  pytmpl->fSelf ? pytmpl->fSelf : pytmpl->fPyClass, (char*)cppmeth->GetName() );
308  PyObject* result = MethodProxy_Type.tp_call( pymeth, args, kwds );
309  Py_DECREF( pymeth );
310  return result;
311  }
312  }
313  }
314 
315  // case 4b/5: instantiating obj->method< t0, t1, ... >( a0, a1, ... )
316  if ( pyname_v1 ) {
317  std::string mname = PyROOT_PyUnicode_AsString( pyname_v1 );
318  // the following causes instantiation as necessary
319  TMethod* cppmeth = klass ? klass->GetMethodAny( mname.c_str() ) : 0;
320  if ( cppmeth ) { // overload stops here
321  pymeth = (PyObject*)MethodProxy_New(
322  mname, new TMethodHolder( Cppyy::GetScope( klass->GetName() ), (Cppyy::TCppMethod_t)cppmeth ) );
323  PyObject_SetAttr( pytmpl->fPyClass, pyname_v1, (PyObject*)pymeth );
324  if ( mname != cppmeth->GetName() ) // happens with typedefs and template default arguments
325  PyObject_SetAttrString( pytmpl->fPyClass, (char*)mname.c_str(), (PyObject*)pymeth );
326  Py_DECREF( pymeth );
327  pymeth = PyObject_GetAttr( pytmpl->fSelf ? pytmpl->fSelf : pytmpl->fPyClass, pyname_v1 );
328  Py_DECREF( pyname_v1 );
329  return pymeth; // callable method, next step is by user
330  }
331  Py_DECREF( pyname_v1 );
332  }
333 
334  // moderately generic error message, but should be clear enough
335  PyErr_Format( PyExc_TypeError, "can not resolve method template call for \'%s\'",
336  PyROOT_PyUnicode_AsString( pytmpl->fPyName ) );
337  return 0;
338  }
339 
340 ////////////////////////////////////////////////////////////////////////////////
341 /// create and use a new template proxy (language requirement)
342 
343  TemplateProxy* tpp_descrget( TemplateProxy* pytmpl, PyObject* pyobj, PyObject* )
344  {
345  TemplateProxy* newPyTmpl = (TemplateProxy*)TemplateProxy_Type.tp_alloc( &TemplateProxy_Type, 0 );
346 
347  // copy name and class pointers
348  Py_INCREF( pytmpl->fPyName );
349  newPyTmpl->fPyName = pytmpl->fPyName;
350 
351  Py_XINCREF( pytmpl->fPyClass );
352  newPyTmpl->fPyClass = pytmpl->fPyClass;
353 
354  // copy non-templated method proxy pointer
355  Py_INCREF( pytmpl->fNonTemplated );
356  newPyTmpl->fNonTemplated = pytmpl->fNonTemplated;
357 
358  // copy templated method proxy pointer
359  Py_INCREF( pytmpl->fTemplated );
360  newPyTmpl->fTemplated = pytmpl->fTemplated;
361 
362  // new method is to be bound to current object (may be NULL)
363  Py_XINCREF( pyobj );
364  newPyTmpl->fSelf = pyobj;
365 
366  return newPyTmpl;
367  }
368 
369 ////////////////////////////////////////////////////////////////////////////////
370 
371  PyGetSetDef tpp_getset[] = {
372  { (char*)"__doc__", (getter)tpp_doc, NULL, NULL, NULL },
373  { (char*)NULL, NULL, NULL, NULL, NULL }
374  };
375 
376 } // unnamed namespace
377 
378 
379 //= PyROOT template proxy type ===============================================
380 PyTypeObject TemplateProxy_Type = {
381  PyVarObject_HEAD_INIT( &PyType_Type, 0 )
382  (char*)"ROOT.TemplateProxy", // tp_name
383  sizeof(TemplateProxy), // tp_basicsize
384  0, // tp_itemsize
385  (destructor)tpp_dealloc, // tp_dealloc
386  0, // tp_print
387  0, // tp_getattr
388  0, // tp_setattr
389  0, // tp_compare
390  0, // tp_repr
391  0, // tp_as_number
392  0, // tp_as_sequence
393  0, // tp_as_mapping
394  0, // tp_hash
395  (ternaryfunc)tpp_call, // tp_call
396  0, // tp_str
397  0, // tp_getattro
398  0, // tp_setattro
399  0, // tp_as_buffer
400  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, // tp_flags
401  (char*)"PyROOT template proxy (internal)", // tp_doc
402  (traverseproc)tpp_traverse,// tp_traverse
403  (inquiry)tpp_clear, // tp_clear
404  0, // tp_richcompare
405  0, // tp_weaklistoffset
406  0, // tp_iter
407  0, // tp_iternext
408  0, // tp_methods
409  0, // tp_members
410  tpp_getset, // tp_getset
411  0, // tp_base
412  0, // tp_dict
413  (descrgetfunc)tpp_descrget,// tp_descr_get
414  0, // tp_descr_set
415  0, // tp_dictoffset
416  0, // tp_init
417  0, // tp_alloc
418  (newfunc)tpp_new, // tp_new
419  0, // tp_free
420  0, // tp_is_gc
421  0, // tp_bases
422  0, // tp_mro
423  0, // tp_cache
424  0, // tp_subclasses
425  0 // tp_weaklist
426 #if PY_VERSION_HEX >= 0x02030000
427  , 0 // tp_del
428 #endif
429 #if PY_VERSION_HEX >= 0x02060000
430  , 0 // tp_version_tag
431 #endif
432 #if PY_VERSION_HEX >= 0x03040000
433  , 0 // tp_finalize
434 #endif
435 };
436 
437 } // namespace PyROOT
virtual const char * GetName() const
Returns name of object.
Definition: TNamed.h:47
#define PyBytes_FromString
Definition: PyROOT.h:59
#define PyROOT_PyUnicode_FromString
Definition: PyROOT.h:71
void AddOverload(MethodProxy *mp)
Store overloads of this templated method.
MethodProxy * fNonTemplated
Definition: TemplateProxy.h:33
MethodProxy * MethodProxy_New(const std::string &name, std::vector< PyCallable * > &methods)
Definition: MethodProxy.h:75
int Int_t
Definition: RtypesCore.h:41
PyObject * BuildTemplateName(PyObject *pyname, PyObject *args, int argoff)
Helper to construct the "< type, type, ... >" part of a templated name (either for a class as in Make...
Definition: Utility.cxx:462
bool Bool_t
Definition: RtypesCore.h:59
#define PyVarObject_HEAD_INIT(type, size)
Definition: PyROOT.h:149
Long_t Property() const
Get property description word. For meaning of bits see EProperty.
Definition: TFunction.cxx:183
#define PyROOT_PyUnicode_AsString
Definition: PyROOT.h:66
MethodProxy * fTemplated
Definition: TemplateProxy.h:34
#define PyBytes_Check
Definition: PyROOT.h:52
ptrdiff_t TCppMethod_t
Definition: Cppyy.h:15
PyObject_HEAD PyObject * fSelf
Definition: TemplateProxy.h:30
The ROOT global object gROOT contains a list of all defined classes.
Definition: TClass.h:75
void AddTemplate(PyCallable *pc)
Long_t Property() const
Set TObject::fBits and fStreamerType to cache information about the class.
Definition: TClass.cxx:5768
PyTypeObject MethodProxy_Type
PyTypeObject TemplateProxy_Type
void Set(const std::string &name, PyObject *pyclass)
Initialize the proxy for the given &#39;pyclass.&#39;.
const Bool_t kFALSE
Definition: RtypesCore.h:88
MethodInfo_t * fMethodInfo
Definition: MethodProxy.h:52
#define PyROOT_PyUnicode_AppendAndDel
Definition: PyROOT.h:74
TCppScope_t GetScope(const std::string &scope_name)
Definition: Cppyy.cxx:176
R__EXTERN PyObject * gName
Definition: PyStrings.h:33
void AddMethod(PyCallable *pc)
Fill in the data of a freshly created method proxy.
R__EXTERN PyObject * gTypeCode
Definition: PyStrings.h:36
static RooMathCoreReg dummy
#define PyROOT_PyUnicode_Check
Definition: PyROOT.h:64
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition: TClass.cxx:2887
R__EXTERN PyObject * gCppName
Definition: PyStrings.h:34
#define Py_TYPE(ob)
Definition: PyROOT.h:151
int Py_ssize_t
Definition: PyROOT.h:156
Each ROOT class (see TClass) has a linked list of methods.
Definition: TMethod.h:38
Bool_t MethodProxy_Check(T *object)
Definition: MethodProxy.h:63
const char * proto
Definition: civetweb.c:15049
static constexpr double pc
TMethod * GetMethodAny(const char *method)
Return pointer to method without looking at parameters.
Definition: TClass.cxx:4195
TMethod * GetMethodWithPrototype(const char *method, const char *proto, Bool_t objectIsConst=kFALSE, ROOT::EFunctionMatchMode mode=ROOT::kConversionMatch)
Find the method with a given prototype.
Definition: TClass.cxx:4277
ptrdiff_t TCppScope_t
Definition: Cppyy.h:12
const Bool_t kTRUE
Definition: RtypesCore.h:87
Template proxy object to return functions and methods.
Definition: TemplateProxy.h:24
char name[80]
Definition: TGX11.cxx:109
_object PyObject
Definition: TPyArg.h:20