Logo ROOT   6.18/05
Reference Guide
RootWrapper.cxx
Go to the documentation of this file.
1// @(#)root/pyroot:$Id$
2// Author: Wim Lavrijsen, Apr 2004
3
4// Bindings
5#include "PyROOT.h"
6#include "PyStrings.h"
7#include "RootWrapper.h"
8#include "PyRootType.h"
9#include "ObjectProxy.h"
10#include "MethodProxy.h"
11#include "TemplateProxy.h"
12#include "PropertyProxy.h"
13#include "Pythonize.h"
14#include "TMethodHolder.h"
15#include "TConstructorHolder.h"
16#include "TClassMethodHolder.h"
17#include "TFunctionHolder.h"
18#include "TSetItemHolder.h"
19#include "TMemoryRegulator.h"
20#include "TTupleOfInstances.h"
21#include "Utility.h"
22
23// ROOT
24#include "TROOT.h"
25#include "TSystem.h"
26#include "TDataMember.h"
27#include "TClassEdit.h"
28#include "TEnum.h"
29#include "TEnumConstant.h"
30#include "TInterpreter.h"
31#include "TGlobal.h"
32#include "DllImport.h"
33#include "TFunctionTemplate.h"
34#include "TCollection.h"
35
36// Standard
37#include <map>
38#include <set>
39#include <string>
40#include <algorithm>
41#include <vector>
42
43//- FOR CLING WORKAROUND
44#include "TError.h"
45//
46
47
48//- data _______________________________________________________________________
49namespace PyROOT {
51
52// TODO: move this to Cppyy.cxx (if possible) (and gPinnedTypes should be a hashmap)
53 R__EXTERN std::vector<std::pair<Cppyy::TCppType_t, Cppyy::TCppType_t> > gPinnedTypes;
54 R__EXTERN std::vector<Cppyy::TCppType_t> gIgnorePinnings;
55}
56
57namespace {
58
59// to prevent having to walk scopes, track python classes by ROOT class
60 typedef std::map< Cppyy::TCppScope_t, PyObject* > PyClassMap_t;
61 PyClassMap_t gPyClasses;
62
63// helper for creating new ROOT python types
64 PyObject* CreateNewROOTPythonClass( const std::string& name, PyObject* pybases )
65 {
66 // Create a new python shadow class with the required hierarchy and meta-classes.
67 Py_XINCREF( pybases );
68 if ( ! pybases ) {
69 pybases = PyTuple_New( 1 );
70 Py_INCREF( (PyObject*)(void*)&PyROOT::ObjectProxy_Type );
71 PyTuple_SET_ITEM( pybases, 0, (PyObject*)(void*)&PyROOT::ObjectProxy_Type );
72 }
73
74 PyObject* pymetabases = PyTuple_New( PyTuple_GET_SIZE( pybases ) );
75 for ( int i = 0; i < PyTuple_GET_SIZE( pybases ); ++i ) {
76 PyObject* btype = (PyObject*)Py_TYPE( PyTuple_GetItem( pybases, i ) );
77 Py_INCREF( btype );
78 PyTuple_SET_ITEM( pymetabases, i, btype );
79 }
80
81 PyObject* args = Py_BuildValue( (char*)"sO{}", (name+"_meta").c_str(), pymetabases );
82 Py_DECREF( pymetabases );
83
84 PyObject* pymeta = PyType_Type.tp_new( &PyROOT::PyRootType_Type, args, NULL );
85 Py_DECREF( args );
86 if ( ! pymeta ) {
87 PyErr_Print();
88 Py_DECREF( pybases );
89 return 0;
90 }
91
92 args = Py_BuildValue( (char*)"sO{}", Cppyy::GetName(name).c_str(), pybases );
93 PyObject* pyclass = ((PyTypeObject*)pymeta)->tp_new( (PyTypeObject*)pymeta, args, NULL );
94 Py_DECREF( args );
95 Py_DECREF( pymeta );
96
97 Py_DECREF( pybases );
98
99 return pyclass;
100 }
101
102 inline void AddPropertyToClass1(
103 PyObject* pyclass, PyROOT::PropertyProxy* property, Bool_t isStatic )
104 {
105 // allow access at the instance level
106 PyObject_SetAttrString( pyclass,
107 const_cast< char* >( property->GetName().c_str() ), (PyObject*)property );
108
109 // allow access at the class level (always add after setting instance level)
110 if ( isStatic ) {
111 PyObject_SetAttrString( (PyObject*)Py_TYPE(pyclass),
112 const_cast< char* >( property->GetName().c_str() ), (PyObject*)property );
113 }
114 }
115
116 void AddPropertyToClass( PyObject* pyclass,
118 {
119 PyROOT::PropertyProxy* property = PyROOT::PropertyProxy_New( scope, idata );
120 AddPropertyToClass1( pyclass, property, Cppyy::IsStaticData( scope, idata ) );
121 Py_DECREF( property );
122 }
123
124 void AddConstantPropertyToClass( PyObject* pyclass,
125 Cppyy::TCppScope_t scope, const std::string& name, void* address, TEnum* en )
126 {
127 PyROOT::PropertyProxy* property =
128 PyROOT::PropertyProxy_NewConstant( scope, name, address, en );
129 AddPropertyToClass1( pyclass, property, kTRUE );
130 Py_DECREF( property );
131 }
132
133
134} // unnamed namespace
135
136
137//- helpers --------------------------------------------------------------------
138namespace {
139
140 using namespace PyROOT;
141
142 inline void AddToGlobalScope(
143 const char* label, const char* /* hdr */, TObject* obj, Cppyy::TCppType_t klass )
144 {
145 // Bind the given object with the given class in the global scope with the
146 // given label for its reference.
147 PyModule_AddObject( gRootModule, const_cast< char* >( label ),
148 PyROOT::BindCppObjectNoCast( obj, klass ) );
149 }
150
151 std::set< std::string > gSTLTypes, gSTLExceptions;
152 struct InitSTLTypes_t {
153 InitSTLTypes_t()
154 {
155 // Initialize the sets of known STL (container) types.
156 const std::string nss = "std::";
157
158 const char* stlTypes[] = { "complex", "exception",
159 "deque", "list", "queue", "stack", "vector",
160 "map", "multimap", "set", "multiset" };
161 for ( int i = 0; i < int(sizeof(stlTypes)/sizeof(stlTypes[0])); ++i ) {
162 gSTLTypes.insert( stlTypes[ i ] );
163 gSTLTypes.insert( nss + stlTypes[ i ] );
164 }
165
166 const char* stlExceptions[] = { "logic_error", "domain_error",
167 "invalid_argument", "length_error", "out_of_range", "runtime_error",
168 "range_error", "overflow_error", "underflow_error" };
169 for ( int i = 0; i < int(sizeof(stlExceptions)/sizeof(stlExceptions[0])); ++i ) {
170 gSTLExceptions.insert( stlExceptions[ i ] );
171 gSTLExceptions.insert( nss + stlExceptions[ i ] );
172 }
173 }
174 } initSTLTypes_;
175} // unnamed namespace
176
177
178//- public functions ---------------------------------------------------------
180{
181// setup interpreter locks to allow for threading in ROOT
182 PyEval_InitThreads();
183
184// memory management
185 static TMemoryRegulator m;
186 gROOT->GetListOfCleanups()->Add( &m );
187
188// bind ROOT globals that are needed in ROOT.py
189 AddToGlobalScope( "gROOT", "TROOT.h", gROOT, Cppyy::GetScope( gROOT->IsA()->GetName() ) );
190 AddToGlobalScope( "gSystem", "TSystem.h", gSystem, Cppyy::GetScope( gSystem->IsA()->GetName() ) );
191 AddToGlobalScope( "gInterpreter", "TInterpreter.h", gInterpreter, Cppyy::GetScope( gInterpreter->IsA()->GetName() ) );
192}
193
194////////////////////////////////////////////////////////////////////////////////
195/// Collect methods and data for the given scope, and add them to the given python
196/// proxy object.
197
198static int BuildScopeProxyDict( Cppyy::TCppScope_t scope, PyObject* pyclass ) {
199// some properties that'll affect building the dictionary
200 Bool_t isNamespace = Cppyy::IsNamespace( scope );
201 Bool_t hasConstructor = kFALSE;
202
203// load all public methods and data members
204 typedef std::vector< PyCallable* > Callables_t;
205 typedef std::map< std::string, Callables_t > CallableCache_t;
206 CallableCache_t cache;
207
208// bypass custom __getattr__ for efficiency
209 getattrofunc oldgetattro = Py_TYPE(pyclass)->tp_getattro;
210 Py_TYPE(pyclass)->tp_getattro = PyType_Type.tp_getattro;
211
212 // Add function templates that have not been instantiated to the class dictionary
213 auto cppClass = TClass::GetClass(Cppyy::GetFinalName(scope).c_str());
214 for (auto templ : ROOT::Detail::TRangeStaticCast<TFunctionTemplate>(cppClass->GetListOfFunctionTemplates())) {
215 if (templ->Property() & kIsPublic) { // Discard private templates
216 auto templName = templ->GetName();
217 auto attr = PyObject_GetAttrString(pyclass, templName);
218 if (!TemplateProxy_Check(attr)) {
219 auto templProxy = TemplateProxy_New(templName, pyclass);
220 PyObject_SetAttrString(pyclass, templName, (PyObject*)templProxy);
221 Py_DECREF(templProxy);
222 }
223 Py_XDECREF(attr);
224 }
225 }
226
227// functions in namespaces are properly found through lazy lookup, so do not
228// create them until needed (the same is not true for data members)
229 const Cppyy::TCppIndex_t nMethods =
230 Cppyy::IsNamespace( scope ) ? 0 : Cppyy::GetNumMethods( scope );
231 for ( Cppyy::TCppIndex_t imeth = 0; imeth < nMethods; ++imeth ) {
232 Cppyy::TCppMethod_t method = Cppyy::GetMethod( scope, imeth );
233
234 // process the method based on its name
235 std::string mtName = Cppyy::GetMethodName( method );
236
237 // special case trackers
238 Bool_t setupSetItem = kFALSE;
239 Bool_t isConstructor = Cppyy::IsConstructor( method );
240
241 // filter empty names (happens for namespaces, is bug?)
242 if ( mtName == "" )
243 continue;
244
245 // filter C++ destructors
246 if ( mtName[0] == '~' )
247 continue;
248
249 // translate operators
250 mtName = Utility::MapOperatorName( mtName, Cppyy::GetMethodNumArgs( method ) );
251
252 // operator[]/() returning a reference type will be used for __setitem__
253 if ( mtName == "__call__" || mtName == "__getitem__" ) {
254 const std::string& qual_return = Cppyy::ResolveName( Cppyy::GetMethodResultType( method ) );
255 if ( qual_return.find( "const", 0, 5 ) == std::string::npos ) {
256 const std::string& cpd = Utility::Compound( qual_return );
257 if ( ! cpd.empty() && cpd[ cpd.size() - 1 ] == '&' ) {
258 setupSetItem = kTRUE;
259 }
260 }
261 }
262
263 // decide on method type: member or static (which includes globals)
264 Bool_t isStatic = Cppyy::IsStaticMethod( method );
265
266 // template members; handled by adding a dispatcher to the class
267 std::string tmplName = "";
268 if ( ! (isNamespace || isStatic || isConstructor) && mtName[mtName.size()-1] == '>' ) {
269 tmplName = mtName.substr( 0, mtName.find('<') );
270 // TODO: the following is incorrect if both base and derived have the same
271 // templated method (but that is an unlikely scenario anyway)
272 PyObject* attr = PyObject_GetAttrString( pyclass, const_cast< char* >( tmplName.c_str() ) );
273 if ( ! TemplateProxy_Check( attr ) ) {
274 PyErr_Clear();
275 TemplateProxy* pytmpl = TemplateProxy_New( tmplName, pyclass );
276 if ( MethodProxy_Check( attr ) ) pytmpl->AddOverload( (MethodProxy*)attr );
277 PyObject_SetAttrString(
278 pyclass, const_cast< char* >( tmplName.c_str() ), (PyObject*)pytmpl );
279 Py_DECREF( pytmpl );
280 }
281 Py_XDECREF( attr );
282 // continue processing to actually add the method so that the proxy can find
283 // it on the class when called explicitly
284 }
285
286 // public methods are normally visible, private methods are mangled python-wise
287 // note the overload implications which are name based, and note that rootcint
288 // does not create the interface methods for private/protected methods ...
289 if ( ! Cppyy::IsPublicMethod( method ) ) {
290 if ( isConstructor ) // don't expose private ctors
291 continue;
292 else { // mangle private methods
293 const std::string& clName = TClassEdit::ShortType(
295 mtName = "_" + clName + "__" + mtName;
296 }
297 }
298
299 // construct the holder
300 PyCallable* pycall = 0;
301 if ( isStatic ) // class method
302 pycall = new TClassMethodHolder( scope, method );
303 else if ( isNamespace ) // free function
304 pycall = new TFunctionHolder( scope, method );
305 else if ( isConstructor ) { // constructor
306 pycall = new TConstructorHolder( scope, method );
307 mtName = "__init__";
308 hasConstructor = kTRUE;
309 } else // member function
310 pycall = new TMethodHolder( scope, method );
311
312 // lookup method dispatcher and store method
313 Callables_t& md = (*(cache.insert(
314 std::make_pair( mtName, Callables_t() ) ).first)).second;
315 md.push_back( pycall );
316
317 // special case for operator[]/() that returns by ref, use for getitem/call and setitem
318 if ( setupSetItem ) {
319 Callables_t& setitem = (*(cache.insert(
320 std::make_pair( std::string( "__setitem__" ), Callables_t() ) ).first)).second;
321 setitem.push_back( new TSetItemHolder( scope, method ) );
322 }
323
324 // special case for templates, add another call for the template name
325 if ( ! tmplName.empty() ) {
326 PyObject* attr = PyObject_GetAttrString( pyclass, const_cast< char* >( tmplName.c_str() ) );
327 ((TemplateProxy*)attr)->AddTemplate( pycall->Clone() );
328 Py_DECREF( attr );
329 }
330 }
331
332// add a pseudo-default ctor, if none defined
333 if ( ! isNamespace && ! hasConstructor )
334 cache[ "__init__" ].push_back( new TConstructorHolder( scope, (Cppyy::TCppMethod_t)0 ) );
335
336// add the methods to the class dictionary
337 for ( CallableCache_t::iterator imd = cache.begin(); imd != cache.end(); ++imd ) {
338 // in order to prevent removing templated editions of this method (which were set earlier,
339 // above, as a different proxy object), we'll check and add this method flagged as a generic
340 // one (to be picked up by the templated one as appropriate) if a template exists
341 PyObject* attr = PyObject_GetAttrString( pyclass, const_cast< char* >( imd->first.c_str() ) );
342 if ( TemplateProxy_Check( attr ) ) {
343 // template exists, supply it with the non-templated method overloads
344 for ( Callables_t::iterator cit = imd->second.begin(); cit != imd->second.end(); ++cit )
345 ((TemplateProxy*)attr)->AddOverload( *cit );
346 } else {
347 if ( ! attr ) PyErr_Clear();
348 // normal case, add a new method
349 MethodProxy* method = MethodProxy_New( imd->first, imd->second );
350 PyObject_SetAttrString(
351 pyclass, const_cast< char* >( method->GetName().c_str() ), (PyObject*)method );
352 Py_DECREF( method );
353 }
354
355 Py_XDECREF( attr ); // could have be found in base class or non-existent
356 }
357
358// collect enums; this must happen before data members, so that we can check on their existence
359 TClass* klass = TClass::GetClass( Cppyy::GetFinalName( scope ).c_str() );
360 TList* enums = klass->GetListOfEnums();
361 TIter ienum( enums );
362 TEnum* e = 0;
363 while ( (e = (TEnum*)ienum.Next()) ) {
364 const TSeqCollection* seq = e->GetConstants();
365 auto isScoped = e->Property() & kIsScopedEnum;
366 if (isScoped) continue; // scoped enum: do not add constants as properties of the enum's scope
367 for ( Int_t i = 0; i < seq->GetSize(); i++ ) {
368 TEnumConstant* ec = (TEnumConstant*)seq->At( i );
369 AddConstantPropertyToClass( pyclass, scope, ec->GetName(), ec->GetAddress(), e );
370 }
371 }
372
373// collect data members
374 const Cppyy::TCppIndex_t nDataMembers = Cppyy::GetNumDatamembers( scope );
375 for ( Cppyy::TCppIndex_t idata = 0; idata < nDataMembers; ++idata ) {
376 // allow only public members
377 if ( ! Cppyy::IsPublicData( scope, idata ) )
378 continue;
379
380 // enum datamembers (this in conjunction with previously collected enums above)
381 if ( Cppyy::IsEnumData( scope, idata ) && Cppyy::IsStaticData( scope, idata ) ) {
382 // some implementation-specific data members have no address: ignore them
383 if ( ! Cppyy::GetDatamemberOffset( scope, idata ) )
384 continue;
385
386 // two options: this is a static variable, or it is the enum value, the latter
387 // already exists, so check for it and move on if set
388 PyObject* eset = PyObject_GetAttrString( pyclass,
389 const_cast<char*>( Cppyy::GetDatamemberName( scope, idata ).c_str()) );
390 if ( eset ) {
391 Py_DECREF( eset );
392 continue;
393 }
394
395 PyErr_Clear();
396
397 // it could still be that this is an anonymous enum, which is not in the list
398 // provided by the class
399 if ( strstr( Cppyy::GetDatamemberType( scope, idata ).c_str(), "(anonymous)" ) != 0 ) {
400 AddPropertyToClass( pyclass, scope, idata );
401 continue;
402 }
403 }
404
405 // properties (aka public (static) data members)
406 AddPropertyToClass( pyclass, scope, idata );
407 }
408
409// restore custom __getattr__
410 Py_TYPE(pyclass)->tp_getattro = oldgetattro;
411
412// all ok, done
413 return 0;
414}
415
416////////////////////////////////////////////////////////////////////////////////
417/// Build a tuple of python shadow classes of all the bases of the given 'klass'.
418
420{
421 size_t nbases = Cppyy::GetNumBases( klass );
422
423// collect bases while removing duplicates
424 std::vector< std::string > uqb;
425 uqb.reserve( nbases );
426
427 for ( size_t ibase = 0; ibase < nbases; ++ibase ) {
428 const std::string& name = Cppyy::GetBaseName( klass, ibase );
429 if ( std::find( uqb.begin(), uqb.end(), name ) == uqb.end() ) {
430 uqb.push_back( name );
431 }
432 }
433
434// allocate a tuple for the base classes, special case for first base
435 nbases = uqb.size();
436
437 PyObject* pybases = PyTuple_New( nbases ? nbases : 1 );
438 if ( ! pybases )
439 return 0;
440
441// build all the bases
442 if ( nbases == 0 ) {
443 Py_INCREF( (PyObject*)(void*)&ObjectProxy_Type );
444 PyTuple_SET_ITEM( pybases, 0, (PyObject*)(void*)&ObjectProxy_Type );
445 } else {
446 for ( std::vector< std::string >::size_type ibase = 0; ibase < nbases; ++ibase ) {
447 PyObject* pyclass = CreateScopeProxy( uqb[ ibase ] );
448 if ( ! pyclass ) {
449 Py_DECREF( pybases );
450 return 0;
451 }
452
453 PyTuple_SET_ITEM( pybases, ibase, pyclass );
454 }
455
456 // special case, if true python types enter the hierarchy, make sure that
457 // the first base seen is still the ObjectProxy_Type
458 if ( ! PyObject_IsSubclass( PyTuple_GET_ITEM( pybases, 0 ), (PyObject*)&ObjectProxy_Type ) ) {
459 PyObject* newpybases = PyTuple_New( nbases + 1 );
460 Py_INCREF( (PyObject*)(void*)&ObjectProxy_Type );
461 PyTuple_SET_ITEM( newpybases, 0, (PyObject*)(void*)&ObjectProxy_Type );
462 for ( int ibase = 0; ibase < (int)nbases; ++ibase ) {
463 PyObject* pyclass = PyTuple_GET_ITEM( pybases, ibase );
464 Py_INCREF( pyclass );
465 PyTuple_SET_ITEM( newpybases, ibase + 1, pyclass );
466 }
467 Py_DECREF( pybases );
468 pybases = newpybases;
469 }
470 }
471
472 return pybases;
473}
474
475////////////////////////////////////////////////////////////////////////////////
476/// Retrieve scope proxy from the known ones.
477
479{
480 PyClassMap_t::iterator pci = gPyClasses.find( scope );
481 if ( pci != gPyClasses.end() ) {
482 PyObject* pyclass = PyWeakref_GetObject( pci->second );
483 if ( pyclass ) {
484 Py_INCREF( pyclass );
485 return pyclass;
486 }
487 }
488
489 return nullptr;
490}
491
492////////////////////////////////////////////////////////////////////////////////
493/// Convenience function with a lookup first through the known existing proxies.
494
496{
497 PyObject* pyclass = GetScopeProxy( scope );
498 if ( pyclass )
499 return pyclass;
500
502}
503
504////////////////////////////////////////////////////////////////////////////////
505/// Build a python shadow class for the named C++ class.
506
508{
509 std::string cname = PyROOT_PyUnicode_AsString( PyTuple_GetItem( args, 0 ) );
510 if ( PyErr_Occurred() )
511 return nullptr;
512
513 return CreateScopeProxy( cname );
514}
515
516////////////////////////////////////////////////////////////////////////////////
517/// Build a python shadow class for the named C++ class.
518
519PyObject* PyROOT::CreateScopeProxy( const std::string& scope_name, PyObject* parent )
520{
521 if ( scope_name.empty() || scope_name == "std" ) {
522 // special cases, as gbl and gbl.std are defined in cppyy.py
523 PyObject* mods = PyImport_GetModuleDict();
524 PyObject* gbl = PyDict_GetItemString( mods, "cppyy.gbl" );
525 if ( gbl ) {
526 if ( scope_name.empty() ) {
527 Py_INCREF( gbl );
528 return gbl;
529 } else
530 return PyObject_GetAttrString( gbl, "std" );
531 }
532 PyErr_SetString( PyExc_SystemError, "could not locate global namespace" );
533 return nullptr;
534 }
535
536// force building of the class if a parent is specified (prevents loops)
537 Bool_t force = parent != 0;
538
539// working copy
540 std::string name = scope_name;
541
542// determine complete scope name, if a python parent has been given
543 std::string scName = "";
544 if ( parent ) {
545 PyObject* pyparent = PyObject_GetAttr( parent, PyStrings::gCppName );
546 if ( ! pyparent ) {
547 PyErr_Clear();
548 pyparent = PyObject_GetAttr( parent, PyStrings::gName );
549 }
550 if ( ! pyparent ) {
551 PyErr_Format( PyExc_SystemError, "given scope has no name for %s", name.c_str() );
552 return 0;
553 }
554
555 // should be a string
556 scName = PyROOT_PyUnicode_AsString( pyparent );
557 Py_DECREF( pyparent );
558 if ( PyErr_Occurred() )
559 return 0;
560
561 // accept this parent scope and use it's name for prefixing
562 Py_INCREF( parent );
563 }
564
565// retrieve ROOT class (this verifies name, and is therefore done first)
566 const std::string& lookup = parent ? (scName+"::"+name) : name;
567 Cppyy::TCppScope_t klass = Cppyy::GetScope( lookup );
568
569 if ( ! (Bool_t)klass && gInterpreter->CheckClassTemplate( lookup.c_str() ) ) {
570 // a "naked" templated class is requested: return callable proxy for instantiations
571 PyObject* pytcl = PyObject_GetAttr( gRootModule, PyStrings::gTemplate );
572 PyObject* pytemplate = PyObject_CallFunction(
573 pytcl, const_cast< char* >( "s" ), const_cast< char* >( lookup.c_str() ) );
574 Py_DECREF( pytcl );
575
576 // cache the result
577 PyObject_SetAttrString( parent ? parent : gRootModule, (char*)name.c_str(), pytemplate );
578
579 // done, next step should be a call into this template
580 Py_XDECREF( parent );
581 return pytemplate;
582 }
583
584 if ( ! (Bool_t)klass ) { // if so, all options have been exhausted: it doesn't exist as such
585 if ( ! parent && scope_name.find( "ROOT::" ) == std::string::npos ) { // not already in ROOT::
586 // final attempt, for convenience, the "ROOT" namespace isn't required, try again ...
587 klass = Cppyy::GetScope( "ROOT::"+scope_name );
588 if ( (Bool_t)klass ) {
589 PyObject* rtns = PyObject_GetAttr( gRootModule, PyStrings::gROOTns );
590 PyObject* pyclass = CreateScopeProxy( scope_name, rtns );
591 Py_DECREF( rtns );
592 return pyclass;
593 }
594 }
595
596 PyErr_Format( PyExc_TypeError, "requested class \'%s\' does not exist", lookup.c_str() );
597 Py_XDECREF( parent );
598 return 0;
599 }
600
601// locate class by ID, if possible, to prevent parsing scopes/templates anew
602 PyObject* pyscope = GetScopeProxy( klass );
603 if ( pyscope ) {
604 if ( parent ) PyObject_SetAttrString( parent, (char*)scope_name.c_str(), pyscope );
605 return pyscope;
606 }
607
608// locate the parent, if necessary, for building the class if not specified
609 std::string::size_type last = 0;
610 if ( ! parent ) {
611 // need to deal with template paremeters that can have scopes themselves
612 Int_t tpl_open = 0;
613 for ( std::string::size_type pos = 0; pos < name.size(); ++pos ) {
614 std::string::value_type c = name[ pos ];
615
616 // count '<' and '>' to be able to skip template contents
617 if ( c == '<' )
618 ++tpl_open;
619 else if ( c == '>' )
620 --tpl_open;
621
622 // by only checking for "::" the last part (class name) is dropped
623 else if ( tpl_open == 0 &&\
624 c == ':' && pos+1 < name.size() && name[ pos+1 ] == ':' ) {
625 // found a new scope part
626 const std::string& part = name.substr( last, pos-last );
627
628 PyObject* next = PyObject_GetAttrString(
629 parent ? parent : gRootModule, const_cast< char* >( part.c_str() ) );
630
631 if ( ! next ) { // lookup failed, try to create it
632 PyErr_Clear();
633 next = CreateScopeProxy( part, parent );
634 }
635 Py_XDECREF( parent );
636
637 if ( ! next ) // create failed, give up
638 return 0;
639
640 // found scope part
641 parent = next;
642
643 // done with part (note that pos is moved one ahead here)
644 last = pos+2; ++pos;
645 }
646
647 }
648
649 if ( parent && !PyRootType_Check( parent ) ) {
650 // Special case: parent found is not one of ours (it's e.g. a pure Python module), so
651 // continuing would fail badly. One final lookup, then out of here ...
652 std::string unscoped = scope_name.substr( last, std::string::npos );
653 return PyObject_GetAttrString( parent, unscoped.c_str() );
654 }
655 }
656
657// use global scope if no inner scope found
658 if ( ! parent ) {
659 parent = gRootModule;
660 Py_INCREF( parent );
661 }
662
663// use actual class name for binding
664 const std::string& actual = Cppyy::GetFinalName( klass );
665
666// first try to retrieve an existing class representation
667 PyObject* pyactual = PyROOT_PyUnicode_FromString( Cppyy::GetName(actual).c_str() );
668 PyObject* pyclass = force ? 0 : PyObject_GetAttr( parent, pyactual );
669
670 Bool_t bClassFound = pyclass ? kTRUE : kFALSE;
671
672// build if the class does not yet exist
673 if ( ! pyclass ) {
674 // ignore error generated from the failed lookup
675 PyErr_Clear();
676
677 // construct the base classes
678 PyObject* pybases = BuildCppClassBases( klass );
679 if ( pybases != 0 ) {
680 // create a fresh Python class, given bases, name, and empty dictionary
681 pyclass = CreateNewROOTPythonClass( actual, pybases );
682 Py_DECREF( pybases );
683 }
684
685 // fill the dictionary, if successful
686 if ( pyclass != 0 ) {
687 if ( BuildScopeProxyDict( klass, pyclass ) != 0 ) {
688 // something failed in building the dictionary
689 Py_DECREF( pyclass );
690 pyclass = 0;
691 } else {
692 PyObject_SetAttr( parent, pyactual, pyclass );
693 }
694 }
695
696 }
697
698 if ( pyclass && name != actual ) // class exists, but is typedef-ed: simply map reference
699 PyObject_SetAttrString( parent, const_cast< char* >( name.c_str() ), pyclass );
700
701 if ( pyclass && ! bClassFound ) {
702 // store a ref from ROOT TClass to new python class
703 gPyClasses[ klass ] = PyWeakref_NewRef( pyclass, NULL );
704
705 // add a ref in the class to its scope
706 PyObject_SetAttrString( pyclass, "__scope__", PyROOT_PyUnicode_FromString( scName.c_str() ) );
707 }
708
709 // add __cppname__ to keep the C++ name of the class/scope
710 PyObject_SetAttr( pyclass, PyStrings::gCppName, PyROOT_PyUnicode_FromString( actual.c_str() ) );
711
712 // add __module__ (see https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_name)
713 std::string module;
714 if( parent == gRootModule) {
715 module = "ROOT";
716 } else {
717 PyObject* _name_ = PyObject_GetAttr(parent, PyStrings::gName);
718 PyObject* _module_ = PyObject_GetAttr(parent, PyStrings::gModule);
719 if(_module_) {
720 module = PyROOT_PyUnicode_AsString(_module_);
721 module += ".";
722 Py_DECREF(_module_);
723 }
724 if(_name_) {
725 module += PyROOT_PyUnicode_AsString(_name_);
726 Py_DECREF(_name_);
727 }
728 }
729 PyObject_SetAttr( pyclass, PyStrings::gModule, PyROOT_PyUnicode_FromString( module.c_str()) );
730
731 Py_DECREF( pyactual );
732 Py_DECREF( parent );
733
734
735 if ( ! bClassFound ) { // add python-style features to newly minted classes
736 if ( ! Pythonize( pyclass, actual ) ) {
737 Py_XDECREF( pyclass );
738 pyclass = 0;
739 }
740 }
741
742
743 if ( pyclass && actual != "ROOT" ) {
744 // add to sys.modules to allow importing from this module
745 std::string pyfullname = lookup;
746 std::string::size_type pos = pyfullname.find( "::" );
747 while ( pos != std::string::npos ) {
748 pyfullname = pyfullname.replace( pos, 2, "." );
749 pos = pyfullname.find( "::", pos );
750 }
751 PyObject* modules = PySys_GetObject( const_cast<char*>("modules") );
752 if ( modules && PyDict_Check( modules) ) {
753 PyDict_SetItemString( modules,
754 const_cast<char*>(("ROOT."+pyfullname).c_str()), pyclass );
755 }
756 }
757
758// all done
759 return pyclass;
760}
761
762////////////////////////////////////////////////////////////////////////////////
763/// get the requested name
764
766{
767 std::string ename = PyROOT_PyUnicode_AsString( PyTuple_GetItem( args, 0 ) );
768
769 if ( PyErr_Occurred() )
770 return 0;
771
772 return GetCppGlobal( ename );
773}
774
775////////////////////////////////////////////////////////////////////////////////
776/// try named global variable/enum (first ROOT, then Cling: sync is too slow)
777
778PyObject* PyROOT::GetCppGlobal( const std::string& name )
779{
781 if ( 0 <= idata )
783
784// still here ... try functions (sync has been fixed, so is okay)
785 const std::vector< Cppyy::TCppMethod_t >& methods =
787 if ( ! methods.empty() ) {
788 std::vector< PyCallable* > overloads;
789 for ( auto method : methods )
790 overloads.push_back( new TFunctionHolder( Cppyy::gGlobalScope, method ) );
791 return (PyObject*)MethodProxy_New( name, overloads );
792 }
793
794 // Try function templates
797 }
798
799// allow lookup into std as if global (historic)
800 TDataMember* dm = TClass::GetClass( "std" )->GetDataMember( name.c_str() );
801 if ( dm ) {
803 return BindCppObjectNoCast( (void*)dm->GetOffset(), klass, kFALSE );
804 }
805
806// nothing found
807 PyErr_Format( PyExc_LookupError, "no such global: %s", name.c_str() );
808 return 0;
809}
810
811////////////////////////////////////////////////////////////////////////////////
812/// only known or knowable objects will be bound (null object is ok)
813
815 Cppyy::TCppObject_t address, Cppyy::TCppType_t klass, Bool_t isRef, Bool_t isValue ) {
816 if ( ! klass ) {
817 PyErr_SetString( PyExc_TypeError, "attempt to bind ROOT object w/o class" );
818 return 0;
819 }
820
821// retrieve python class
822 PyObject* pyclass = CreateScopeProxy( klass );
823 if ( ! pyclass )
824 return 0; // error has been set in CreateScopeProxy
825
826// instantiate an object of this class
827 PyObject* args = PyTuple_New(0);
828 ObjectProxy* pyobj =
829 (ObjectProxy*)((PyTypeObject*)pyclass)->tp_new( (PyTypeObject*)pyclass, args, NULL );
830 Py_DECREF( args );
831 Py_DECREF( pyclass );
832
833// bind, register and return if successful
834 if ( pyobj != 0 ) { // fill proxy value?
835 // TODO: take flags directly instead of separate Bool_t args
836 unsigned flags = (isRef ? ObjectProxy::kIsReference : 0) | (isValue ? ObjectProxy::kIsValue : 0);
837 pyobj->Set( address, (ObjectProxy::EFlags)flags );
838 }
839
840// successful completion
841 return (PyObject*)pyobj;
842}
843
844////////////////////////////////////////////////////////////////////////////////
845/// if the object is a null pointer, return a typed one (as needed for overloading)
846
848{
849 if ( ! address )
850 return BindCppObjectNoCast( address, klass, kFALSE );
851
852// only known or knowable objects will be bound
853 if ( ! klass ) {
854 PyErr_SetString( PyExc_TypeError, "attempt to bind ROOT object w/o class" );
855 return 0;
856 }
857
858// get actual class for recycling checking and/or downcasting
859// CLING WORKAROUND -- silence:
860// Error in <TStreamerInfo::Build>: __gnu_cxx::__normal_iterator<int*,vector<int> >, discarding: int* _M_current, no [dimension]
861 Int_t oldval = gErrorIgnoreLevel;
862 gErrorIgnoreLevel = 5000;
863 Cppyy::TCppType_t clActual = isRef ? 0 : Cppyy::GetActualClass( klass, address );
864 gErrorIgnoreLevel = oldval;
865
866// obtain pointer to TObject base class (if possible) for memory mgmt; this is
867// done before downcasting, as upcasting from the current class may be easier and
868// downcasting is unnecessary if the python side object gets recycled by the
869// memory regulator
870 TObject* object = 0;
871 static Cppyy::TCppScope_t sTObjectScope = Cppyy::GetScope( "TObject" );
872 if ( ! isRef && Cppyy::IsSubtype( klass, sTObjectScope) ) {
873 object = (TObject*)((Long_t)address + \
874 Cppyy::GetBaseOffset( klass, sTObjectScope, address, 1 /* up-cast */ ) );
875
876 // use the old reference if the object already exists
877 PyObject* oldPyObject = TMemoryRegulator::RetrieveObject( object, clActual ? clActual : klass );
878 if ( oldPyObject )
879 return oldPyObject;
880 }
881
882// downcast to real class for object returns
883 if ( clActual && klass != clActual ) {
884 ptrdiff_t offset = Cppyy::GetBaseOffset(
885 clActual, klass, address, -1 /* down-cast */, true /* report errors */ );
886 if ( offset != -1 ) { // may fail if clActual not fully defined
887 address = (void*)((Long_t)address + offset);
888 klass = clActual;
889 }
890 }
891
892
893// check if type is pinned
894 Bool_t ignore_pin = std::find(
895 gIgnorePinnings.begin(), gIgnorePinnings.end(), klass ) != gIgnorePinnings.end();
896
897 if ( ! ignore_pin ) {
898 for ( auto it = gPinnedTypes.cbegin(); it != gPinnedTypes.cend(); ++it ) {
899 if ( klass == std::get<0>(*it) || Cppyy::IsSubtype( klass, std::get<0>(*it) ) )
900 klass = std::get<1>(*it);
901 }
902 }
903
904// actual binding
905 ObjectProxy* pyobj = (ObjectProxy*)BindCppObjectNoCast( address, klass, isRef );
906
907// memory management, for TObject's only (for referenced objects, it is assumed
908// that the (typically global) reference itself is zeroed out (or replaced) on
909// destruction; it can't thus be reliably zeroed out from the python side)
910 if ( object && !(pyobj->fFlags & ObjectProxy::kIsReference) ) {
911 TMemoryRegulator::RegisterObject( pyobj, object );
912 }
913
914// completion (returned object may be zero w/ a python exception set)
915 return (PyObject*)pyobj;
916}
917
918////////////////////////////////////////////////////////////////////////////////
919/// TODO: this function exists for symmetry; need to figure out if it's useful
920
922 Cppyy::TCppObject_t address, Cppyy::TCppType_t klass, Int_t size ) {
923 return TTupleOfInstances_New( address, klass, size );
924}
925
926
927////////////////////////////////////////////////////////////////////////////////
928/// gbl == 0 means global does not exist (rather than gbl is NULL pointer)
929
931{
932 if ( ! gbl || strcmp(gbl->GetName(), "") == 0 ) {
933 Py_INCREF( Py_None );
934 return Py_None;
935 }
936
937// determine type and cast as appropriate
939 if ( klass != 0 ) {
940 // handle array of objects
941 if ( gbl->GetArrayDim() == 1 ) {
942 return BindCppObjectArray( (void*)gbl->GetAddress(), klass, gbl->GetMaxIndex(0) );
943 } else if ( gbl->GetArrayDim() ) {
944 PyErr_SetString( PyExc_NotImplementedError,
945 "larger than 1D arrays of objects not supported" );
946 return 0;
947 }
948
949 // special case where there should be no casting:
950 // TODO: WORK HERE ... restore cast
951 //if ( klass->InheritsFrom( "ios_base" ) )
952 //return BindCppObjectNoCast( (void*)gbl->GetAddress(), klass );
953
954 // pointer types are bound "by-reference"
955 if ( Utility::Compound( gbl->GetFullTypeName() ) != "" )
956 return BindCppObject( (void*)gbl->GetAddress(), klass, kTRUE );
957 }
958
959 if ( gbl->GetAddress() && // check for enums and consts
960 (unsigned long)gbl->GetAddress() != (unsigned long)-1 && // Cling (??)
961 ( gInterpreter->ClassInfo_IsEnum( gbl->GetTypeName() ) ) ) {
962 return PyInt_FromLong( (long)*((int*)gbl->GetAddress()) );
963 }
964
965// no class and no enum: for built-in types, to ensure setability
968 return result;
969}
#define R__EXTERN
Definition: DllImport.h:27
#define Py_TYPE(ob)
Definition: PyROOT.h:161
#define PyROOT_PyUnicode_AsString
Definition: PyROOT.h:78
#define PyROOT_PyUnicode_FromString
Definition: PyROOT.h:82
#define c(i)
Definition: RSha256.hxx:101
#define e(i)
Definition: RSha256.hxx:103
static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject *pyclass)
Collect methods and data for the given scope, and add them to the given python proxy object.
static PyObject * BuildCppClassBases(Cppyy::TCppType_t klass)
Build a tuple of python shadow classes of all the bases of the given 'klass'.
int Int_t
Definition: RtypesCore.h:41
const Bool_t kFALSE
Definition: RtypesCore.h:88
long Long_t
Definition: RtypesCore.h:50
bool Bool_t
Definition: RtypesCore.h:59
const Bool_t kTRUE
Definition: RtypesCore.h:87
@ kIsPublic
Definition: TDictionary.h:74
@ kIsScopedEnum
Definition: TDictionary.h:89
@ kIsReference
Definition: TDictionary.h:81
R__EXTERN Int_t gErrorIgnoreLevel
Definition: TError.h:105
char name[80]
Definition: TGX11.cxx:109
#define gInterpreter
Definition: TInterpreter.h:553
_object PyObject
Definition: TPyArg.h:20
#define gROOT
Definition: TROOT.h:414
R__EXTERN TSystem * gSystem
Definition: TSystem.h:560
const std::string & GetName() const
Definition: MethodProxy.h:45
void Set(void *address, EFlags flags=kNone)
Definition: ObjectProxy.h:33
std::string GetName()
Definition: PropertyProxy.h:30
virtual PyCallable * Clone()=0
Template proxy object to return functions and methods.
Definition: TemplateProxy.h:24
void AddOverload(MethodProxy *mp)
Store overloads of this templated method.
TRangeStaticCast is an adaptater class that allows the typed iteration through a TCollection.
Definition: TCollection.h:382
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition: TClass.h:75
TDataMember * GetDataMember(const char *datamember) const
Return pointer to datamember object with name "datamember".
Definition: TClass.cxx:3288
TList * GetListOfEnums(Bool_t load=kTRUE)
Return a list containing the TEnums of a class.
Definition: TClass.cxx:3579
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:2895
virtual Int_t GetSize() const
Return the capacity of the collection, i.e.
Definition: TCollection.h:182
All ROOT classes may have RTTI (run time type identification) support added.
Definition: TDataMember.h:31
const char * GetTrueTypeName() const
Get full type description of data member, e,g.: "class TDirectory*".
Long_t GetOffset() const
Get offset from "this".
The TEnumConstant class implements the constants of the enum type.
Definition: TEnumConstant.h:29
void * GetAddress() const override
Return address of global.
Definition: TEnumConstant.h:39
The TEnum class implements the enum type.
Definition: TEnum.h:33
Global variables class (global variables are obtained from CINT).
Definition: TGlobal.h:28
virtual Int_t GetArrayDim() const
Return number of array dimensions.
Definition: TGlobal.cxx:85
virtual const char * GetTypeName() const
Get type of global variable, e,g.
Definition: TGlobal.cxx:111
virtual Int_t GetMaxIndex(Int_t dim) const
Return maximum index for array dimension "dim".
Definition: TGlobal.cxx:101
virtual void * GetAddress() const
Return address of global.
Definition: TGlobal.cxx:77
virtual const char * GetFullTypeName() const
Get full type description of global variable, e,g.: "class TDirectory*".
Definition: TGlobal.cxx:120
TObject * Next()
Definition: TCollection.h:249
A doubly linked list.
Definition: TList.h:44
virtual const char * GetName() const
Returns name of object.
Definition: TNamed.h:47
Mother of all ROOT objects.
Definition: TObject.h:37
Sequenceable collection abstract base class.
virtual TObject * At(Int_t idx) const =0
ptrdiff_t GetBaseOffset(TCppType_t derived, TCppType_t base, TCppObject_t address, int direction, bool rerror=false)
Definition: Cppyy.cxx:640
Bool_t IsConstructor(TCppMethod_t method)
Definition: Cppyy.cxx:895
Bool_t IsPublicMethod(TCppMethod_t method)
Definition: Cppyy.cxx:904
ptrdiff_t TCppScope_t
Definition: Cppyy.h:15
Bool_t IsNamespace(TCppScope_t scope)
Definition: Cppyy.cxx:556
bool ExistsMethodTemplate(TCppScope_t scope, const std::string &name)
Definition: Cppyy.cxx:839
std::string GetMethodName(TCppMethod_t)
Definition: Cppyy.cxx:753
TCppIndex_t GetNumMethods(TCppScope_t scope)
Definition: Cppyy.cxx:679
TCppIndex_t GetNumDatamembers(TCppScope_t scope)
Definition: Cppyy.cxx:923
Bool_t IsPublicData(TCppScope_t scope, TCppIndex_t idata)
Definition: Cppyy.cxx:1033
std::string GetName(const std::string &scope_name)
Definition: Cppyy.cxx:146
TCppScope_t gGlobalScope
Definition: Cppyy.cxx:64
std::string ResolveName(const std::string &cppitem_name)
Definition: Cppyy.cxx:167
TCppType_t GetActualClass(TCppType_t klass, TCppObject_t obj)
Definition: Cppyy.cxx:224
TCppScope_t TCppType_t
Definition: Cppyy.h:16
std::string GetBaseName(TCppType_t type, TCppIndex_t ibase)
Definition: Cppyy.cxx:612
std::string GetScopedFinalName(TCppType_t type)
Definition: Cppyy.cxx:590
Bool_t IsEnumData(TCppScope_t scope, TCppIndex_t idata)
Definition: Cppyy.cxx:1069
Bool_t IsStaticData(TCppScope_t scope, TCppIndex_t idata)
Definition: Cppyy.cxx:1044
TCppIndex_t GetMethodNumArgs(TCppMethod_t)
Definition: Cppyy.cxx:775
TCppIndex_t GetDatamemberIndex(TCppScope_t scope, const std::string &name)
Definition: Cppyy.cxx:1009
Long_t TCppIndex_t
Definition: Cppyy.h:21
std::string GetDatamemberType(TCppScope_t scope, TCppIndex_t idata)
Definition: Cppyy.cxx:958
TCppScope_t GetScope(const std::string &scope_name)
Definition: Cppyy.cxx:193
Bool_t IsStaticMethod(TCppMethod_t method)
Definition: Cppyy.cxx:913
std::vector< TCppMethod_t > GetMethodsFromName(TCppScope_t scope, const std::string &name, bool alsoInBases=false)
Definition: Cppyy.cxx:710
ptrdiff_t GetDatamemberOffset(TCppScope_t scope, TCppIndex_t idata)
Definition: Cppyy.cxx:993
ptrdiff_t TCppMethod_t
Definition: Cppyy.h:18
std::string GetMethodResultType(TCppMethod_t)
Definition: Cppyy.cxx:764
std::string GetFinalName(TCppType_t type)
Definition: Cppyy.cxx:581
TCppIndex_t GetNumBases(TCppType_t type)
Definition: Cppyy.cxx:603
TCppMethod_t GetMethod(TCppScope_t scope, TCppIndex_t imeth)
Definition: Cppyy.cxx:747
std::string GetDatamemberName(TCppScope_t scope, TCppIndex_t idata)
Definition: Cppyy.cxx:946
void * TCppObject_t
Definition: Cppyy.h:17
Bool_t IsSubtype(TCppType_t derived, TCppType_t base)
Definition: Cppyy.cxx:618
R__EXTERN PyObject * gName
Definition: PyStrings.h:33
R__EXTERN PyObject * gROOTns
Definition: PyStrings.h:56
R__EXTERN PyObject * gTemplate
Definition: PyStrings.h:51
R__EXTERN PyObject * gModule
Definition: PyStrings.h:31
R__EXTERN PyObject * gCppName
Definition: PyStrings.h:34
std::string MapOperatorName(const std::string &name, Bool_t bTakesParames)
Map the given C++ operator name on the python equivalent.
Definition: Utility.cxx:634
const std::string Compound(const std::string &name)
Break down the compound of a fully qualified type name.
Definition: Utility.cxx:680
PyObject * BindCppObject(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, Bool_t isRef=kFALSE)
if the object is a null pointer, return a typed one (as needed for overloading)
PyObject * GetScopeProxy(Cppyy::TCppScope_t)
Retrieve scope proxy from the known ones.
PyTypeObject PyRootType_Type
Definition: PyRootType.cxx:231
PropertyProxy * PropertyProxy_NewConstant(Cppyy::TCppScope_t scope, const std::string &name, void *address, TEnum *en)
Definition: PropertyProxy.h:72
PyObject * BindCppGlobal(TGlobal *)
gbl == 0 means global does not exist (rather than gbl is NULL pointer)
Bool_t PyRootType_Check(T *object)
Definition: PyRootType.h:50
void InitRoot()
PyObject * BindCppObjectArray(Cppyy::TCppObject_t address, Cppyy::TCppType_t klass, Int_t size)
TODO: this function exists for symmetry; need to figure out if it's useful.
PyObject * TTupleOfInstances_New(Cppyy::TCppObject_t address, Cppyy::TCppType_t klass, Py_ssize_t nelems)
Bool_t TemplateProxy_Check(T *object)
Definition: TemplateProxy.h:50
PyTypeObject ObjectProxy_Type
TemplateProxy * TemplateProxy_New(const std::string &name, PyObject *pyclass)
Definition: TemplateProxy.h:62
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
PyObject * CreateScopeProxy(Cppyy::TCppScope_t)
Convenience function with a lookup first through the known existing proxies.
PropertyProxy * PropertyProxy_New(Cppyy::TCppScope_t scope, Cppyy::TCppIndex_t idata)
Definition: PropertyProxy.h:62
std::vector< Cppyy::TCppType_t > gIgnorePinnings
Definition: RootModule.cxx:163
std::vector< std::pair< Cppyy::TCppType_t, Cppyy::TCppType_t > > gPinnedTypes
Definition: RootModule.cxx:162
R__EXTERN PyObject * gRootModule
Definition: ObjectProxy.cxx:39
PyObject * GetCppGlobal(const std::string &name)
try named global variable/enum (first ROOT, then Cling: sync is too slow)
PyObject * BindCppObjectNoCast(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, Bool_t isRef=kFALSE, Bool_t isValue=kFALSE)
only known or knowable objects will be bound (null object is ok)
Bool_t Pythonize(PyObject *pyclass, const std::string &name)
Definition: Pythonize.cxx:2377
std::string ShortType(const char *typeDesc, int mode)
Return the absolute type of typeDesc.
auto * m
Definition: textangle.C:8