Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
ProxyWrappers.cxx
Go to the documentation of this file.
1// Bindings
2#include "CPyCppyy.h"
3#include "ProxyWrappers.h"
4#include "CPPClassMethod.h"
5#include "CPPConstructor.h"
6#include "CPPDataMember.h"
7#include "CPPExcInstance.h"
8#include "CPPFunction.h"
9#include "CPPGetSetItem.h"
10#include "CPPInstance.h"
11#include "CPPMethod.h"
12#include "CPPOperator.h"
13#include "CPPOverload.h"
14#include "CPPScope.h"
15#include "MemoryRegulator.h"
16#include "PyStrings.h"
17#include "Pythonize.h"
18#include "TemplateProxy.h"
19#include "TupleOfInstances.h"
20#include "TypeManip.h"
21#include "Utility.h"
22
23// Standard
24#include <algorithm>
25#include <deque>
26#include <map>
27#include <set>
28#include <string>
29#include <vector>
30
31
32//- data _______________________________________________________________________
33namespace CPyCppyy {
34 extern PyObject* gThisModule;
35 extern PyObject* gPyTypeMap;
36 extern std::set<Cppyy::TCppType_t> gPinnedTypes;
37}
38
39// to prevent having to walk scopes, track python classes by C++ class
40typedef std::map<Cppyy::TCppScope_t, PyObject*> PyClassMap_t;
42
43
44//- helpers --------------------------------------------------------------------
45
46namespace CPyCppyy {
47
48typedef struct {
49 PyObject_HEAD
52
53// helper for creating new C++ proxy python types
55{
56// Create a new python shadow class with the required hierarchy and meta-classes.
57 PyObject* pymetabases = PyTuple_New(PyTuple_GET_SIZE(pybases));
58 for (int i = 0; i < PyTuple_GET_SIZE(pybases); ++i) {
59 PyObject* btype = (PyObject*)Py_TYPE(PyTuple_GetItem(pybases, i));
60 Py_INCREF(btype);
61 PyTuple_SET_ITEM(pymetabases, i, btype);
62 }
63
64 std::string name = Cppyy::GetFinalName(klass);
65
66// create meta-class, add a dummy __module__ to pre-empt the default setting
67 PyObject* args = Py_BuildValue((char*)"sO{}", (name+"_meta").c_str(), pymetabases);
68 PyDict_SetItem(PyTuple_GET_ITEM(args, 2), PyStrings::gModule, Py_True);
69 Py_DECREF(pymetabases);
70
71 PyObject* pymeta = (PyObject*)CPPScopeMeta_New(klass, args);
72 Py_DECREF(args);
73 if (!pymeta)
74 return nullptr;
75
76// alright, and now we really badly want to get rid of the dummy ...
77 PyObject* dictproxy = PyObject_GetAttr(pymeta, PyStrings::gDict);
78 PyDict_DelItem(((proxyobject*)dictproxy)->dict, PyStrings::gModule);
79
80// create actual class
81 args = Py_BuildValue((char*)"sO{}", name.c_str(), pybases);
82 PyObject* pyclass =
83 ((PyTypeObject*)pymeta)->tp_new((PyTypeObject*)pymeta, args, nullptr);
84
85 Py_DECREF(args);
86 Py_DECREF(pymeta);
87
88 return pyclass;
89}
90
91static inline
94{
96 PyObject* pname = CPyCppyy_PyText_InternFromString(const_cast<char*>(property->GetName().c_str()));
97
98// allow access at the instance level
99 PyType_Type.tp_setattro(pyclass, pname, (PyObject*)property);
100
101// allow access at the class level (always add after setting instance level)
102 if (Cppyy::IsStaticData(scope, idata))
103 PyType_Type.tp_setattro((PyObject*)Py_TYPE(pyclass), pname, (PyObject*)property);
104
105// cleanup
106 Py_DECREF(pname);
107 Py_DECREF(property);
108}
109
110static inline
111void AddScopeToParent(PyObject* parent, const std::string& name, PyObject* newscope)
112{
113 PyObject* pyname = CPyCppyy_PyText_InternFromString((char*)name.c_str());
114 if (CPPScope_Check(parent)) PyType_Type.tp_setattro(parent, pyname, newscope);
115 else PyObject_SetAttr(parent, pyname, newscope);
116 Py_DECREF(pyname);
117}
118
119static inline
121// get an attribute without causing getattr lookups
122 PyObject* dct = PyObject_GetAttr(pyclass, PyStrings::gDict);
123 if (dct) {
124 PyObject* attr = PyObject_GetItem(dct, pyname);
125 Py_DECREF(dct);
126 return attr;
127 }
128 return nullptr;
129}
130
131} // namespace CPyCppyy
132
133
134//- public functions ---------------------------------------------------------
135namespace CPyCppyy {
136
137static inline void sync_templates(
138 PyObject* pyclass, const std::string& mtCppName, const std::string& mtName)
139{
140 PyObject* dct = PyObject_GetAttr(pyclass, PyStrings::gDict);
141 PyObject* pyname = CPyCppyy_PyText_InternFromString(const_cast<char*>(mtName.c_str()));
142 PyObject* attr = PyObject_GetItem(dct, pyname);
143 if (!attr) PyErr_Clear();
144 Py_DECREF(dct);
146 TemplateProxy* pytmpl = TemplateProxy_New(mtCppName, mtName, pyclass);
148 PyType_Type.tp_setattro(pyclass, pyname, (PyObject*)pytmpl);
149 Py_DECREF(pytmpl);
150 }
151 Py_XDECREF(attr);
152 Py_DECREF(pyname);
153}
154
155static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject* pyclass, const unsigned int flags)
156{
157// Collect methods and data for the given scope, and add them to the given python
158// proxy object.
159
160// some properties that'll affect building the dictionary
161 bool isNamespace = Cppyy::IsNamespace(scope);
162 bool isAbstract = Cppyy::IsAbstract(scope);
163 bool hasConstructor = false;
165
166// load all public methods and data members
167 typedef std::vector<PyCallable*> Callables_t;
168 typedef std::map<std::string, Callables_t> CallableCache_t;
169 CallableCache_t cache;
170
171// bypass custom __getattr__ for efficiency
172 getattrofunc oldgetattro = Py_TYPE(pyclass)->tp_getattro;
173 Py_TYPE(pyclass)->tp_getattro = PyType_Type.tp_getattro;
174
175// functions in namespaces are properly found through lazy lookup, so do not
176// create them until needed (the same is not true for data members)
177 const Cppyy::TCppIndex_t nMethods = isNamespace ? 0 : Cppyy::GetNumMethods(scope);
178 for (Cppyy::TCppIndex_t imeth = 0; imeth < nMethods; ++imeth) {
179 Cppyy::TCppMethod_t method = Cppyy::GetMethod(scope, imeth);
180
181 // do not expose non-public methods as the Cling wrappers as those won't compile
182 if (!Cppyy::IsPublicMethod(method))
183 continue;
184
185 // process the method based on its name
186 std::string mtCppName = Cppyy::GetMethodName(method);
187
188 // special case trackers
189 bool setupSetItem = false;
190 bool isConstructor = Cppyy::IsConstructor(method);
191 bool isTemplate = isConstructor ? false : Cppyy::IsMethodTemplate(scope, imeth);
192 bool isStubbedOperator = false;
193
194 // filter empty names (happens for namespaces, is bug?)
195 if (mtCppName == "")
196 continue;
197
198 // filter C++ destructors
199 if (mtCppName[0] == '~')
200 continue;
201
202 // translate operators
203 std::string mtName = Utility::MapOperatorName(
204 mtCppName, Cppyy::GetMethodNumArgs(method), &isStubbedOperator);
205 if (mtName.empty())
206 continue;
207
208 // operator[]/() returning a reference type will be used for __setitem__
209 bool isCall = mtName == "__call__";
210 if (isCall || mtName == "__getitem__") {
211 const std::string& qual_return = Cppyy::ResolveName(Cppyy::GetMethodResultType(method));
212 const std::string& cpd = TypeManip::compound(qual_return);
213 if (!cpd.empty() && cpd[cpd.size()-1] == '&' && \
214 qual_return.find("const", 0, 5) == std::string::npos) {
215 if (isCall && !potGetItem) potGetItem = method;
216 setupSetItem = true; // will add methods as overloads
217 } else if (isCall && 1 < Cppyy::GetMethodNumArgs(method)) {
218 // not a non-const by-ref return, thus better __getitem__ candidate; the
219 // requirement for multiple arguments is that there is otherwise no benefit
220 // over the use of normal __getitem__ (this allows multi-indexing arguments,
221 // which is clean in Python, but not allowed in C++)
222 potGetItem = method;
223 }
224 }
225
226 // template members; handled by adding a dispatcher to the class
227 bool storeOnTemplate =
228 isTemplate ? true : (!isConstructor && Cppyy::ExistsMethodTemplate(scope, mtCppName));
229 if (storeOnTemplate) {
230 sync_templates(pyclass, mtCppName, mtName);
231 // continue processing to actually add the method so that the proxy can find
232 // it on the class when called explicitly
233 }
234
235 // construct the holder
236 PyCallable* pycall = nullptr;
237 if (Cppyy::IsStaticMethod(method)) // class method
238 pycall = new CPPClassMethod(scope, method);
239 else if (isNamespace) // free function
240 pycall = new CPPFunction(scope, method);
241 else if (isConstructor) { // ctor
242 mtName = "__init__";
243 hasConstructor = true;
244 if (!isAbstract) {
245 if (flags & CPPScope::kIsMultiCross) {
246 pycall = new CPPMultiConstructor(scope, method);
247 } else
248 pycall = new CPPConstructor(scope, method);
249 } else
250 pycall = new CPPAbstractClassConstructor(scope, method);
251 } else if (isStubbedOperator) {
252 pycall = new CPPOperator(scope, method, mtName);
253 } else // member function
254 pycall = new CPPMethod(scope, method);
255
256 if (storeOnTemplate) {
257 // template proxy was already created in sync_templates call above, so
258 // add only here, not to the cache of collected methods
259 PyObject* attr = PyObject_GetAttrString(pyclass, const_cast<char*>(mtName.c_str()));
260 if (isTemplate) ((TemplateProxy*)attr)->AdoptTemplate(pycall);
261 else ((TemplateProxy*)attr)->AdoptMethod(pycall);
262 Py_DECREF(attr);
263
264 // for operator[]/() that returns by ref, also add __setitem__
265 if (setupSetItem) {
267 if (!TemplateProxy_Check(pysi)) {
268 CPPOverload* precursor = (CPPOverload_Check(pysi)) ? (CPPOverload*)pysi : nullptr;
269 if (pysi && !precursor) Py_DECREF(pysi); // something unknown, just drop it
270 pysi = TemplateProxy_New(mtCppName, "__setitem__", pyclass);
271 if (precursor) pysi->MergeOverload(precursor);
272 Py_XDECREF(precursor);
273 PyObject_SetAttrString(pyclass, const_cast<char*>("__setitem__"), (PyObject*)pysi);
274 }
275 if (isTemplate) pysi->AdoptTemplate(new CPPSetItem(scope, method));
276 else pysi->AdoptMethod(new CPPSetItem(scope, method));
277 Py_XDECREF(pysi);
278 }
279
280 } else {
281 // lookup method dispatcher and store method
282 Callables_t& md = (*(cache.insert(
283 std::make_pair(mtName, Callables_t())).first)).second;
284 md.push_back(pycall);
285
286 // special case for operator[]/() that returns by ref, use for getitem/call and setitem
287 if (setupSetItem) {
288 Callables_t& setitem = (*(cache.insert(
289 std::make_pair(std::string("__setitem__"), Callables_t())).first)).second;
290 setitem.push_back(new CPPSetItem(scope, method));
291 }
292 }
293 }
294
295// add proxies for un-instantiated/non-overloaded templated methods
296 const Cppyy::TCppIndex_t nTemplMethods = isNamespace ? 0 : Cppyy::GetNumTemplatedMethods(scope);
297 for (Cppyy::TCppIndex_t imeth = 0; imeth < nTemplMethods; ++imeth) {
298 const std::string mtCppName = Cppyy::GetTemplatedMethodName(scope, imeth);
299 // the number of arguments isn't known until instantiation and as far as C++ is concerned, all
300 // same-named operators are simply overloads; so will pre-emptively add both names if with and
301 // without arguments differ, letting the normal overload mechanism resolve on call
302 bool isConstructor = Cppyy::IsTemplatedConstructor(scope, imeth);
303
304 // first add with no arguments
305 std::string mtName0 = isConstructor ? "__init__" : Utility::MapOperatorName(mtCppName, false);
306 sync_templates(pyclass, mtCppName, mtName0);
307
308 // then add when taking arguments, if this method is different
309 if (!isConstructor) {
310 std::string mtName1 = Utility::MapOperatorName(mtCppName, true);
311 if (mtName0 != mtName1)
312 sync_templates(pyclass, mtCppName, mtName1);
313 }
314 }
315
316// add a pseudo-default ctor, if none defined
317 if (!hasConstructor) {
318 PyCallable* defctor = nullptr;
319 if (isAbstract)
320 defctor = new CPPAbstractClassConstructor(scope, (Cppyy::TCppMethod_t)0);
321 else if (isNamespace)
322 defctor = new CPPNamespaceConstructor(scope, (Cppyy::TCppMethod_t)0);
324 ((CPPScope*)pyclass)->fFlags |= CPPScope::kIsInComplete;
325 defctor = new CPPIncompleteClassConstructor(scope, (Cppyy::TCppMethod_t)0);
326 } else
327 defctor = new CPPAllPrivateClassConstructor(scope, (Cppyy::TCppMethod_t)0);
328 cache["__init__"].push_back(defctor);
329 }
330
331// map __call__ to __getitem__ if also mapped to __setitem__
332 if (potGetItem) {
333 Callables_t& getitem = (*(cache.insert(
334 std::make_pair(std::string("__getitem__"), Callables_t())).first)).second;
335 getitem.push_back(new CPPGetItem(scope, potGetItem));
336 }
337
338// add the methods to the class dictionary
339 PyObject* dct = PyObject_GetAttr(pyclass, PyStrings::gDict);
340 for (CallableCache_t::iterator imd = cache.begin(); imd != cache.end(); ++imd) {
341 // in order to prevent removing templated editions of this method (which were set earlier,
342 // above, as a different proxy object), we'll check and add this method flagged as a generic
343 // one (to be picked up by the templated one as appropriate) if a template exists
344 PyObject* pyname = CPyCppyy_PyText_FromString(const_cast<char*>(imd->first.c_str()));
345 PyObject* attr = PyObject_GetItem(dct, pyname);
346 Py_DECREF(pyname);
348 // template exists, supply it with the non-templated method overloads
349 for (auto cit : imd->second)
350 ((TemplateProxy*)attr)->AdoptMethod(cit);
351 } else {
352 if (!attr) PyErr_Clear();
353 // normal case, add a new method
354 CPPOverload* method = CPPOverload_New(imd->first, imd->second);
355 PyObject* pymname = CPyCppyy_PyText_InternFromString(const_cast<char*>(method->GetName().c_str()));
356 PyType_Type.tp_setattro(pyclass, pymname, (PyObject*)method);
357 Py_DECREF(pymname);
358 Py_DECREF(method);
359 }
360
361 Py_XDECREF(attr); // could have been found in base class or non-existent
362 }
363 Py_DECREF(dct);
364
365 // collect data members (including enums)
366 const Cppyy::TCppIndex_t nDataMembers = Cppyy::GetNumDatamembers(scope);
367 for (Cppyy::TCppIndex_t idata = 0; idata < nDataMembers; ++idata) {
368 // allow only public members
369 if (!Cppyy::IsPublicData(scope, idata))
370 continue;
371
372 // enum datamembers (this in conjunction with previously collected enums above)
373 if (Cppyy::IsEnumData(scope, idata) && Cppyy::IsStaticData(scope, idata)) {
374 // some implementation-specific data members have no address: ignore them
375 if (!Cppyy::GetDatamemberOffset(scope, idata))
376 continue;
377
378 // two options: this is a static variable, or it is the enum value, the latter
379 // already exists, so check for it and move on if set
380 PyObject* eset = PyObject_GetAttrString(pyclass,
381 const_cast<char*>(Cppyy::GetDatamemberName(scope, idata).c_str()));
382 if (eset) {
383 Py_DECREF(eset);
384 continue;
385 }
386
387 PyErr_Clear();
388
389 // it could still be that this is an anonymous enum, which is not in the list
390 // provided by the class
391 if (strstr(Cppyy::GetDatamemberType(scope, idata).c_str(), "(anonymous)") != 0 ||
392 strstr(Cppyy::GetDatamemberType(scope, idata).c_str(), "(unnamed)") != 0) {
393 AddPropertyToClass(pyclass, scope, idata);
394 continue;
395 }
396 }
397
398 // properties (aka public (static) data members)
399 AddPropertyToClass(pyclass, scope, idata);
400 }
401
402// restore custom __getattr__
403 Py_TYPE(pyclass)->tp_getattro = oldgetattro;
404
405// all ok, done
406 return 0;
407}
408
409//----------------------------------------------------------------------------
410static void CollectUniqueBases(Cppyy::TCppType_t klass, std::deque<std::string>& uqb)
411{
412// collect bases in acceptable mro order, while removing duplicates (this may
413// break the overload resolution in esoteric cases, but otherwise the class can
414// not be used at all, as CPython will refuse the mro).
415 size_t nbases = Cppyy::GetNumBases(klass);
416
417 std::deque<Cppyy::TCppType_t> bids;
418 for (size_t ibase = 0; ibase < nbases; ++ibase) {
419 const std::string& name = Cppyy::GetBaseName(klass, ibase);
420 int decision = 2;
422 if (!tp) continue; // means this base with not be available Python-side
423 for (size_t ibase2 = 0; ibase2 < uqb.size(); ++ibase2) {
424 if (uqb[ibase2] == name) { // not unique ... skip
425 decision = 0;
426 break;
427 }
428
429 if (Cppyy::IsSubtype(tp, bids[ibase2])) {
430 // mro requirement: sub-type has to follow base
431 decision = 1;
432 break;
433 }
434 }
435
436 if (decision == 1) {
437 uqb.push_front(name);
438 bids.push_front(tp);
439 } else if (decision == 2) {
440 uqb.push_back(name);
441 bids.push_back(tp);
442 }
443 // skipped if decision == 0 (not unique)
444 }
445}
446
448{
449// Build a tuple of python proxy classes of all the bases of the given 'klass'.
450 std::deque<std::string> uqb;
451 CollectUniqueBases(klass, uqb);
452
453// allocate a tuple for the base classes, special case for first base
454 size_t nbases = uqb.size();
455
456 PyObject* pybases = PyTuple_New(nbases ? nbases : 1);
457 if (!pybases)
458 return nullptr;
459
460// build all the bases
461 if (nbases == 0) {
462 Py_INCREF((PyObject*)(void*)&CPPInstance_Type);
463 PyTuple_SET_ITEM(pybases, 0, (PyObject*)(void*)&CPPInstance_Type);
464 } else {
465 for (std::deque<std::string>::size_type ibase = 0; ibase < nbases; ++ibase) {
466 PyObject* pyclass = CreateScopeProxy(uqb[ibase]);
467 if (!pyclass) {
468 Py_DECREF(pybases);
469 return nullptr;
470 }
471
472 PyTuple_SET_ITEM(pybases, ibase, pyclass);
473 }
474
475 // special case, if true python types enter the hierarchy, make sure that
476 // the first base seen is still the CPPInstance_Type
477 if (!PyObject_IsSubclass(PyTuple_GET_ITEM(pybases, 0), (PyObject*)&CPPInstance_Type)) {
478 PyObject* newpybases = PyTuple_New(nbases+1);
479 Py_INCREF((PyObject*)(void*)&CPPInstance_Type);
480 PyTuple_SET_ITEM(newpybases, 0, (PyObject*)(void*)&CPPInstance_Type);
481 for (int ibase = 0; ibase < (int)nbases; ++ibase) {
482 PyObject* pyclass = PyTuple_GET_ITEM(pybases, ibase);
483 Py_INCREF(pyclass);
484 PyTuple_SET_ITEM(newpybases, ibase+1, pyclass);
485 }
486 Py_DECREF(pybases);
487 pybases = newpybases;
488 }
489 }
490
491 return pybases;
492}
493
494} // namespace CPyCppyy
495
496//----------------------------------------------------------------------------
498{
499// Retrieve scope proxy from the known ones.
500 PyClassMap_t::iterator pci = gPyClasses.find(scope);
501 if (pci != gPyClasses.end()) {
502 PyObject* pyclass = CPyCppyy_GetWeakRef(pci->second);
503 if (pyclass)
504 return pyclass;
505 }
506
507 return nullptr;
508}
509
510//----------------------------------------------------------------------------
512{
513// Convenience function with a lookup first through the known existing proxies.
514 PyObject* pyclass = GetScopeProxy(scope);
515 if (pyclass)
516 return pyclass;
517
518 return CreateScopeProxy(Cppyy::GetScopedFinalName(scope), nullptr, flags);
519}
520
521//----------------------------------------------------------------------------
523{
524// Build a python shadow class for the named C++ class.
525 std::string cname = CPyCppyy_PyText_AsString(PyTuple_GetItem(args, 0));
526 if (PyErr_Occurred())
527 return nullptr;
528
529 return CreateScopeProxy(cname);
530}
531
532//----------------------------------------------------------------------------
533PyObject* CPyCppyy::CreateScopeProxy(const std::string& name, PyObject* parent, const unsigned flags)
534{
535// Build a python shadow class for the named C++ class or namespace.
536
537// determine complete scope name, if a python parent has been given
538 std::string scName = "";
539 if (parent) {
540 if (CPPScope_Check(parent))
541 scName = Cppyy::GetScopedFinalName(((CPPScope*)parent)->fCppType);
542 else {
543 PyObject* parname = PyObject_GetAttr(parent, PyStrings::gName);
544 if (!parname) {
545 PyErr_Format(PyExc_SystemError, "given scope has no name for %s", name.c_str());
546 return nullptr;
547 }
548
549 // should be a string
550 scName = CPyCppyy_PyText_AsString(parname);
551 Py_DECREF(parname);
552 if (PyErr_Occurred())
553 return nullptr;
554 }
555
556 // accept this parent scope and use it's name for prefixing
557 Py_INCREF(parent);
558 }
559
560// retrieve C++ class (this verifies name, and is therefore done first)
561 const std::string& lookup = scName.empty() ? name : (scName+"::"+name);
562 Cppyy::TCppScope_t klass = Cppyy::GetScope(lookup);
563
564 if (!(bool)klass && Cppyy::IsTemplate(lookup)) {
565 // a "naked" templated class is requested: return callable proxy for instantiations
566 PyObject* pytcl = PyObject_GetAttr(gThisModule, PyStrings::gTemplate);
567 PyObject* pytemplate = PyObject_CallFunction(
568 pytcl, const_cast<char*>("s"), const_cast<char*>(lookup.c_str()));
569 Py_DECREF(pytcl);
570
571 // cache the result
572 AddScopeToParent(parent ? parent : gThisModule, name, pytemplate);
573
574 // done, next step should be a call into this template
575 Py_XDECREF(parent);
576 return pytemplate;
577 }
578
579 if (!(bool)klass) {
580 // could be an enum, which are treated separately in CPPScope (TODO: maybe they
581 // should be handled here instead anyway??)
582 if (Cppyy::IsEnum(lookup))
583 return nullptr;
584
585 // final possibility is a typedef of a builtin; these are mapped on the python side
586 std::string resolved = Cppyy::ResolveName(lookup);
587 if (gPyTypeMap) {
588 PyObject* tc = PyDict_GetItemString(gPyTypeMap, resolved.c_str()); // borrowed
589 if (tc && PyCallable_Check(tc)) {
590 PyObject* nt = PyObject_CallFunction(tc, (char*)"ss", name.c_str(), scName.c_str());
591 if (nt) {
592 if (parent) {
593 AddScopeToParent(parent, name, nt);
594 Py_DECREF(parent);
595 }
596 return nt;
597 }
598 PyErr_Clear();
599 }
600 }
601
602 // all options have been exhausted: it doesn't exist as such
603 PyErr_Format(PyExc_TypeError, "\'%s\' is not a known C++ class", lookup.c_str());
604 Py_XDECREF(parent);
605 return nullptr;
606 }
607
608// locate class by ID, if possible, to prevent parsing scopes/templates anew
609 PyObject* pyscope = GetScopeProxy(klass);
610 if (pyscope) {
611 if (parent) {
612 AddScopeToParent(parent, name, pyscope);
613 Py_DECREF(parent);
614 }
615 return pyscope;
616 }
617
618// now have a class ... get the actual, fully scoped class name, so that typedef'ed
619// classes are created in the right place
620 const std::string& actual = Cppyy::GetScopedFinalName(klass);
621 if (actual != lookup) {
622 pyscope = CreateScopeProxy(actual);
623 if (!pyscope) PyErr_Clear();
624 }
625
626// locate the parent, if necessary, for memoizing the class if not specified
627 std::string::size_type last = 0;
628 if (!parent) {
629 // TODO: move this to TypeManip, which already has something similar in
630 // the form of 'extract_namespace'
631 // need to deal with template parameters that can have scopes themselves
632 int tpl_open = 0;
633 for (std::string::size_type pos = 0; pos < name.size(); ++pos) {
634 std::string::value_type c = name[pos];
635
636 // count '<' and '>' to be able to skip template contents
637 if (c == '<')
638 ++tpl_open;
639 else if (c == '>')
640 --tpl_open;
641
642 // by only checking for "::" the last part (class name) is dropped
643 else if (tpl_open == 0 && \
644 c == ':' && pos+1 < name.size() && name[ pos+1 ] == ':') {
645 // found a new scope part
646 const std::string& part = name.substr(last, pos-last);
647
648 PyObject* next = PyObject_GetAttrString(
649 parent ? parent : gThisModule, const_cast<char*>(part.c_str()));
650
651 if (!next) { // lookup failed, try to create it
652 PyErr_Clear();
653 next = CreateScopeProxy(part, parent);
654 }
655 Py_XDECREF(parent);
656
657 if (!next) // create failed, give up
658 return nullptr;
659
660 // found scope part
661 parent = next;
662
663 // done with part (note that pos is moved one ahead here)
664 last = pos+2; ++pos;
665 }
666 }
667
668 if (parent && !CPPScope_Check(parent)) {
669 // Special case: parent found is not one of ours (it's e.g. a pure Python module), so
670 // continuing would fail badly. One final lookup, then out of here ...
671 std::string unscoped = name.substr(last, std::string::npos);
672 PyObject* ret = PyObject_GetAttrString(parent, unscoped.c_str());
673 Py_DECREF(parent);
674 return ret;
675 }
676 }
677
678// use the module as a fake scope if no outer scope found
679 if (!parent) {
680 Py_INCREF(gThisModule);
681 parent = gThisModule;
682 }
683
684// if the scope was earlier found as actual, then we're done already, otherwise
685// build a new scope proxy
686 if (!pyscope) {
687 // construct the base classes
688 PyObject* pybases = BuildCppClassBases(klass);
689 if (pybases != 0) {
690 // create a fresh Python class, given bases, name, and empty dictionary
691 pyscope = CreateNewCppProxyClass(klass, pybases);
692 Py_DECREF(pybases);
693 }
694
695 // fill the dictionary, if successful
696 if (pyscope) {
697 if (BuildScopeProxyDict(klass, pyscope, flags)) {
698 // something failed in building the dictionary
699 Py_DECREF(pyscope);
700 pyscope = nullptr;
701 }
702 }
703
704 // store a ref from cppyy scope id to new python class
705 if (pyscope && !(((CPPScope*)pyscope)->fFlags & CPPScope::kIsInComplete)) {
706 gPyClasses[klass] = PyWeakref_NewRef(pyscope, nullptr);
707
708 if (!(((CPPScope*)pyscope)->fFlags & CPPScope::kIsNamespace)) {
709 // add python-style features to classes only
710 if (!Pythonize(pyscope, Cppyy::GetScopedFinalName(klass))) {
711 Py_DECREF(pyscope);
712 pyscope = nullptr;
713 }
714 } else {
715 // add to sys.modules to allow importing from this namespace
716 PyObject* pyfullname = PyObject_GetAttr(pyscope, PyStrings::gModule);
718 CPyCppyy_PyText_AppendAndDel(&pyfullname, PyObject_GetAttr(pyscope, PyStrings::gName));
719 PyObject* modules = PySys_GetObject(const_cast<char*>("modules"));
720 if (modules && PyDict_Check(modules))
721 PyDict_SetItem(modules, pyfullname, pyscope);
722 Py_DECREF(pyfullname);
723 }
724 }
725 }
726
727// store on parent if found/created and complete
728 if (pyscope && !(((CPPScope*)pyscope)->fFlags & CPPScope::kIsInComplete))
729 AddScopeToParent(parent, name, pyscope);
730 Py_DECREF(parent);
731
732// all done
733 return pyscope;
734}
735
736
737//----------------------------------------------------------------------------
739{
740// To allow use of C++ exceptions in lieue of Python exceptions, they need to
741// derive from BaseException, which can not mix with the normal CPPInstance and
742// use of the meta-class. Instead, encapsulate them in a forwarding class that
743// derives from Pythons Exception class
744
745// start with creation of CPPExcInstance type base classes
746 std::deque<std::string> uqb;
747 CollectUniqueBases(((CPPScope*)pyscope)->fCppType, uqb);
748 size_t nbases = uqb.size();
749
750// Support for multiple bases actually can not actually work as-is: the reason
751// for deriving from BaseException is to guarantee the layout needed for storing
752// traces. If some other base is std::exception (as e.g. boost::bad_any_cast) or
753// also derives from std::exception, then there are two trace locations. OTOH,
754// if the other class is a non-exception type, then the exception class does not
755// need to derive from it because it can never be caught as that type forwarding
756// to the proxy will work as expected, through, which is good enough).
757//
758// The code below restricts the hierarchy to a single base class, picking the
759// "best" by filtering std::exception and non-exception bases.
760
761 PyObject* pybases = PyTuple_New(1);
762 if (nbases == 0) {
763 Py_INCREF((PyObject*)(void*)&CPPExcInstance_Type);
764 PyTuple_SET_ITEM(pybases, 0, (PyObject*)(void*)&CPPExcInstance_Type);
765 } else {
766 PyObject* best_base = nullptr;
767
768 for (std::deque<std::string>::size_type ibase = 0; ibase < nbases; ++ibase) {
769 // retrieve bases through their enclosing scope to guarantee treatment as
770 // exception classes and proper caching
771 const std::string& finalname = Cppyy::GetScopedFinalName(Cppyy::GetScope(uqb[ibase]));
772 const std::string& parentname = TypeManip::extract_namespace(finalname);
773 PyObject* base_parent = CreateScopeProxy(parentname);
774 if (!base_parent) {
775 Py_DECREF(pybases);
776 return nullptr;
777 }
778
779 PyObject* excbase = PyObject_GetAttrString(base_parent,
780 parentname.empty() ? finalname.c_str() : finalname.substr(parentname.size()+2, std::string::npos).c_str());
781 Py_DECREF(base_parent);
782 if (!excbase) {
783 Py_DECREF(pybases);
784 return nullptr;
785 }
786
787 if (PyType_IsSubtype((PyTypeObject*)excbase, &CPPExcInstance_Type)) {
788 Py_XDECREF(best_base);
789 best_base = excbase;
790 if (finalname != "std::exception")
791 break;
792 } else {
793 // just skip: there will be at least one exception derived base class
794 Py_DECREF(excbase);
795 }
796 }
797
798 PyTuple_SET_ITEM(pybases, 0, best_base);
799 }
800
801 PyObject* args = Py_BuildValue((char*)"OO{}", pyname, pybases);
802
803// meta-class attributes (__cpp_name__, etc.) can not be resolved lazily so add
804// them directly instead in case they are needed
805 PyObject* dct = PyTuple_GET_ITEM(args, 2);
806 PyDict_SetItem(dct, PyStrings::gUnderlying, pyscope);
807 PyDict_SetItem(dct, PyStrings::gName, PyObject_GetAttr(pyscope, PyStrings::gName));
808 PyDict_SetItem(dct, PyStrings::gCppName, PyObject_GetAttr(pyscope, PyStrings::gCppName));
809 PyDict_SetItem(dct, PyStrings::gModule, PyObject_GetAttr(pyscope, PyStrings::gModule));
810
811// create the actual exception class
812 PyObject* exc_pyscope = PyType_Type.tp_new(&PyType_Type, args, nullptr);
813 Py_DECREF(args);
814 Py_DECREF(pybases);
815
816// cache the result for future lookups and return
817 PyType_Type.tp_setattro(parent, pyname, exc_pyscope);
818 return exc_pyscope;
819}
820
821
822//----------------------------------------------------------------------------
824 Cppyy::TCppType_t klass, const unsigned flags)
825{
826// only known or knowable objects will be bound (null object is ok)
827 if (!klass) {
828 PyErr_SetString(PyExc_TypeError, "attempt to bind C++ object w/o class");
829 return nullptr;
830 }
831
832// retrieve python class
833 PyObject* pyclass = CreateScopeProxy(klass);
834 if (!pyclass)
835 return nullptr; // error has been set in CreateScopeProxy
836
837 bool isRef = flags & CPPInstance::kIsReference;
838 bool isValue = flags & CPPInstance::kIsValue;
839
840// TODO: make sure that a consistent address is used (may have to be done in BindCppObject)
841 if (address && !isValue /* always fresh */ && !(flags & (CPPInstance::kNoWrapConv|CPPInstance::kNoMemReg))) {
842 PyObject* oldPyObject = MemoryRegulator::RetrievePyObject(
843 isRef ? *(void**)address : address, pyclass);
844
845 // ptr-ptr requires old object to be a reference to enable re-use
846 if (oldPyObject && (!(flags & CPPInstance::kIsPtrPtr) ||
847 ((CPPInstance*)oldPyObject)->fFlags & CPPInstance::kIsReference)) {
848 return oldPyObject;
849 }
850 }
851
852// if smart, instantiate a Python-side object of the underlying type, carrying the smartptr
853 PyObject* smart_type = (flags != CPPInstance::kNoWrapConv && (((CPPClass*)pyclass)->fFlags & CPPScope::kIsSmart)) ? pyclass : nullptr;
854 if (smart_type) {
855 pyclass = CreateScopeProxy(((CPPSmartClass*)smart_type)->fUnderlyingType);
856 if (!pyclass) {
857 // simply restore and expose as the actual smart pointer class
858 pyclass = smart_type;
859 smart_type = nullptr;
860 }
861 }
862
863// instantiate an object of this class
864 PyObject* args = PyTuple_New(0);
865 CPPInstance* pyobj =
866 (CPPInstance*)((PyTypeObject*)pyclass)->tp_new((PyTypeObject*)pyclass, args, nullptr);
867 Py_DECREF(args);
868
869// bind, register and return if successful
870 if (pyobj != 0) { // fill proxy value?
871 unsigned objflags = flags & \
872 (CPPInstance::kIsReference | CPPInstance::kIsPtrPtr | CPPInstance::kIsValue | CPPInstance::kIsOwner | CPPInstance::kIsActual);
873 pyobj->Set(address, (CPPInstance::EFlags)objflags);
874
875 if (smart_type)
876 pyobj->SetSmart(smart_type);
877
878 // do not register null pointers, references (?), or direct usage of smart pointers or iterators
879 if (address && !isRef && !(flags & (CPPInstance::kNoWrapConv|CPPInstance::kNoMemReg)))
880 MemoryRegulator::RegisterPyObject(pyobj, pyobj->GetObject());
881 }
882
883// successful completion; wrap exception options to make them raiseable, normal return otherwise
884 if (((CPPClass*)pyclass)->fFlags & CPPScope::kIsException) {
885 PyObject* exc_obj = CPPExcInstance_Type.tp_new(&CPPExcInstance_Type, nullptr, nullptr);
886 ((CPPExcInstance*)exc_obj)->fCppInstance = (PyObject*)pyobj;
887 Py_DECREF(pyclass);
888 return exc_obj;
889 }
890
891 Py_DECREF(pyclass);
892 return (PyObject*)pyobj;
893}
894
895//----------------------------------------------------------------------------
897 Cppyy::TCppType_t klass, const unsigned flags)
898{
899// if the object is a null pointer, return a typed one (as needed for overloading)
900 if (!address)
901 return BindCppObjectNoCast(address, klass, flags);
902
903// only known or knowable objects will be bound
904 if (!klass) {
905 PyErr_SetString(PyExc_TypeError, "attempt to bind C++ object w/o class");
906 return nullptr;
907 }
908
909 bool isRef = flags & CPPInstance::kIsReference;
910
911// downcast to real class for object returns, unless pinned
912// TODO: should the memory regulator for klass be searched first, so that if
913// successful, no down-casting is attempted?
914// TODO: optimize for final classes
915 unsigned new_flags = flags;
916 if (!isRef && (gPinnedTypes.empty() || gPinnedTypes.find(klass) == gPinnedTypes.end())) {
917 Cppyy::TCppType_t clActual = Cppyy::GetActualClass(klass, address);
918
919 if (clActual) {
920 if (clActual != klass) {
921 intptr_t offset = Cppyy::GetBaseOffset(
922 clActual, klass, address, -1 /* down-cast */, true /* report errors */);
923 if (offset != -1) { // may fail if clActual not fully defined
924 address = (void*)((intptr_t)address + offset);
925 klass = clActual;
926 }
927 }
928 new_flags |= CPPInstance::kIsActual;
929 }
930 }
931
932// actual binding (returned object may be zero w/ a python exception set)
933 return BindCppObjectNoCast(address, klass, new_flags);
934}
935
936//----------------------------------------------------------------------------
938 Cppyy::TCppObject_t address, Cppyy::TCppType_t klass, cdims_t dims)
939{
940// TODO: this function exists for symmetry; need to figure out if it's useful
941 return TupleOfInstances_New(address, klass, dims);
942}
static PyObject * CPyCppyy_GetWeakRef(PyObject *ref)
Definition CPyCppyy.h:356
#define Py_TYPE(ob)
Definition CPyCppyy.h:196
#define CPyCppyy_PyText_InternFromString
Definition CPyCppyy.h:82
#define CPyCppyy_PyText_AsString
Definition CPyCppyy.h:76
#define CPyCppyy_PyText_AppendAndDel
Definition CPyCppyy.h:84
#define CPyCppyy_PyText_FromString
Definition CPyCppyy.h:81
Cppyy::TCppType_t fUnderlyingType
static PyClassMap_t gPyClasses
std::map< Cppyy::TCppScope_t, PyObject * > PyClassMap_t
_object PyObject
std::ios_base::fmtflags fFlags
#define c(i)
Definition RSha256.hxx:101
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 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 property
char name[80]
Definition TGX11.cxx:110
void Set(void *address, EFlags flags=kDefault)
Definition CPPInstance.h:92
void SetSmart(PyObject *smart_type)
const std::string & GetName() const
Definition CPPOverload.h:65
void AdoptTemplate(PyCallable *pc)
void AdoptMethod(PyCallable *pc)
void MergeOverload(CPPOverload *mp)
PyObject * gSetItem
Definition PyStrings.cxx:25
std::string compound(const std::string &name)
std::string MapOperatorName(const std::string &name, bool bTakesParames, bool *stubbed=nullptr)
Definition Utility.cxx:932
CPPOverload * CPPOverload_New(const std::string &name, std::vector< PyCallable * > &methods)
PyTypeObject CPPInstance_Type
PyTypeObject CPPExcInstance_Type
PyObject * GetScopeProxy(Cppyy::TCppScope_t)
static PyObject * GetAttrDirect(PyObject *pyclass, PyObject *pyname)
PyObject * CreateScopeProxy(Cppyy::TCppScope_t, const unsigned flags=0)
static PyObject * CreateNewCppProxyClass(Cppyy::TCppScope_t klass, PyObject *pybases)
bool Pythonize(PyObject *pyclass, const std::string &name)
static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject *pyclass, const unsigned int flags)
std::set< Cppyy::TCppType_t > gPinnedTypes
PyObject * BindCppObjectNoCast(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
bool CPPOverload_Check(T *object)
Definition CPPOverload.h:90
bool CPPScope_Check(T *object)
Definition CPPScope.h:81
static void sync_templates(PyObject *pyclass, const std::string &mtCppName, const std::string &mtName)
static PyObject * BuildCppClassBases(Cppyy::TCppType_t klass)
static void AddScopeToParent(PyObject *parent, const std::string &name, PyObject *newscope)
PyObject * TupleOfInstances_New(Cppyy::TCppObject_t address, Cppyy::TCppType_t klass, cdims_t dims)
CPPScope * CPPScopeMeta_New(Cppyy::TCppScope_t klass, PyObject *args)
Definition CPPScope.h:97
CPPDataMember * CPPDataMember_New(Cppyy::TCppScope_t scope, Cppyy::TCppIndex_t idata)
PyObject * CreateExcScopeProxy(PyObject *pyscope, PyObject *pyname, PyObject *parent)
static void AddPropertyToClass(PyObject *pyclass, Cppyy::TCppScope_t scope, Cppyy::TCppIndex_t idata)
PyObject * BindCppObjectArray(Cppyy::TCppObject_t address, Cppyy::TCppType_t klass, cdims_t dims)
PyObject * gThisModule
Definition CPPMethod.cxx:30
PyObject * BindCppObject(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
PyObject * gPyTypeMap
bool TemplateProxy_Check(T *object)
TemplateProxy * TemplateProxy_New(const std::string &cppname, const std::string &pyname, PyObject *pyclass)
static void CollectUniqueBases(Cppyy::TCppType_t klass, std::deque< std::string > &uqb)
size_t TCppIndex_t
Definition cpp_cppyy.h:24
RPY_EXPORTED TCppIndex_t GetNumTemplatedMethods(TCppScope_t scope, bool accept_namespace=false)
RPY_EXPORTED ptrdiff_t GetBaseOffset(TCppType_t derived, TCppType_t base, TCppObject_t address, int direction, bool rerror=false)
RPY_EXPORTED bool IsEnumData(TCppScope_t scope, TCppIndex_t idata)
RPY_EXPORTED bool IsAbstract(TCppType_t type)
intptr_t TCppMethod_t
Definition cpp_cppyy.h:22
RPY_EXPORTED bool IsTemplate(const std::string &template_name)
RPY_EXPORTED bool IsEnum(const std::string &type_name)
RPY_EXPORTED bool ExistsMethodTemplate(TCppScope_t scope, const std::string &name)
RPY_EXPORTED TCppIndex_t GetNumDatamembers(TCppScope_t scope, bool accept_namespace=false)
RPY_EXPORTED std::string GetMethodName(TCppMethod_t)
RPY_EXPORTED bool IsSubtype(TCppType_t derived, TCppType_t base)
void * TCppObject_t
Definition cpp_cppyy.h:21
RPY_EXPORTED bool IsConstructor(TCppMethod_t method)
RPY_EXPORTED TCppIndex_t GetNumMethods(TCppScope_t scope, bool accept_namespace=false)
RPY_EXPORTED std::string ResolveName(const std::string &cppitem_name)
TCppScope_t TCppType_t
Definition cpp_cppyy.h:19
RPY_EXPORTED TCppIndex_t GetMethodNumArgs(TCppMethod_t)
RPY_EXPORTED TCppType_t GetActualClass(TCppType_t klass, TCppObject_t obj)
RPY_EXPORTED std::string GetBaseName(TCppType_t type, TCppIndex_t ibase)
RPY_EXPORTED bool IsNamespace(TCppScope_t scope)
RPY_EXPORTED std::string GetScopedFinalName(TCppType_t type)
RPY_EXPORTED bool IsPublicData(TCppScope_t scope, TCppIndex_t idata)
RPY_EXPORTED bool IsComplete(const std::string &type_name)
RPY_EXPORTED bool IsStaticMethod(TCppMethod_t method)
RPY_EXPORTED bool IsStaticData(TCppScope_t scope, TCppIndex_t idata)
RPY_EXPORTED std::string GetDatamemberType(TCppScope_t scope, TCppIndex_t idata)
RPY_EXPORTED TCppMethod_t GetMethod(TCppScope_t scope, TCppIndex_t imeth)
RPY_EXPORTED std::string GetTemplatedMethodName(TCppScope_t scope, TCppIndex_t imeth)
RPY_EXPORTED TCppScope_t GetScope(const std::string &scope_name)
size_t TCppScope_t
Definition cpp_cppyy.h:18
RPY_EXPORTED bool IsTemplatedConstructor(TCppScope_t scope, TCppIndex_t imeth)
RPY_EXPORTED TCppIndex_t GetNumBases(TCppType_t type)
RPY_EXPORTED std::string GetMethodResultType(TCppMethod_t)
RPY_EXPORTED std::string GetFinalName(TCppType_t type)
RPY_EXPORTED bool IsMethodTemplate(TCppScope_t scope, TCppIndex_t imeth)
RPY_EXPORTED std::string GetDatamemberName(TCppScope_t scope, TCppIndex_t idata)
RPY_EXPORTED bool IsPublicMethod(TCppMethod_t method)
RPY_EXPORTED intptr_t GetDatamemberOffset(TCppScope_t scope, TCppIndex_t idata)
PyObject_HEAD PyObject * dict