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