Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
ProxyWrappers.cxx
Go to the documentation of this file.
1// Bindings
2#include "cpyrt.h"
3
4using namespace cppjit;
5#include "CPPClassMethod.h"
6#include "CPPConstructor.h"
7#include "CPPDataMember.h"
8#include "CPPExcInstance.h"
9#include "CPPFunction.h"
10#include "CPPGetSetItem.h"
11#include "CPPInstance.h"
12#include "CPPMethod.h"
13#include "CPPOperator.h"
14#include "CPPOverload.h"
15#include "CPPScope.h"
16#include "MemoryRegulator.h"
17#include "ProxyWrappers.h"
18#include "PyStrings.h"
19#include "Pythonize.h"
20#include "TemplateProxy.h"
21#include "TupleOfInstances.h"
22#include "TypeManip.h"
23#include "Utility.h"
24#include "cppjit_interop.h"
25
26// Standard
27#include <algorithm>
28#include <deque>
29#include <map>
30#include <string>
31#include <unordered_set>
32#include <vector>
33
34//- data _______________________________________________________________________
35namespace cppjit::cpyrt {
36extern PyObject* gThisModule;
37extern PyObject* gPyTypeMap;
38extern std::unordered_set<interop::TCppScope_t> gPinnedTypes;
39} // namespace cppjit::cpyrt
40
41// to prevent having to walk scopes, track python classes by C++ class
42typedef std::unordered_map<interop::TCppScope_t, PyObject*> PyClassMap_t;
44
45//- helpers --------------------------------------------------------------------
46
47namespace cppjit::cpyrt {
48
49typedef struct {
50 PyObject_HEAD PyObject* dict;
52
53// helper for creating new C++ proxy python types
55 PyObject* pybases) {
56 // Create a new python shadow class with the required hierarchy and
57 // meta-classes.
58 PyObject* pymetabases = PyTuple_New(PyTuple_GET_SIZE(pybases));
59 for (int i = 0; i < PyTuple_GET_SIZE(pybases); ++i) {
60 PyObject* btype = (PyObject*)Py_TYPE(PyTuple_GetItem(pybases, i));
61 Py_INCREF(btype);
62 PyTuple_SET_ITEM(pymetabases, i, btype);
63 }
64
65 std::string name = interop::GetFinalName(klass);
66
67 // create meta-class, add a dummy __module__ to pre-empt the default setting
68 PyObject* args =
69 Py_BuildValue((char*)"sO{}", (name + "_meta").c_str(), pymetabases);
70 PyDict_SetItem(PyTuple_GET_ITEM(args, 2), PyStrings::gModule, Py_True);
71 Py_DECREF(pymetabases);
72
73 PyObject* pymeta = (PyObject*)CPPScopeMeta_New(klass, args);
74 Py_DECREF(args);
75 if (!pymeta)
76 return nullptr;
77
78 // alright, and now we really badly want to get rid of the dummy ...
79 PyObject* dictproxy = PyObject_GetAttr(pymeta, PyStrings::gDict);
80 PyDict_DelItem(((proxyobject*)dictproxy)->dict, PyStrings::gModule);
81
82 // create actual class
83 args = Py_BuildValue((char*)"sO{}", name.c_str(), pybases);
84 PyObject* pyclass =
85 ((PyTypeObject*)pymeta)->tp_new((PyTypeObject*)pymeta, args, nullptr);
86
87 Py_DECREF(args);
88 Py_DECREF(pymeta);
89
90 return pyclass;
91}
92
93static inline void AddPropertyToClass(PyObject* pyclass,
97 PyObject* pname = PyUnicode_InternFromString(
98 const_cast<char*>(property->GetName().c_str()));
99
100 // allow access at the instance level
101 PyType_Type.tp_setattro(pyclass, pname, (PyObject*)property);
102 if (PyErr_Occurred()) {
103 // CPPDataMember.tp_descr_set raises an error
104 // when all of the following conditions are met
105 // 1. data is static const
106 // 2. scope is a derived class
107 // 3. data is defined in both parent and base class (using
108 // parent::attribute;)
109 PyErr_Clear();
110 }
111
112 // allow access at the class level (always add after setting instance level)
114 PyType_Type.tp_setattro((PyObject*)Py_TYPE(pyclass), pname,
116
117 // cleanup
118 Py_DECREF(pname);
119 Py_DECREF(property);
120}
121
122static inline void AddScopeToParent(PyObject* parent, const std::string& name,
123 PyObject* newscope) {
124 PyObject* pyname = PyUnicode_InternFromString((char*)name.c_str());
125 if (CPPScope_Check(parent))
126 PyType_Type.tp_setattro(parent, pyname, newscope);
127 else
128 PyObject_SetAttr(parent, pyname, newscope);
129 Py_DECREF(pyname);
130}
131
132static inline PyObject* GetAttrDirect(PyObject* pyclass, PyObject* pyname) {
133 // get an attribute without causing getattr lookups
134 PyObject* dct = PyObject_GetAttr(pyclass, PyStrings::gDict);
135 if (dct) {
136 PyObject* attr = PyObject_GetItem(dct, pyname);
137 Py_DECREF(dct);
138 return attr;
139 }
140 return nullptr;
141}
142
143} // namespace cppjit::cpyrt
144
145//- public functions ---------------------------------------------------------
146namespace cppjit::cpyrt {
147
148static inline void sync_templates(PyObject* pyclass,
149 const std::string& mtCppName,
150 const std::string& mtName) {
151 PyObject* dct = PyObject_GetAttr(pyclass, PyStrings::gDict);
152 PyObject* pyname = PyUnicode_FromString(const_cast<char*>(mtName.c_str()));
153 PyObject* attr = PyObject_GetItem(dct, pyname);
154 if (!attr)
155 PyErr_Clear();
156 Py_DECREF(dct);
158 TemplateProxy* pytmpl = TemplateProxy_New(mtCppName, mtName, pyclass);
160 pytmpl->MergeOverload((CPPOverload*)attr);
161 PyType_Type.tp_setattro(pyclass, pyname, (PyObject*)pytmpl);
162 Py_DECREF(pytmpl);
163 }
164 Py_XDECREF(attr);
165 Py_DECREF(pyname);
166}
167
169 const unsigned int flags) {
170 // Collect methods and data for the given scope, and add them to the given
171 // python proxy object.
172
173 // some properties that'll affect building the dictionary
174 bool isNamespace = interop::IsNamespace(scope);
175 bool isComplete = interop::IsComplete(scope);
176 bool hasConstructor = false;
177 interop::TCppMethod_t potGetItem = nullptr;
178
179 // load all public methods and data members
180 typedef std::vector<PyCallable*> Callables_t;
181 typedef std::unordered_map<std::string, Callables_t> CallableCache_t;
182 CallableCache_t cache;
183
184 // bypass custom __getattr__ for efficiency
185 getattrofunc oldgetattro = Py_TYPE(pyclass)->tp_getattro;
186 Py_TYPE(pyclass)->tp_getattro = PyType_Type.tp_getattro;
187
188 // functions in namespaces are properly found through lazy lookup, so do not
189 // create them until needed (the same is not true for data members)
190 std::vector<interop::TCppMethod_t> methods;
191 if (isComplete)
192 interop::GetClassMethods(scope, methods);
193
194 for (auto& method : methods) {
195
196 // do not expose non-public methods as the Cling wrappers as those won't
197 // compile; exception: a non-public method whose declaring class is not this
198 // scope can only have entered the enumeration through a public
199 // using-declaration (the backend drops non-public shadows), so it is
200 // publicly callable through this class and its wrapper compiles (e.g.
201 // MSVC's std::shared_ptr re-exposes the protected std::_Ptr_base<T>::get
202 // with `public: using _Mybase::get;`)
203 if (!interop::IsPublicMethod(method) &&
204 interop::GetParentScope(interop::TCppScope_t(method.data)) == scope)
205 continue;
206
207 // process the method based on its name
208 std::string mtCppName = interop::GetName(interop::TCppScope_t(method.data));
209
210 // special case trackers
211 bool setupSetItem = false;
212 bool isConstructor = interop::IsConstructor(method);
213
214 // Here isTemplate means to ignore templated constructors, that is handled
215 // in the next loop.
216 bool isTemplate = interop::IsTemplatedMethod(method) && !isConstructor;
217 bool isStubbedOperator = false;
218
219 // filter empty names (happens for namespaces, is bug?)
220 if (mtCppName == "")
221 continue;
222
223 // filter C++ destructors
224 if (mtCppName[0] == '~')
225 continue;
226
227 // translate operators
228 std::string mtName = Utility::MapOperatorName(
229 mtCppName, interop::GetMethodNumArgs(method), &isStubbedOperator);
230 if (mtName.empty())
231 continue;
232
233 // operator[]/() returning a reference type will be used for __setitem__
234 bool isCall = mtName == "__call__";
235 if (isCall || mtName == "__getitem__") {
236 const std::string& qual_return =
238 const std::string& cpd = TypeManip::compound(qual_return);
239 if (!cpd.empty() && cpd[cpd.size() - 1] == '&' &&
240 qual_return.find("const", 0, 5) == std::string::npos) {
241 if (isCall && !potGetItem)
242 potGetItem = method;
243 setupSetItem = true; // will add methods as overloads
244 } else if (isCall && 1 < interop::GetMethodNumArgs(method)) {
245 // not a non-const by-ref return, thus better __getitem__ candidate; the
246 // requirement for multiple arguments is that there is otherwise no
247 // benefit over the use of normal __getitem__ (this allows
248 // multi-indexing arguments, which is clean in Python, but not allowed
249 // in C++)
250 potGetItem = method;
251 }
252 }
253
254 // template members; handled by adding a dispatcher to the class
255 bool storeOnTemplate = isTemplate;
256 if (storeOnTemplate) {
257 sync_templates(pyclass, mtCppName, mtName);
258 // continue processing to actually add the method so that the proxy can
259 // find it on the class when called explicitly
260 }
261
262 // construct the holder
263 PyCallable* pycall = nullptr;
264 if (interop::IsStaticMethod(method)) // class method
265 pycall = new CPPClassMethod(scope, method);
266 else if (isNamespace) // free function
267 pycall = new CPPFunction(scope, method);
268 else if (isConstructor) { // ctor
269 mtName = "__init__";
270 hasConstructor = true;
271 if (!interop::IsAbstract(scope)) {
272 if (flags & CPPScope::kIsMultiCross) {
273 pycall = new CPPMultiConstructor(scope, method);
274 } else
275 pycall = new CPPConstructor(scope, method);
276 } else
277 pycall = new CPPAbstractClassConstructor(scope, method);
278 } else if (isStubbedOperator) {
279 pycall = new CPPOperator(scope, method, mtName);
280 } else // member function
281 pycall = new CPPMethod(scope, method);
282
283 if (storeOnTemplate) {
284 // template proxy was already created in sync_templates call above, so
285 // add only here, not to the cache of collected methods
286 PyObject* attr =
287 PyObject_GetAttrString(pyclass, const_cast<char*>(mtName.c_str()));
288 if (isTemplate)
289 ((TemplateProxy*)attr)->AdoptTemplate(pycall);
290 else
291 ((TemplateProxy*)attr)->AdoptMethod(pycall);
292 Py_DECREF(attr);
293
294 // for operator[]/() that returns by ref, also add __setitem__
295 if (setupSetItem) {
296 TemplateProxy* pysi =
298 if (!TemplateProxy_Check(pysi)) {
299 CPPOverload* precursor =
300 (CPPOverload_Check(pysi)) ? (CPPOverload*)pysi : nullptr;
301 if (pysi && !precursor)
302 Py_DECREF(pysi); // something unknown, just drop it
303 pysi = TemplateProxy_New(mtCppName, "__setitem__", pyclass);
304 if (precursor)
305 pysi->MergeOverload(precursor);
306 Py_XDECREF(precursor);
307 PyObject_SetAttrString(pyclass, const_cast<char*>("__setitem__"),
308 (PyObject*)pysi);
309 }
310 if (isTemplate)
311 pysi->AdoptTemplate(new CPPSetItem(scope, method));
312 else
313 pysi->AdoptMethod(new CPPSetItem(scope, method));
314 Py_XDECREF(pysi);
315 }
316
317 } else {
318 // lookup method dispatcher and store method
319 Callables_t& md =
320 (*(cache.insert(std::make_pair(mtName, Callables_t())).first)).second;
321 md.push_back(pycall);
322
323 // special case for operator[]/() that returns by ref, use for
324 // getitem/call and setitem
325 if (setupSetItem) {
326 Callables_t& setitem =
327 (*(cache
328 .insert(std::make_pair(std::string("__setitem__"),
329 Callables_t()))
330 .first))
331 .second;
332 setitem.push_back(new CPPSetItem(scope, method));
333 }
334 }
335 }
336
337 // add proxies for un-instantiated/non-overloaded templated methods
338 std::vector<interop::TCppMethod_t> templ_methods;
339 interop::GetTemplatedMethods(scope, templ_methods);
340 for (auto& method : templ_methods) {
341 const std::string mtCppName =
343 // the number of arguments isn't known until instantiation and as far as C++
344 // is concerned, all same-named operators are simply overloads; so will
345 // pre-emptively add both names if with and without arguments differ,
346 // letting the normal overload mechanism resolve on call
347 bool isConstructor = interop::IsConstructor(method);
348
349 // first add with no arguments
350 std::string mtName0 =
351 isConstructor ? "__init__" : Utility::MapOperatorName(mtCppName, false);
352 sync_templates(pyclass, mtCppName, mtName0);
353
354 // then add when taking arguments, if this method is different
355 if (!isConstructor) {
356 std::string mtName1 = Utility::MapOperatorName(mtCppName, true);
357 if (mtName0 != mtName1)
358 sync_templates(pyclass, mtCppName, mtName1);
359 }
360 }
361
362 // add a pseudo-default ctor, if none defined
363 if (!hasConstructor) {
364 PyCallable* defctor = nullptr;
365 if (!isComplete) {
366 ((CPPScope*)pyclass)->fFlags |= CPPScope::kIsInComplete;
367 defctor =
369 } else if (interop::IsAbstract(scope)) {
371 } else if (isNamespace) {
372 defctor = new CPPNamespaceConstructor(scope, interop::TCppMethod_t{});
373 } else {
374 defctor =
376 }
377
378 if (defctor)
379 cache["__init__"].push_back(defctor);
380 }
381
382 // map __call__ to __getitem__ if also mapped to __setitem__
383 if (potGetItem) {
384 Callables_t& getitem =
385 (*(cache
386 .insert(
387 std::make_pair(std::string("__getitem__"), Callables_t()))
388 .first))
389 .second;
390 getitem.push_back(new CPPGetItem(scope, potGetItem));
391 }
392
393 // add the methods to the class dictionary
394 PyObject* dct = PyObject_GetAttr(pyclass, PyStrings::gDict);
395 for (CallableCache_t::iterator imd = cache.begin(); imd != cache.end();
396 ++imd) {
397 // in order to prevent removing templated editions of this method (which
398 // were set earlier, above, as a different proxy object), we'll check and
399 // add this method flagged as a generic one (to be picked up by the
400 // templated one as appropriate) if a template exists
401 PyObject* pyname =
402 PyUnicode_FromString(const_cast<char*>(imd->first.c_str()));
403 PyObject* attr = PyObject_GetItem(dct, pyname);
404 Py_DECREF(pyname);
406 // template exists, supply it with the non-templated method overloads
407 for (auto cit : imd->second)
408 ((TemplateProxy*)attr)->AdoptMethod(cit);
409 } else {
410 if (!attr)
411 PyErr_Clear();
412 // normal case, add a new method
413 CPPOverload* method = CPPOverload_New(imd->first, imd->second);
414 PyObject* pymname = PyUnicode_InternFromString(
415 const_cast<char*>(method->GetName().c_str()));
416 PyType_Type.tp_setattro(pyclass, pymname, (PyObject*)method);
417 Py_DECREF(pymname);
418 Py_DECREF(method);
419 }
420
421 Py_XDECREF(attr); // could have been found in base class or non-existent
422 }
423 Py_DECREF(dct);
424
425 // collect data members (including enums)
426 std::vector<interop::TCppScope_t> datamembers;
427 interop::GetDatamembers(scope, datamembers);
428 for (auto& datamember : datamembers) {
429 // allow only public members
430 if (!interop::IsPublicData(datamember))
431 continue;
432
433 // enum datamembers (this in conjunction with previously collected enums
434 // above)
436 interop::IsEnumConstant(datamember)) {
437 // two options: this is a static variable, or it is the enum value, the
438 // latter already exists, so check for it and move on if set
439 PyObject* eset = PyObject_GetAttrString(
440 pyclass,
441 const_cast<char*>(interop::GetFinalName(datamember).c_str()));
442 if (eset) {
443 Py_DECREF(eset);
444 continue;
445 }
446
447 PyErr_Clear();
448
449 // it could still be that this is an anonymous enum, which is not in the
450 // list provided by the class
451 if (strstr(interop::GetDatamemberTypeAsString(datamember).c_str(),
452 "(anonymous)") != 0 ||
453 strstr(interop::GetDatamemberTypeAsString(datamember).c_str(),
454 "(unnamed)") != 0) {
455 AddPropertyToClass(pyclass, scope, datamember);
456 continue;
457 }
458 }
459
460 // properties (aka public (static) data members)
461 AddPropertyToClass(pyclass, scope, datamember);
462 }
463
464 // restore custom __getattr__
465 Py_TYPE(pyclass)->tp_getattro = oldgetattro;
466
467 // all ok, done
468 return 0;
469}
470
471//----------------------------------------------------------------------------
473 std::deque<interop::TCppScope_t>& uqb) {
474 // collect bases in acceptable mro order, while removing duplicates (this may
475 // break the overload resolution in esoteric cases, but otherwise the class
476 // can not be used at all, as CPython will refuse the mro).
477 size_t nbases = interop::GetNumBases(klass);
478
479 for (size_t ibase = 0; ibase < nbases; ++ibase) {
481 int decision = 2;
482 if (!tp)
483 continue; // means this base with not be available Python-side
484 for (size_t ibase2 = 0; ibase2 < uqb.size(); ++ibase2) {
485 if (uqb[ibase2] == tp) { // not unique ... skip
486 decision = 0;
487 break;
488 }
489
490 if (interop::IsSubclass(tp, uqb[ibase2])) {
491 // mro requirement: sub-type has to follow base
492 decision = 1;
493 break;
494 }
495 }
496
497 if (decision == 1) {
498 uqb.push_front(tp);
499 } else if (decision == 2) {
500 uqb.push_back(tp);
501 }
502 // skipped if decision == 0 (not unique)
503 }
504}
505
507 // Build a tuple of python proxy classes of all the bases of the given
508 // 'klass'.
509 std::deque<interop::TCppScope_t> uqb;
510 CollectUniqueBases(klass, uqb);
511
512 // allocate a tuple for the base classes, special case for first base
513 size_t nbases = uqb.size();
514
515 PyObject* pybases = PyTuple_New(nbases ? nbases : 1);
516 if (!pybases)
517 return nullptr;
518
519 // build all the bases
520 if (nbases == 0) {
521 Py_INCREF((PyObject*)(void*)&CPPInstance_Type);
522 PyTuple_SET_ITEM(pybases, 0, (PyObject*)(void*)&CPPInstance_Type);
523 } else {
524 for (std::deque<interop::TCppScope_t>::size_type ibase = 0; ibase < nbases;
525 ++ibase) {
526 PyObject* pyclass = CreateScopeProxy(uqb[ibase]);
527 if (!pyclass) {
528 Py_DECREF(pybases);
529 return nullptr;
530 }
531
532 PyTuple_SET_ITEM(pybases, ibase, pyclass);
533 }
534
535 // special case, if true python types enter the hierarchy, make sure that
536 // the first base seen is still the CPPInstance_Type
537 if (!PyObject_IsSubclass(PyTuple_GET_ITEM(pybases, 0),
539 PyObject* newpybases = PyTuple_New(nbases + 1);
540 Py_INCREF((PyObject*)(void*)&CPPInstance_Type);
541 PyTuple_SET_ITEM(newpybases, 0, (PyObject*)(void*)&CPPInstance_Type);
542 for (int ibase = 0; ibase < (int)nbases; ++ibase) {
543 PyObject* pyclass = PyTuple_GET_ITEM(pybases, ibase);
544 Py_INCREF(pyclass);
545 PyTuple_SET_ITEM(newpybases, ibase + 1, pyclass);
546 }
547 Py_DECREF(pybases);
548 pybases = newpybases;
549 }
550 }
551
552 return pybases;
553}
554
555} // namespace cppjit::cpyrt
556
557//----------------------------------------------------------------------------
559 // Retrieve scope proxy from the known ones.
560 PyClassMap_t::iterator pci = gPyClasses.find(scope);
561 if (pci != gPyClasses.end()) {
562 PyObject* pyclass = cpyrt_GetWeakRef(pci->second);
563 if (pyclass)
564 return pyclass;
565 }
566
567 return nullptr;
568}
569namespace cppjit::cpyrt {
571 interop::TCppScope_t parent_scope,
572 PyObject* parent) {
574 if (gPyTypeMap) {
575 const std::string& resolved = interop::GetTypeAsString(resolved_type);
576 PyObject* tc =
577 PyDict_GetItemString(gPyTypeMap, resolved.c_str()); // borrowed
578 if (tc && PyCallable_Check(tc)) {
579 const std::string& scName = interop::GetScopedFinalName(parent_scope);
580 PyObject* nt =
581 PyObject_CallFunction(tc, (char*)"ss", name.c_str(),
582 scName != "<unnamed>" ? scName.c_str() : "");
583 if (nt) {
584 if (parent) {
585 AddScopeToParent(parent, name, nt);
586 Py_DECREF(parent);
587 }
588 return nt;
589 }
590 PyErr_Clear();
591 }
592 }
593 return nullptr;
594}
595} // namespace cppjit::cpyrt
596
597//----------------------------------------------------------------------------
599 // Build a python shadow class for the named C++ class.
600 std::string cname = PyUnicode_AsUTF8(PyTuple_GetItem(args, 0));
601 if (PyErr_Occurred())
602 return nullptr;
603
604 return CreateScopeProxy(cname);
605}
606
607//----------------------------------------------------------------------------
608PyObject* cpyrt::CreateScopeProxy(const std::string& name, PyObject* parent,
609 const unsigned flags) {
610 // Build a python shadow class for the named C++ class or namespace.
611
612 // determine complete scope name, if a python parent has been given
613 interop::TCppScope_t parent_scope;
614 if (parent) {
615 if (CPPScope_Check(parent))
616 parent_scope = ((CPPScope*)parent)->fCppType;
617 else {
618 PyObject* parname = PyObject_GetAttr(parent, PyStrings::gName);
619 if (!parname) {
620 PyErr_Format(PyExc_SystemError, "given scope has no name for %s",
621 name.c_str());
622 return nullptr;
623 }
624
625 // should be a string
626 std::string scName = PyUnicode_AsUTF8(parname);
627 Py_DECREF(parname);
628 if (PyErr_Occurred())
629 return nullptr;
630 parent_scope = interop::GetScope(scName);
631 }
632
633 Py_INCREF(parent);
634 }
635
636 // retrieve C++ class (this verifies name, and is therefore done first)
637 if (name == "") {
639 Py_INCREF(gThisModule);
640 parent = gThisModule;
641 return CreateScopeProxy(klass, parent, flags);
642 } else if (interop::TCppScope_t klass =
643 interop::GetScope(name, parent_scope)) {
644 if (interop::IsTypedefed(klass) &&
647 return nullptr; // this is handled by the caller; typedef to class pointer
648 return CreateScopeProxy(klass, parent, flags);
649 } else if (interop::IsBuiltin(name)) {
651 PyObject* result = nullptr;
652 if (type)
653 result = CppType_To_PyObject(type, name, parent_scope, parent);
654 if (result)
655 return result;
656 }
657 // all options have been exhausted: it doesn't exist as such
658 PyErr_Format(PyExc_TypeError, "\'%s\' is not a known C++ class",
659 name.c_str());
660 Py_XDECREF(parent);
661 return nullptr;
662}
663
664//----------------------------------------------------------------------------
666 const unsigned flags) {
667 // Convenience function with a lookup first through the known existing
668 // proxies.
669 PyObject* pyclass = GetScopeProxy(scope);
670 if (pyclass)
671 return pyclass;
672
673 interop::TCppScope_t parent_scope = nullptr;
674 if (CPPScope_Check(parent))
675 parent_scope = ((CPPScope*)parent)->fCppType;
676 if (!parent_scope)
677 parent_scope = interop::GetParentScope(scope);
678
679 if (!parent) {
680 if (parent_scope)
681 parent = CreateScopeProxy(parent_scope);
682 else {
683 Py_INCREF(gThisModule);
684 parent = gThisModule;
685 }
686 }
687
688 std::string name = interop::GetFinalName(scope);
689 bool is_typedef = false;
690 std::string typedefed_name = "";
691 if (interop::IsTypedefed(scope)) {
692 is_typedef = true;
693 typedefed_name = interop::GetFullName(scope);
694 interop::TCppScope_t underlying_scope = interop::GetUnderlyingScope(scope);
695 if ((underlying_scope) && (underlying_scope != scope)) {
696 scope = underlying_scope;
697 } else {
699 interop::GetTypeFromScope(scope), name, parent_scope, parent))
700 return result;
701
702 PyErr_Format(PyExc_TypeError, "\'%s\' is not a known C++ class",
703 interop::GetScopedFinalName(scope).c_str());
704 Py_XDECREF(parent);
705 return nullptr;
706 }
707 }
708
709 if (interop::IsTemplate(scope)) {
710 // a "naked" templated class is requested: return callable proxy for
711 // instantiations
712 PyObject* pytcl = PyObject_GetAttr(gThisModule, PyStrings::gTemplate);
713 PyObject* cppscope = PyLong_FromVoidPtr(scope.data);
714 PyObject* pytemplate = PyObject_CallFunction(
715 pytcl, const_cast<char*>("sO"),
716 const_cast<char*>(interop::GetScopedFinalName(scope).c_str()),
717 cppscope);
718 Py_DECREF(pytcl);
719
720 // cache the result
721 AddScopeToParent(parent, name, pytemplate);
722
723 // done, next step should be a call into this template
724 Py_XDECREF(parent);
725 return pytemplate;
726 }
727
728 if (interop::IsEnumScope(scope))
729 return nullptr;
730
731 // locate class by ID, if possible, to prevent parsing scopes/templates anew
732 PyObject* pyscope = GetScopeProxy(scope);
733 if (pyscope) {
734 if (parent) {
735 AddScopeToParent(parent, name, pyscope);
736 Py_DECREF(parent);
737 }
738 return pyscope;
739 }
740
741 // if the scope was earlier found as actual, then we're done already,
742 // otherwise build a new scope proxy
743 if (!pyscope) {
744 // construct the base classes
745 PyObject* pybases = BuildCppClassBases(scope);
746 if (pybases != 0) {
747 // create a fresh Python class, given bases, name, and empty dictionary
748 pyscope = CreateNewCppProxyClass(scope, pybases);
749 Py_DECREF(pybases);
750 }
751
752 // fill the dictionary, if successful
753 if (pyscope) {
754 if (BuildScopeProxyDict(scope, pyscope, flags)) {
755 // something failed in building the dictionary
756 Py_DECREF(pyscope);
757 pyscope = nullptr;
758 }
759 }
760
761 // store a ref from cppjit scope id to new python class
762 if (pyscope && !(((CPPScope*)pyscope)->fFlags & CPPScope::kIsInComplete)) {
763 gPyClasses[scope] = PyWeakref_NewRef(pyscope, nullptr);
764
765 if (!(((CPPScope*)pyscope)->fFlags & CPPScope::kIsNamespace)) {
766 // add python-style features to classes only
767 if (!Pythonize(pyscope, scope)) {
768 Py_DECREF(pyscope);
769 pyscope = nullptr;
770 }
771 } else {
772 // add to sys.modules to allow importing from this namespace
773 PyObject* pyfullname = PyObject_GetAttr(pyscope, PyStrings::gModule);
774 PyUnicode_AppendAndDel(&pyfullname, PyUnicode_FromString("."));
775 PyUnicode_AppendAndDel(&pyfullname,
776 PyObject_GetAttr(pyscope, PyStrings::gName));
777 PyObject* modules = PySys_GetObject(const_cast<char*>("modules"));
778 if (modules && PyDict_Check(modules))
779 PyDict_SetItem(modules, pyfullname, pyscope);
780 Py_DECREF(pyfullname);
781 }
782 }
783 }
784
785 // store on parent if found/created and complete
786 if (pyscope && !(((CPPScope*)pyscope)->fFlags & CPPScope::kIsInComplete)) {
787 // FIXME: This is to mimic original behaviour. Still required?
790 if (is_typedef && !typedefed_name.empty())
791 AddScopeToParent(parent, typedefed_name, pyscope);
792 AddScopeToParent(parent, name, pyscope);
793 }
794 Py_DECREF(parent);
795
796 // all done
797 return pyscope;
798}
799
800//----------------------------------------------------------------------------
802 PyObject* parent) {
803 // To allow use of C++ exceptions in lieue of Python exceptions, they need to
804 // derive from BaseException, which can not mix with the normal CPPInstance
805 // and use of the meta-class. Instead, encapsulate them in a forwarding class
806 // that derives from Pythons Exception class
807
808 // start with creation of CPPExcInstance type base classes
809 std::deque<interop::TCppScope_t> uqb;
810 CollectUniqueBases(((CPPScope*)pyscope)->fCppType, uqb);
811 size_t nbases = uqb.size();
812
813 // Support for multiple bases actually can not actually work as-is: the reason
814 // for deriving from BaseException is to guarantee the layout needed for
815 // storing traces. If some other base is std::exception (as e.g.
816 // boost::bad_any_cast) or also derives from std::exception, then there are
817 // two trace locations. OTOH, if the other class is a non-exception type, then
818 // the exception class does not need to derive from it because it can never be
819 // caught as that type forwarding to the proxy will work as expected, through,
820 // which is good enough).
821 //
822 // The code below restricts the hierarchy to a single base class, picking the
823 // "best" by filtering std::exception and non-exception bases.
824
825 PyObject* pybases = PyTuple_New(1);
826 if (nbases == 0) {
827 Py_INCREF((PyObject*)(void*)&CPPExcInstance_Type);
828 PyTuple_SET_ITEM(pybases, 0, (PyObject*)(void*)&CPPExcInstance_Type);
829 } else {
830 PyObject* best_base = nullptr;
831
832 for (std::deque<interop::TCppScope_t>::size_type ibase = 0; ibase < nbases;
833 ++ibase) {
834 // retrieve bases through their enclosing scope to guarantee treatment as
835 // exception classes and proper caching
836 interop::TCppScope_t parent_scope = interop::GetParentScope(uqb[ibase]);
837 PyObject* base_parent = CreateScopeProxy(parent_scope);
838 if (!base_parent) {
839 Py_DECREF(pybases);
840 return nullptr;
841 }
842
843 PyObject* excbase = PyObject_GetAttrString(
844 base_parent, interop::GetFinalName(uqb[ibase]).c_str());
845 Py_DECREF(base_parent);
846 if (!excbase) {
847 Py_DECREF(pybases);
848 return nullptr;
849 }
850
851 if (PyType_IsSubtype((PyTypeObject*)excbase, &CPPExcInstance_Type)) {
852 Py_XDECREF(best_base);
853 best_base = excbase;
854
855 if (interop::GetScopedFinalName(uqb[ibase]) != "std::exception")
856 break;
857 } else {
858 // just skip: there will be at least one exception derived base class
859 Py_DECREF(excbase);
860 }
861 }
862
863 PyTuple_SET_ITEM(pybases, 0, best_base);
864 }
865
866 PyObject* args = Py_BuildValue((char*)"OO{}", pyname, pybases);
867
868 // meta-class attributes (__cpp_name__, etc.) can not be resolved lazily so
869 // add them directly instead in case they are needed
870 PyObject* dct = PyTuple_GET_ITEM(args, 2);
871 PyDict_SetItem(dct, PyStrings::gUnderlying, pyscope);
872 PyDict_SetItem(dct, PyStrings::gName,
873 PyObject_GetAttr(pyscope, PyStrings::gName));
874 PyDict_SetItem(dct, PyStrings::gCppName,
875 PyObject_GetAttr(pyscope, PyStrings::gCppName));
876 PyDict_SetItem(dct, PyStrings::gModule,
877 PyObject_GetAttr(pyscope, PyStrings::gModule));
878
879 // create the actual exception class
880 PyObject* exc_pyscope = PyType_Type.tp_new(&PyType_Type, args, nullptr);
881 Py_DECREF(args);
882 Py_DECREF(pybases);
883
884 // cache the result for future lookups and return
885 PyType_Type.tp_setattro(parent, pyname, exc_pyscope);
886 return exc_pyscope;
887}
888
889//----------------------------------------------------------------------------
892 const unsigned flags) {
893 // only known or knowable objects will be bound (null object is ok)
894 if (!klass) {
895 PyErr_SetString(PyExc_TypeError, "attempt to bind C++ object w/o class");
896 return nullptr;
897 }
898
899 // retrieve python class
900 PyObject* pyclass = CreateScopeProxy(klass);
901 if (!pyclass)
902 return nullptr; // error has been set in CreateScopeProxy
903
904 bool noReg = flags & (CPPInstance::kNoMemReg | CPPInstance::kNoWrapConv);
905 bool isRef = flags & CPPInstance::kIsReference;
906 void* r_address =
907 isRef ? (address ? *(void**)address.data : nullptr) : address.data;
908
909 // check whether the object to be bound is a smart pointer that needs
910 // embedding
911 PyObject* smart_type = (!(flags & CPPInstance::kNoWrapConv) &&
912 (((CPPClass*)pyclass)->fFlags & CPPScope::kIsSmart))
913 ? pyclass
914 : nullptr;
915 if (smart_type) {
916 interop::TCppScope_t underlying =
917 ((CPPSmartClass*)smart_type)->fUnderlyingType;
918
919 // Downcast the underlying object to its actual (most derived) class, as
920 // BindCppObject does for raw pointers, but only when the cast is safe:
921 // * the actual class must really derive from the declared one;
922 // GetActualClass() can report a base (e.g. when the actual class has no
923 // dictionary of its own), and such an up-cast must be rejected.
924 // * the cast must need no pointer adjustment: the dereferencer always
925 // returns a pointer to the declared class, so a non-zero offset could
926 // not be applied consistently on later member access (multiple
927 // inheritance).
928 if (address && !isRef) {
929 if (void* deref =
930 interop::CallR(((CPPSmartClass*)smart_type)->fDereferencer,
931 address, 0, nullptr)) {
932 interop::TCppScope_t clActual =
933 interop::GetActualClass(underlying, deref);
934 if (clActual && clActual != underlying &&
935 interop::IsSubclass(clActual, underlying) &&
936 interop::GetBaseOffset(clActual, underlying, deref,
937 -1 /* down-cast */) == 0)
938 underlying = clActual;
939 }
940 }
941
942 pyclass = CreateScopeProxy(underlying);
943 if (!pyclass) {
944 // simply restore and expose as the actual smart pointer class
945 pyclass = smart_type;
946 smart_type = nullptr;
947 }
948 }
949
950 // TODO: make sure that a consistent address is used (may have to be done in
951 // BindCppObject)
952 if (address && !(flags & CPPInstance::kIsValue) /* always fresh */ &&
953 !noReg) {
954 PyObject* oldPyObject =
955 MemoryRegulator::RetrievePyObject(r_address, pyclass);
956
957 // embedded smart pointers are registered with the class of the underlying
958 // type, b/c there is no Python proxy for the smart pointer itself in that
959 // case; check whether the result found matches the smart-ness and if so,
960 // whether the smart pointer found is the same as the one requested (note:
961 // ptr-ptr requires old object to be a reference to enable re-use)
962 if (oldPyObject) {
963 CPPInstance* o_pyobj = ((CPPInstance*)oldPyObject);
964
965 if ((bool)smart_type == o_pyobj->IsSmart() &&
966 r_address ==
967 (smart_type ? o_pyobj->GetObjectRaw() : o_pyobj->GetObject()) &&
968 (!(flags & CPPInstance::kIsPtrPtr) ||
969 (o_pyobj->fFlags & CPPInstance::kIsReference))) {
970 Py_DECREF(pyclass);
971 return oldPyObject;
972 } else { // reference or naked v.s. smart pointer mismatch
973 Py_DECREF(oldPyObject);
974 }
975 }
976 }
977
978 // instantiate an object of this class
979 PyObject* args = PyTuple_New(0);
980 CPPInstance* pyobj = (CPPInstance*)((PyTypeObject*)pyclass)
981 ->tp_new((PyTypeObject*)pyclass, args, nullptr);
982 Py_DECREF(args);
983
984 // bind, register and return if successful
985 if (pyobj != 0) { // fill proxy value?
986 unsigned objflags =
987 flags & (CPPInstance::kIsReference | CPPInstance::kIsPtrPtr |
988 CPPInstance::kIsValue | CPPInstance::kIsOwner |
989 CPPInstance::kIsActual);
990 pyobj->Set(address.data, (CPPInstance::EFlags)objflags);
991
992 if (smart_type)
993 pyobj->SetSmart(smart_type);
994
995 // do not register null pointers, references (regulated objects can be
996 // referenced, but referenced objects can not be regulated), or direct usage
997 // of smart pointers or iterators
998 if (address && !isRef && !noReg)
999 MemoryRegulator::RegisterPyObject(
1000 pyobj, smart_type ? pyobj->GetObjectRaw() : pyobj->GetObject());
1001 }
1002
1003 // successful completion; wrap exception options to make them raiseable,
1004 // normal return otherwise
1005 if (((CPPClass*)pyclass)->fFlags & CPPScope::kIsException) {
1006 PyObject* exc_obj =
1007 CPPExcInstance_Type.tp_new(&CPPExcInstance_Type, nullptr, nullptr);
1008 ((CPPExcInstance*)exc_obj)->fCppInstance = (PyObject*)pyobj;
1009 Py_DECREF(pyclass);
1010 return exc_obj;
1011 }
1012
1013 Py_DECREF(pyclass);
1014 return (PyObject*)pyobj;
1015}
1016
1017//----------------------------------------------------------------------------
1020 const unsigned flags) {
1021 // if the object is a null pointer, return a typed one (as needed for
1022 // overloading)
1023 if (!address)
1024 return BindCppObjectNoCast(address, klass, flags);
1025
1026 // only known or knowable objects will be bound
1027 if (!klass) {
1028 PyErr_SetString(PyExc_TypeError, "attempt to bind C++ object w/o class");
1029 return nullptr;
1030 }
1031
1032 bool isRef = flags & CPPInstance::kIsReference;
1033
1034 // downcast to real class for object returns, unless pinned
1035 // TODO: should the memory regulator for klass be searched first, so that if
1036 // successful, no down-casting is attempted?
1037 // TODO: optimize for final classes
1038 unsigned new_flags = flags;
1039 if (!isRef && (gPinnedTypes.empty() ||
1040 gPinnedTypes.find(klass) == gPinnedTypes.end())) {
1041 interop::TCppScope_t clActual = interop::GetActualClass(klass, address);
1042
1043 if (clActual) {
1044 if (clActual != klass) {
1045 intptr_t offset =
1046 interop::GetBaseOffset(clActual, klass, address, -1 /* down-cast */,
1047 true /* report errors */);
1048 if (offset != -1) { // may fail if clActual not fully defined
1049 address = (void*)((intptr_t)address.data + offset);
1050 klass = clActual;
1051 }
1052 }
1053 new_flags |= CPPInstance::kIsActual;
1054 }
1055 }
1056
1057 // actual binding (returned object may be zero w/ a python exception set)
1058 return BindCppObjectNoCast(address, klass, new_flags);
1059}
1060
1061//----------------------------------------------------------------------------
1063 interop::TCppScope_t klass, cdims_t dims) {
1064 // TODO: this function exists for symmetry; need to figure out if it's useful
1065 return TupleOfInstances_New(address, klass, dims);
1066}
uint32_t fFlags
static PyClassMap_t gPyClasses
std::unordered_map< interop::TCppScope_t, PyObject * > PyClassMap_t
_object PyObject
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char cname
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t attr
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t property
char name[80]
Definition TGX11.cxx:142
void SetSmart(PyObject *smart_type)
void Set(void *address, EFlags flags=kDefault)
Definition CPPInstance.h:99
const std::string & GetName() const
Definition CPPOverload.h:73
void AdoptTemplate(PyCallable *pc)
void AdoptMethod(PyCallable *pc)
void MergeOverload(CPPOverload *mp)
static PyObject * cpyrt_GetWeakRef(PyObject *ref)
Definition cpyrt.h:113
std::string compound(const std::string &name)
std::string MapOperatorName(const std::string &name, bool bTakesParames, bool *stubbed=nullptr)
Definition Utility.cxx:1334
bool CPPOverload_Check(T *object)
Definition CPPOverload.h:94
bool TemplateProxy_Check(T *object)
TemplateProxy * TemplateProxy_New(const std::string &cppname, const std::string &pyname, PyObject *pyclass)
PyObject * gPyTypeMap
PyObject * CppType_To_PyObject(interop::TCppType_t type, std::string name, interop::TCppScope_t parent_scope, PyObject *parent)
CPPScope * CPPScopeMeta_New(interop::TCppScope_t klass, PyObject *args)
Definition CPPScope.h:98
static void AddPropertyToClass(PyObject *pyclass, interop::TCppScope_t scope, interop::TCppScope_t data)
PyObject * gThisModule
Definition CPPMethod.cxx:30
static void sync_templates(PyObject *pyclass, const std::string &mtCppName, const std::string &mtName)
PyObject * CreateExcScopeProxy(PyObject *pyscope, PyObject *pyname, PyObject *parent)
static PyObject * BuildCppClassBases(interop::TCppScope_t klass)
PyTypeObject CPPInstance_Type
PyObject * GetScopeProxy(interop::TCppScope_t)
PyObject * BindCppObject(interop::TCppObject_t object, interop::TCppScope_t klass, const unsigned flags=0)
PyObject * BindCppObjectArray(interop::TCppObject_t address, interop::TCppScope_t klass, cdims_t dims)
static void AddScopeToParent(PyObject *parent, const std::string &name, PyObject *newscope)
std::unordered_set< interop::TCppScope_t > gPinnedTypes
PyObject * TupleOfInstances_New(interop::TCppObject_t address, interop::TCppScope_t klass, cdims_t dims)
PyObject * CreateScopeProxy(PyObject *, PyObject *args)
static int BuildScopeProxyDict(interop::TCppScope_t scope, PyObject *pyclass, const unsigned int flags)
PyTypeObject CPPExcInstance_Type
PyObject * BindCppObjectNoCast(interop::TCppObject_t object, interop::TCppScope_t klass, const unsigned flags=0)
static PyObject * CreateNewCppProxyClass(interop::TCppScope_t klass, PyObject *pybases)
bool CPPScope_Check(T *object)
Definition CPPScope.h:86
CPPOverload * CPPOverload_New(const std::string &name, std::vector< PyCallable * > &methods)
bool Pythonize(PyObject *pyclass, interop::TCppScope_t scope)
CPPDataMember * CPPDataMember_New(interop::TCppScope_t scope, interop::TCppScope_t var)
static void CollectUniqueBases(interop::TCppScope_t klass, std::deque< interop::TCppScope_t > &uqb)
static PyObject * GetAttrDirect(PyObject *pyclass, PyObject *pyname)
Cpp::TypeRef TCppType_t
Definition API.h:42
void GetDatamembers(TCppScope_t scope, std::vector< TCppScope_t > &datamembers)
Cpp::ObjectRef TCppObject_t
Definition API.h:43
TCppType_t GetTypeFromScope(TCppScope_t klass)
std::string GetTypeAsString(TCppType_t type)
TCppType_t GetDatamemberType(TCppScope_t data)
bool IsBuiltin(const std::string &type_name)
std::string GetName(TCppScope_t)
bool IsTemplateInstantiation(TCppScope_t scope)
bool IsConstructor(TCppMethod_t method)
TCppScope_t GetActualClass(TCppScope_t klass, TCppObject_t obj)
void GetTemplatedMethods(TCppScope_t scope, std::vector< TCppMethod_t > &methods)
bool IsAbstract(TCppScope_t scope)
TCppScope_t GetParentScope(TCppScope_t scope)
std::string GetMethodReturnTypeAsString(TCppMethod_t)
TCppIndex_t GetMethodNumArgs(TCppMethod_t)
bool IsStaticDatamember(TCppScope_t var)
bool IsClass(TCppScope_t scope)
TCppIndex_t GetNumBases(TCppScope_t klass)
bool IsSubclass(TCppScope_t derived, TCppScope_t base)
Cpp::FuncRef TCppMethod_t
Definition API.h:44
bool IsPublicData(TCppScope_t var)
TCppScope_t GetBaseScope(TCppScope_t klass, TCppIndex_t ibase)
bool IsTypedefed(TCppScope_t scope)
bool IsEnumScope(TCppScope_t scope)
void GetClassMethods(TCppScope_t scope, std::vector< TCppMethod_t > &methods)
bool IsPointerType(TCppType_t type)
TCppType_t ResolveType(TCppType_t cppitem_name)
TCppScope_t GetUnderlyingScope(TCppScope_t scope)
TCppScope_t GetGlobalScope()
std::string GetFullName(TCppScope_t)
bool IsTemplate(TCppScope_t scope)
void * CallR(TCppMethod_t method, TCppObject_t self, size_t nargs, void *args)
bool IsNamespace(TCppScope_t scope)
bool IsTemplatedMethod(TCppMethod_t method)
TCppScope_t GetScope(const std::string &scope_name, TCppScope_t parent_scope=TCppScope_t{})
bool IsEnumConstant(TCppScope_t scope)
std::string GetFinalName(TCppScope_t type)
ptrdiff_t GetBaseOffset(TCppScope_t derived, TCppScope_t base, TCppObject_t address, int direction, bool rerror=false)
bool IsStaticMethod(TCppMethod_t method)
bool IsPublicMethod(TCppMethod_t method)
TCppType_t GetType(const std::string &name, bool enable_slow_lookup=false)
std::string GetScopedFinalName(TCppScope_t type)
std::string GetDatamemberTypeAsString(TCppScope_t var)
Cpp::DeclRef TCppScope_t
Definition API.h:41
bool IsEnumType(TCppType_t type)
bool IsComplete(TCppScope_t type)
Definition API.cxx:34
PyObject_HEAD PyObject * dict