Logo ROOT   6.16/01
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
18namespace PyROOT {
19
20////////////////////////////////////////////////////////////////////////////////
21/// Initialize the proxy for the given 'pyclass.'
22
23void 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;
32}
33
34////////////////////////////////////////////////////////////////////////////////
35/// Store overloads of this templated method.
36
39}
40
42// Store overload of this templated method.
44}
45
47{
48// Store know template methods.
50}
51
52
53namespace {
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 );
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 ===============================================
380PyTypeObject 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
#define Py_TYPE(ob)
Definition: PyROOT.h:161
#define PyBytes_FromString
Definition: PyROOT.h:71
int Py_ssize_t
Definition: PyROOT.h:166
#define PyROOT_PyUnicode_Check
Definition: PyROOT.h:76
#define PyBytes_Check
Definition: PyROOT.h:64
#define PyROOT_PyUnicode_AsString
Definition: PyROOT.h:78
#define PyROOT_PyUnicode_AppendAndDel
Definition: PyROOT.h:85
#define PyROOT_PyUnicode_FromString
Definition: PyROOT.h:82
#define PyVarObject_HEAD_INIT(type, size)
Definition: PyROOT.h:159
static RooMathCoreReg dummy
int Int_t
Definition: RtypesCore.h:41
const Bool_t kFALSE
Definition: RtypesCore.h:88
bool Bool_t
Definition: RtypesCore.h:59
const Bool_t kTRUE
Definition: RtypesCore.h:87
@ kIsStatic
Definition: TDictionary.h:79
@ kIsNamespace
Definition: TDictionary.h:91
_object PyObject
Definition: TPyArg.h:20
const char * proto
Definition: civetweb.c:16604
void AddMethod(PyCallable *pc)
Fill in the data of a freshly created method proxy.
Template proxy object to return functions and methods.
Definition: TemplateProxy.h:24
MethodProxy * fNonTemplated
Definition: TemplateProxy.h:33
void AddOverload(MethodProxy *mp)
Store overloads of this templated method.
void Set(const std::string &name, PyObject *pyclass)
Initialize the proxy for the given 'pyclass.'.
PyObject_HEAD PyObject * fSelf
Definition: TemplateProxy.h:30
void AddTemplate(PyCallable *pc)
MethodProxy * fTemplated
Definition: TemplateProxy.h:34
The ROOT global object gROOT contains a list of all defined classes.
Definition: TClass.h:75
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:4309
Long_t Property() const
Set TObject::fBits and fStreamerType to cache information about the class.
Definition: TClass.cxx:5800
TMethod * GetMethodAny(const char *method)
Return pointer to method without looking at parameters.
Definition: TClass.cxx:4227
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:2885
Long_t Property() const
Get property description word. For meaning of bits see EProperty.
Definition: TFunction.cxx:183
Each ROOT class (see TClass) has a linked list of methods.
Definition: TMethod.h:38
virtual const char * GetName() const
Returns name of object.
Definition: TNamed.h:47
ptrdiff_t TCppScope_t
Definition: Cppyy.h:15
TCppScope_t GetScope(const std::string &scope_name)
Definition: Cppyy.cxx:193
ptrdiff_t TCppMethod_t
Definition: Cppyy.h:18
R__EXTERN PyObject * gName
Definition: PyStrings.h:33
R__EXTERN PyObject * gTypeCode
Definition: PyStrings.h:36
R__EXTERN PyObject * gCppName
Definition: PyStrings.h:34
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
MethodProxy * MethodProxy_New(const std::string &name, std::vector< PyCallable * > &methods)
Definition: MethodProxy.h:75
Bool_t MethodProxy_Check(T *object)
Definition: MethodProxy.h:63
PyTypeObject TemplateProxy_Type
PyTypeObject MethodProxy_Type
static constexpr double pc