Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
CPPScope.cxx
Go to the documentation of this file.
1// Bindings
2#include "cpyrt.h"
3
4using namespace cppjit;
5#include "CPPDataMember.h"
6#include "CPPEnum.h"
7#include "CPPFunction.h"
8#include "CPPOverload.h"
9#include "CPPScope.h"
10#include "CustomPyTypes.h"
11#include "Dispatcher.h"
12#include "ProxyWrappers.h"
13#include "PyStrings.h"
14#include "TemplateProxy.h"
15#include "TypeManip.h"
16#include "Utility.h"
17#include "cppjit_interop.h"
18
19// Standard
20#include <algorithm> // for for_each
21#include <set>
22#include <string.h>
23#include <string>
24#include <vector>
25
26namespace cppjit::cpyrt {
27
28extern PyTypeObject CPPInstance_Type;
29
30//- helpers ------------------------------------------------------------------
31static inline PyObject*
32add_template(PyObject* pyclass, const std::string& name,
33 std::vector<PyCallable*>* overloads = nullptr) {
34 // If templated, the user-facing function must be the template proxy, but the
35 // specific lookup must be the current overload, if already found.
36
37 const std::string& ncl = TypeManip::clean_type(name);
38 PyObject* pyncl = PyUnicode_FromString(ncl.c_str());
39 TemplateProxy* pytmpl =
40 (TemplateProxy*)PyType_Type.tp_getattro(pyclass, pyncl);
41 if (!pytmpl) {
42 PyErr_Clear();
43 pytmpl = TemplateProxy_New(ncl, ncl, pyclass);
44 // cache the template on its clean name
45 PyType_Type.tp_setattro(pyclass, pyncl, (PyObject*)pytmpl);
46 Py_DECREF(pyncl);
47 } else if (!TemplateProxy_CheckExact((PyObject*)pytmpl)) {
48 Py_DECREF(pytmpl);
49 return nullptr;
50 }
51
52 if (overloads) {
53 // adopt the new overloads
54 if (ncl != name)
55 for (auto clb : *overloads)
56 pytmpl->AdoptTemplate(clb);
57 else
58 for (auto clb : *overloads)
59 pytmpl->AdoptMethod(clb);
60 }
61
62 // the caller expects a method matching the full name, thus is a
63 // specialization was requested, do not return the template yet
64 if (ncl == name)
65 return (PyObject*)pytmpl;
66
67 Py_DECREF(pytmpl);
68 return nullptr; // so that caller caches the method on full name
69}
70
71//= cpyrt type proxy construction/destruction =============================
72static PyObject* meta_alloc(PyTypeObject* meta, Py_ssize_t nitems) {
73 // pure memory allocation; object initialization is in pt_new
74 return PyType_Type.tp_alloc(meta, nitems);
75}
76
77//----------------------------------------------------------------------------
78static void meta_dealloc(CPPScope* scope) {
79 if (scope->fFlags & CPPScope::kIsNamespace) {
80 if (scope->fImp.fUsing) {
81 for (auto pyobj : *scope->fImp.fUsing)
82 Py_DECREF(pyobj);
83 delete scope->fImp.fUsing;
84 scope->fImp.fUsing = nullptr;
85 }
86 } else if (!(scope->fFlags & CPPScope::kIsPython)) {
87 delete scope->fImp.fCppObjects;
88 scope->fImp.fCppObjects = nullptr;
89 }
90 delete scope->fOperators;
91 free(scope->fModuleName);
92 return PyType_Type.tp_dealloc((PyObject*)scope);
93}
94
95//-----------------------------------------------------------------------------
96static PyObject* meta_getcppname(CPPScope* scope, void*) {
97 if ((void*)scope == (void*)&CPPInstance_Type)
98 return PyUnicode_FromString("CPPInstance_Type");
99 return PyUnicode_FromString(
100 interop::GetScopedFinalName(scope->fCppType).c_str());
101}
102
103//-----------------------------------------------------------------------------
104static PyObject* meta_getmodule(CPPScope* scope, void*) {
105 if ((void*)scope == (void*)&CPPInstance_Type)
106 return PyUnicode_FromString("cppjit.gbl");
107
108 if (scope->fModuleName)
109 return PyUnicode_FromString(scope->fModuleName);
110
111 // get C++ representation of outer scope
113 if (parent_scope == interop::GetGlobalScope())
114 return PyUnicode_FromString(const_cast<char*>("cppjit.gbl"));
115
116 // now peel scopes one by one, pulling in the python naming (which will
117 // simply recurse if not overridden in python)
118 PyObject* pymodule = nullptr;
119 PyObject* pyscope = cpyrt::GetScopeProxy(parent_scope);
120 if (pyscope) {
121 // get the module of our module
122 pymodule = PyObject_GetAttr(pyscope, PyStrings::gModule);
123 if (pymodule) {
124 // append name of our module
125 PyObject* pymodname = PyObject_GetAttr(pyscope, PyStrings::gName);
126 if (pymodname) {
127 PyUnicode_AppendAndDel(&pymodule, PyUnicode_FromString("."));
128 PyUnicode_AppendAndDel(&pymodule, pymodname);
129 }
130 }
131 Py_DECREF(pyscope);
132 }
133
134 if (pymodule)
135 return pymodule;
136 PyErr_Clear();
137
138 // lookup through python failed, so simply cook up a '::' -> '.' replacement
139 std::string modname = interop::GetScopedFinalName(parent_scope);
141 return PyUnicode_FromString(("cppjit.gbl." + modname).c_str());
142}
143
144//-----------------------------------------------------------------------------
145static int meta_setmodule(CPPScope* scope, PyObject* value, void*) {
146 if ((void*)scope == (void*)&CPPInstance_Type) {
147 PyErr_SetString(PyExc_AttributeError,
148 "attribute \'__module__\' of 'cppjit.CPPScope\' objects is "
149 "not writable");
150 return -1;
151 }
152
153 const char* newname = PyUnicode_AsUTF8(value);
154 if (!value)
155 return -1;
156
157 free(scope->fModuleName);
158 Py_ssize_t sz = PyUnicode_GET_LENGTH(value);
159 scope->fModuleName = (char*)malloc(sz + 1);
160 memcpy(scope->fModuleName, newname, sz + 1);
161
162 return 0;
163}
164
165//----------------------------------------------------------------------------
166static PyObject* meta_repr(CPPScope* scope) {
167 // Specialized b/c type_repr expects __module__ to live in the dictionary,
168 // whereas it is a property (to save memory).
169 if ((void*)scope == (void*)&CPPInstance_Type)
170 return PyUnicode_FromFormat(
171 const_cast<char*>("<class cppjit.CPPInstance at %p>"), scope);
172
174 // either meta type or Python-side derived class: use default type printing
175 return PyType_Type.tp_repr((PyObject*)scope);
176 }
177
178 // skip in case some Python-side derived meta class
179 if (!CPPScope_Check(scope) || !scope->fCppType)
180 return PyType_Type.tp_repr((PyObject*)scope);
181
182 // printing of C++ classes
183 PyObject* modname = meta_getmodule(scope, nullptr);
184 std::string clName = interop::GetFinalName(scope->fCppType);
185 const char* kind =
186 (scope->fFlags & CPPScope::kIsNamespace) ? "namespace" : "class";
187
188 PyObject* repr =
189 PyUnicode_FromFormat("<%s %s.%s at %p>", kind, PyUnicode_AsUTF8(modname),
190 clName.c_str(), scope);
191
192 Py_DECREF(modname);
193 return repr;
194}
195
196//= cpyrt type metaclass behavior =========================================
197static PyObject* pt_new(PyTypeObject* subtype, PyObject* args, PyObject* kwds) {
198 // Called when CPPScope acts as a metaclass; since type_new always resets
199 // tp_alloc, and since it does not call tp_init on types, the metaclass is
200 // being fixed up here, and the class is initialized here as well.
201
202 // fixup of metaclass (left permanent, and in principle only called once b/c
203 // cppjit caches python classes)
204 subtype->tp_alloc = (allocfunc)meta_alloc;
205 subtype->tp_dealloc = (destructor)meta_dealloc;
206
207 // creation of the python-side class; extend the size if this is a smart ptr
208 //
209 // A namespace can never be a smart pointer, so skip the check for namespaces.
210 // This matters beyond performance: GetSmartPtrInfo() resolves the scope name
211 // through a slow interpreter lookup that triggers autoloading of the library
212 // providing the scope, which we don't want to do unnecessarily.
215 if (CPPScope_CheckExact(subtype) &&
216 !interop::IsNamespace(((CPPScope*)subtype)->fCppType)) {
218 interop::GetScopedFinalName(((CPPScope*)subtype)->fCppType), &raw,
219 &deref))
220 subtype->tp_basicsize = sizeof(CPPSmartClass);
221 }
222
223 CPPScope* result = (CPPScope*)PyType_Type.tp_new(subtype, args, kwds);
224 if (!CPPScope_Check(result)) {
225 // either failed or custom user-side metaclass that can't be handled here
226 return nullptr;
227 }
228
229 result->fFlags = CPPScope::kNone;
230 result->fOperators = nullptr;
231 result->fModuleName = nullptr;
232
233 if (raw && deref) {
234 result->fFlags |= CPPScope::kIsSmart;
235 ((CPPSmartClass*)result)->fUnderlyingType = raw;
236 ((CPPSmartClass*)result)->fDereferencer = deref;
237 }
238
239 // initialization of class (based on metatype)
240 if (!CPPScope_CheckExact(subtype) ||
241 !strstr(subtype->tp_name, "_meta") /* convention */) {
242 // there has been a user meta class override in a derived class, so do
243 // the consistent thing, thus allowing user control over naming
244 result->fCppType =
245 interop::GetScope(PyUnicode_AsUTF8(PyTuple_GET_ITEM(args, 0)));
246 } else {
247 // coming here from cppjit or from sub-classing in python; take the
248 // C++ type from the meta class to make sure that the latter category
249 // has fCppType properly set (it inherits the meta class, but has an
250 // otherwise unknown (or wrong) C++ type)
251 result->fCppType = ((CPPScope*)subtype)->fCppType;
252
253 // the following is not robust, but by design, C++ classes get their
254 // dictionaries filled after creation (chicken & egg problem as they
255 // can return themselves in methods), whereas a derived Python class
256 // with method overrides will have a non-empty dictionary (even if it
257 // has no methods, it will at least have a module name)
258 if (3 <= PyTuple_GET_SIZE(args)) {
259 PyObject* dct = PyTuple_GET_ITEM(args, 2);
260 Py_ssize_t sz = PyDict_Size(dct);
261 if (0 < sz && !interop::IsNamespace(result->fCppType)) {
262 result->fFlags |= CPPScope::kIsPython;
263 if (1 < PyTuple_GET_SIZE(PyTuple_GET_ITEM(args, 1)))
265 std::ostringstream errmsg;
266 if (!InsertDispatcher(result, PyTuple_GET_ITEM(args, 1), dct, errmsg)) {
267 PyErr_Format(PyExc_TypeError,
268 "no python-side overrides supported (%s)",
269 errmsg.str().c_str());
270 return nullptr;
271 } else {
272 // the direct base can be useful for some templates, such as
273 // shared_ptrs, so make it accessible (the __cpp_cross__ data member
274 // also signals that this is a cross-inheritance class)
275 PyObject* bname = PyUnicode_FromString(
276 interop::GetBaseName(result->fCppType, 0).c_str());
277 if (!bname)
278 PyErr_Clear();
279 else {
280 if (PyObject_SetAttrString((PyObject*)result, "__cpp_cross__",
281 bname) == -1)
282 PyErr_Clear();
283 Py_DECREF(bname);
284 }
285 }
286 } else if (sz == (Py_ssize_t)-1)
287 PyErr_Clear();
288 }
289 }
290
291 // if the user messed with the metaclass, then we may not have a C++ type,
292 // simply return here before more damage gets done
293 if (!result->fCppType)
294 return (PyObject*)result;
295
296 // maps for using namespaces and tracking objects
297 if (!interop::IsNamespace(result->fCppType)) {
298 static interop::TCppScope_t exc_type =
299 interop::GetScope("exception", interop::GetScope("std"));
300 if (interop::IsSubclass(result->fCppType, exc_type))
302 if (!(result->fFlags & CPPScope::kIsPython))
303 result->fImp.fCppObjects = new CppToPyMap_t;
304 else {
305 // special case: the C++ objects should be stored with the associated C++,
306 // not Python, type
307 CPPClass* kls = (CPPClass*)GetScopeProxy(result->fCppType);
308 if (kls) {
309 result->fImp.fCppObjects = kls->fImp.fCppObjects;
310 Py_DECREF(kls);
311 } else
312 result->fImp.fCppObjects = nullptr;
313 }
314 } else {
315 result->fImp.fUsing = nullptr;
317 }
318
319 if (PyErr_Occurred()) {
320 Py_DECREF((PyObject*)result);
321 return nullptr;
322 }
323
324 return (PyObject*)result;
325}
326
327//----------------------------------------------------------------------------
328static PyObject* meta_getattro(PyObject* pyclass, PyObject* pyname) {
329 // normal type-based lookup
330 PyObject* attr = PyType_Type.tp_getattro(pyclass, pyname);
331 if (pyclass == (PyObject*)&CPPInstance_Type)
332 return attr;
333
334 PyObject* possibly_shadowed = nullptr;
335 if (attr) {
336 if (CPPScope_Check(attr) && CPPScope_Check(pyclass) &&
337 !(((CPPScope*)pyclass)->fFlags & CPPScope::kIsNamespace)) {
338 // TODO: the goal here is to prevent typedefs that are shadowed in
339 // subclasses to be found as from the base class. The better approach
340 // would be to find all typedefs at class creation and insert placeholders
341 // to flag here. The current typedef loop in gInterpreter won't do,
342 // however.
343 PyObject* dct = PyObject_GetAttr(pyclass, PyStrings::gDict);
344 if (dct) {
345 PyObject* attr_from_dict = PyObject_GetItem(dct, pyname);
346 Py_DECREF(dct);
347 if (attr_from_dict) {
348 Py_DECREF(attr);
349 return attr_from_dict;
350 }
351 possibly_shadowed = attr;
352 attr = nullptr;
353 }
354 PyErr_Clear();
355 } else if (CPPScope_Check(attr) && CPPScope_Check(pyclass) &&
357 return CreateExcScopeProxy(attr, pyname, pyclass);
358 } else
359 return attr;
360 }
361
362 if (!PyUnicode_CheckExact(pyname) || !CPPScope_Check(pyclass))
363 return possibly_shadowed;
364
365 // filter for python specials
366 std::string name = PyUnicode_AsUTF8(pyname);
367 if (name.size() >= 5 && name.compare(0, 2, "__") == 0 &&
368 name.compare(name.size() - 2, name.size(), "__") == 0)
369 return possibly_shadowed;
370
371 // more elaborate search in case of failure (eg. for inner classes on demand)
372 std::vector<Utility::PyError_t> errors;
373 Utility::FetchError(errors);
374 attr = CreateScopeProxy(name, pyclass);
375
376 if (CPPScope_Check(attr) &&
378 // Instead of the CPPScope, return a fresh exception class derived from
379 // CPPExcInstance.
380 return CreateExcScopeProxy(attr, pyname, pyclass);
381 }
382
383 bool templated_functions_checked = false;
384 CPPScope* klass = ((CPPScope*)pyclass);
385 if (!attr) {
386 Utility::FetchError(errors);
387 interop::TCppScope_t scope = klass->fCppType;
388
389 // namespaces may have seen updates in their list of global functions, which
390 // are available as "methods" even though they're not really that
391 if ((klass->fFlags & CPPScope::kIsNamespace) ||
392 scope == interop::GetGlobalScope()) {
393 // tickle lazy lookup of functions
394 const std::vector<interop::TCppMethod_t> methods =
396 if (!methods.empty()) {
397 // function exists, now collect overloads
398 std::vector<PyCallable*> overloads;
399 for (auto method : methods) {
400 overloads.push_back(new CPPFunction(scope, method));
401 }
402
403 // Note: can't re-use Utility::AddClass here, as there's the risk of
404 // a recursive call. Simply add method directly, as we're guaranteed
405 // that it doesn't exist yet.
407 attr = add_template(pyclass, name, &overloads);
408 else
409 attr = (PyObject*)CPPOverload_New(name, overloads);
410 templated_functions_checked = true;
411 }
412 }
413
414 if (!attr) {
415 interop::TCppScope_t lookup_result = interop::GetNamed(name, scope);
416 if (interop::IsVariable(lookup_result) ||
417 interop::IsEnumConstant(lookup_result)) {
418 attr = (PyObject*)CPPDataMember_New(scope, lookup_result);
419 } else if (interop::IsTypedefed(lookup_result)) {
420 interop::TCppType_t resolved_type =
422 const std::string& cpd =
424 if (cpd == "*") {
427 if (tcl) {
428 typedefpointertoclassobject* tpc = PyObject_New(
430 tpc->fCppType = tcl;
431 attr = (PyObject*)tpc;
432 }
433 }
434 }
435 }
436
437 // function templates that have not been instantiated (namespaces _may_ have
438 // already been taken care of, by their general function lookup above)
439 if (!attr && !templated_functions_checked) {
441 attr = add_template(pyclass, name);
442 else {
443 // for completeness in error reporting
444 PyErr_Format(PyExc_TypeError, "\'%s\' is not a known C++ template",
445 name.c_str());
446 Utility::FetchError(errors);
447 }
448 }
449
450 // enums types requested as type (rather than the constants)
451 if (!attr) {
452 interop::TCppScope_t enumerator =
454 if (interop::IsEnumScope(enumerator)) {
455 // enum types (incl. named and class enums)
456 attr = (PyObject*)CPPEnum_New(name, enumerator);
457 } else {
458 // for completeness in error reporting
459 PyErr_Format(PyExc_TypeError, "\'%s\' is not a known C++ enum",
460 name.c_str());
461 Utility::FetchError(errors);
462 }
463 }
464
465 if (attr) {
466 // cache the result
468 PyType_Type.tp_setattro((PyObject*)Py_TYPE(pyclass), pyname, attr);
469
470 Py_DECREF(attr);
471 // The call below goes through "dm_get"
472 attr = PyType_Type.tp_getattro(pyclass, pyname);
473 if (!attr && PyErr_Occurred())
474 Utility::FetchError(errors);
475 } else
476 PyType_Type.tp_setattro(pyclass, pyname, attr);
477
478 } else {
479 Utility::FetchError(errors);
480 }
481 }
482
483 if (!attr && (klass->fFlags & CPPScope::kIsNamespace)) {
484 // refresh using list as necessary
485 const std::vector<interop::TCppScope_t>& uv =
487 if (!klass->fImp.fUsing || uv.size() != klass->fImp.fUsing->size()) {
488 if (klass->fImp.fUsing) {
489 for (auto pyref : *klass->fImp.fUsing)
490 Py_DECREF(pyref);
491 klass->fImp.fUsing->clear();
492 } else
493 klass->fImp.fUsing = new std::vector<PyObject*>;
494
495 // reload and reset weak refs
496 for (auto uid : uv) {
497 std::string uname = interop::GetScopedFinalName(uid);
498 PyObject* pyuscope = CreateScopeProxy(uname);
499 if (pyuscope) {
500 klass->fImp.fUsing->push_back(PyWeakref_NewRef(pyuscope, nullptr));
501 // the namespace may not otherwise be held, so tie the lifetimes
502 PyObject* llname =
503 PyUnicode_FromString(("__lifeline_" + uname).c_str());
504 PyType_Type.tp_setattro(pyclass, llname, pyuscope);
505 Py_DECREF(llname);
506 Py_DECREF(pyuscope);
507 } else {
508 PyErr_Clear();
509 }
510 }
511 }
512
513 // try all outstanding using namespaces in turn to find the attribute (will
514 // cache locally later; TODO: doing so may cause pathological cases)
515 for (auto pyref : *klass->fImp.fUsing) {
516 PyObject* pyuscope = cpyrt_GetWeakRef(pyref);
517 if (pyuscope) {
518 attr = PyObject_GetAttr(pyuscope, pyname);
519 if (!attr)
520 PyErr_Clear();
521 Py_DECREF(pyuscope);
522 }
523 if (attr)
524 break;
525 }
526 }
527
528 // if the attribute was not found but could possibly have been shadowed,
529 // insert it into the dict now, to short-circuit future lookups (TODO: this is
530 // part of the workaround described above of which the true solution is to
531 // loop over all typedefs at creation time for the class)
532 if (possibly_shadowed) {
533 if (attr) {
534 Py_DECREF(possibly_shadowed);
535 } else {
536 attr = possibly_shadowed;
537 PyType_Type.tp_setattro(pyclass, pyname, attr);
538 }
539 }
540
541 if (attr) {
542 PyErr_Clear();
543 } else {
544 // not found: prepare a full error report
545 PyObject* topmsg = nullptr;
546 PyObject *pytype = 0, *pyvalue = 0, *pytrace = 0;
547 PyErr_Fetch(&pytype, &pyvalue, &pytrace);
548 PyObject* sklass = PyObject_Str(pyclass);
549 PyErr_Restore(pytype, pyvalue, pytrace);
550 if (sklass) {
551 topmsg = PyUnicode_FromFormat(
552 "%s has no attribute \'%s\'. Full details:", PyUnicode_AsUTF8(sklass),
553 PyUnicode_AsUTF8(pyname));
554 Py_DECREF(sklass);
555 } else {
556 topmsg = PyUnicode_FromFormat("no such attribute \'%s\'. Full details:",
557 PyUnicode_AsUTF8(pyname));
558 }
559 SetDetailedException(std::move(errors), topmsg /* steals */,
560 PyExc_AttributeError /* default error */);
561 }
562
563 return attr;
564}
565
566//----------------------------------------------------------------------------
567static int meta_setattro(PyObject* pyclass, PyObject* pyname, PyObject* pyval) {
568 // Global data and static data in namespaces is found lazily, thus if the
569 // first use is setting of the global data by the user, it will not be
570 // reflected on the C++ side, b/c there is no descriptor yet. This triggers
571 // the creation for for such data as necessary. The many checks to narrow down
572 // the specific case are needed to prevent unnecessary lookups and recursion.
573 // skip if the given pyval is a descriptor already, or an unassignable class
574 if (((CPPScope*)pyclass)->fFlags & CPPScope::kIsNamespace &&
576 std::string name = PyUnicode_AsUTF8(pyname);
577 if (interop::GetNamed(name, ((CPPScope*)pyclass)->fCppType)) {
578 PyObject* attr = meta_getattro(pyclass, pyname); // triggers creation
579 if (!attr)
580 PyErr_Clear();
581 else
582 Py_DECREF(attr);
583 }
584 }
585
586 return PyType_Type.tp_setattro(pyclass, pyname, pyval);
587}
588
589//----------------------------------------------------------------------------
590static PyObject* meta_reflex(CPPScope* klass, PyObject* args) {
591 // Provide the requested reflection information.
592 interop::Reflex::RequestId_t request = -1;
594 if (!PyArg_ParseTuple(args, const_cast<char*>("i|i:__cpp_reflex__"), &request,
595 &format))
596 return nullptr;
597
598 switch (request) {
600 if (klass->fFlags & CPPScope::kIsNamespace)
603 break;
605 // this is not the strict C++ definition of aggregates, but is closer to
606 // what is needed for Numba and C calling conventions (TODO: probably have
607 // to check for all public data types, too, and maybe for no padding?)
608 if (interop::IsAggregate(klass->fCppType) ||
612 break;
613 }
614
615 PyErr_Format(PyExc_ValueError, "unsupported reflex request %d or format %d",
616 request, format);
617 return nullptr;
618}
619
620//----------------------------------------------------------------------------
621// p2.7 does not have a __dir__ in object, and object.__dir__ in p3 does not
622// quite what I'd expected of it, so the following pulls in the internal code
623#include "PyObjectDir27.inc"
624
625static PyObject* meta_dir(CPPScope* klass) {
626 // Collect a list of everything (currently) available in the namespace.
627 // The backend can filter by returning empty strings. Special care is
628 // taken for functions, which need not be unique (overloading).
629 using namespace cppjit::interop;
630
631 if ((void*)klass == (void*)&CPPInstance_Type)
632 return PyList_New(0);
633
634 if (!cpyrt::CPPScope_Check((PyObject*)klass)) {
635 PyErr_SetString(PyExc_TypeError, "C++ proxy scope expected");
636 return nullptr;
637 }
638
639 PyObject* dirlist = _generic_dir((PyObject*)klass);
640 if (!(klass->fFlags & CPPScope::kIsNamespace))
641 return dirlist;
642
643 std::set<std::string> cppnames;
644 interop::GetAllCppNames(klass->fCppType, cppnames);
645
646 // cleanup names
647 std::set<std::string> dir_cppnames;
648 for (const std::string& name : cppnames) {
649 if (name.find("__", 0, 2) != std::string::npos ||
650 name.find("<") != std::string::npos ||
651 name.find("operator", 0, 8) != std::string::npos)
652 continue;
653 dir_cppnames.insert(name);
654 }
655
656 // get rid of duplicates
657 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(dirlist); ++i)
658 dir_cppnames.insert(PyUnicode_AsUTF8(PyList_GET_ITEM(dirlist, i)));
659
660 Py_DECREF(dirlist);
661 dirlist = PyList_New(dir_cppnames.size());
662
663 // copy total onto python list
664 Py_ssize_t i = 0;
665 for (const auto& name : dir_cppnames) {
666 PyList_SET_ITEM(dirlist, i++, PyUnicode_FromString(name.c_str()));
667 }
668 return dirlist;
669}
670
671//-----------------------------------------------------------------------------
672static PyMethodDef meta_methods[] = {
673 {(char*)"__cpp_reflex__", (PyCFunction)meta_reflex, METH_VARARGS,
674 (char*)"C++ datamember reflection information"},
675 {(char*)"__dir__", (PyCFunction)meta_dir, METH_NOARGS, nullptr},
676 {(char*)nullptr, nullptr, 0, nullptr}};
677
678//-----------------------------------------------------------------------------
679static PyGetSetDef meta_getset[] = {
680 {(char*)"__cpp_name__", (getter)meta_getcppname, nullptr, nullptr, nullptr},
681 {(char*)"__module__", (getter)meta_getmodule, (setter)meta_setmodule,
682 nullptr, nullptr},
683 {(char*)nullptr, nullptr, nullptr, nullptr, nullptr}};
684
685//= cpyrt object proxy type type ==========================================
686PyTypeObject CPPScope_Type = {
687 PyVarObject_HEAD_INIT(&PyType_Type, 0)(char*) "cppjit.CPPScope", // tp_name
688 sizeof(cpyrt::CPPScope), // tp_basicsize
689 0, // tp_itemsize
690 0, // tp_dealloc
691 0, // tp_vectorcall_offset / tp_print
692 0, // tp_getattr
693 0, // tp_setattr
694 0, // tp_as_async / tp_compare
695 (reprfunc)meta_repr, // tp_repr
696 0, // tp_as_number
697 0, // tp_as_sequence
698 0, // tp_as_mapping
699 0, // tp_hash
700 0, // tp_call
701 0, // tp_str
702 (getattrofunc)meta_getattro, // tp_getattro
703 (setattrofunc)meta_setattro, // tp_setattro
704 0, // tp_as_buffer
705 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
706 Py_TPFLAGS_TYPE_SUBCLASS, // tp_flags
707 (char*)"cpyrt metatype (internal)", // tp_doc
708 0, // tp_traverse
709 0, // tp_clear
710 0, // tp_richcompare
711 0, // tp_weaklistoffset
712 0, // tp_iter
713 0, // tp_iternext
714 meta_methods, // tp_methods
715 0, // tp_members
716 meta_getset, // tp_getset
717 &PyType_Type, // tp_base
718 0, // tp_dict
719 0, // tp_descr_get
720 0, // tp_descr_set
721 0, // tp_dictoffset
722 0, // tp_init
723 0, // tp_alloc
724 (newfunc)pt_new, // tp_new
725 0, // tp_free
726 0, // tp_is_gc
727 0, // tp_bases
728 0, // tp_mro
729 0, // tp_cache
730 0, // tp_subclasses
731 0, // tp_weaklist
732 0, // tp_del
733 0, // tp_version_tag
734 0, // tp_finalize
735 0 // tp_vectorcall
737
738} // namespace cppjit::cpyrt
free(fBuffer)
uint32_t fFlags
_object PyObject
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 value
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 nitems
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 format
char name[80]
Definition TGX11.cxx:142
Utility::PyOperators * fOperators
Definition CPPScope.h:68
std::vector< PyObject * > * fUsing
Definition CPPScope.h:66
union cppjit::cpyrt::CPPScope::@183 fImp
CppToPyMap_t * fCppObjects
Definition CPPScope.h:65
interop::TCppScope_t fCppType
Definition CPPScope.h:62
void AdoptTemplate(PyCallable *pc)
void AdoptMethod(PyCallable *pc)
#define Py_RETURN_TRUE
Definition cpyrt.h:73
#define Py_RETURN_FALSE
Definition cpyrt.h:77
#define CPYRT_PYTYPE_TAIL
Definition cpyrt.h:159
static PyObject * cpyrt_GetWeakRef(PyObject *ref)
Definition cpyrt.h:113
std::string clean_type(const std::string &cppname, bool template_strip=true, bool const_strip=true)
std::string compound(const std::string &name)
void cppscope_to_pyscope(std::string &cppscope)
size_t FetchError(std::vector< PyError_t > &, bool is_cpp=false)
Definition Utility.cxx:1502
static int meta_setattro(PyObject *pyclass, PyObject *pyname, PyObject *pyval)
Definition CPPScope.cxx:567
static PyObject * meta_reflex(CPPScope *klass, PyObject *args)
Definition CPPScope.cxx:590
bool InsertDispatcher(CPPScope *klass, PyObject *bases, PyObject *dct, std::ostringstream &err)
TemplateProxy * TemplateProxy_New(const std::string &cppname, const std::string &pyname, PyObject *pyclass)
PyObject * CreateExcScopeProxy(PyObject *pyscope, PyObject *pyname, PyObject *parent)
static PyObject * meta_alloc(PyTypeObject *meta, Py_ssize_t nitems)
Definition CPPScope.cxx:72
PyTypeObject CPPScope_Type
Definition CPPScope.cxx:686
PyTypeObject TypedefPointerToClass_Type
static PyObject * meta_dir(CPPScope *klass)
Definition CPPScope.cxx:625
PyTypeObject CPPInstance_Type
PyObject * GetScopeProxy(interop::TCppScope_t)
static void meta_dealloc(CPPScope *scope)
Definition CPPScope.cxx:78
static PyObject * meta_getmodule(CPPScope *scope, void *)
Definition CPPScope.cxx:104
static PyObject * pt_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
Definition CPPScope.cxx:197
static PyGetSetDef meta_getset[]
Definition CPPScope.cxx:679
bool TemplateProxy_CheckExact(T *object)
static PyObject * meta_repr(CPPScope *scope)
Definition CPPScope.cxx:166
PyObject * CreateScopeProxy(PyObject *, PyObject *args)
bool CPPDataMember_Check(T *object)
bool CPPScope_CheckExact(T *object)
Definition CPPScope.h:93
std::unordered_map< interop::TCppObject_t, PyObject * > CppToPyMap_t
Type object to hold class reference (this is only semantically a presentation of CPPScope instances,...
Definition CPPScope.h:38
static PyObject * add_template(PyObject *pyclass, const std::string &name, std::vector< PyCallable * > *overloads=nullptr)
Definition CPPScope.cxx:32
static PyMethodDef meta_methods[]
Definition CPPScope.cxx:672
bool CPPScope_Check(T *object)
Definition CPPScope.h:86
CPPEnum * CPPEnum_New(const std::string &name, interop::TCppScope_t scope)
Definition CPPEnum.cxx:156
static int meta_setmodule(CPPScope *scope, PyObject *value, void *)
Definition CPPScope.cxx:145
CPPOverload * CPPOverload_New(const std::string &name, std::vector< PyCallable * > &methods)
CPPDataMember * CPPDataMember_New(interop::TCppScope_t scope, interop::TCppScope_t var)
static PyObject * meta_getattro(PyObject *pyclass, PyObject *pyname)
Definition CPPScope.cxx:328
static PyObject * meta_getcppname(CPPScope *scope, void *)
Definition CPPScope.cxx:96
const FormatId_t OPTIMAL
Definition Reflex.h:22
const RequestId_t IS_AGGREGATE
Definition Reflex.h:15
const RequestId_t IS_NAMESPACE
Definition Reflex.h:14
Cpp::TypeRef TCppType_t
Definition API.h:42
TCppType_t GetTypeFromScope(TCppScope_t klass)
std::string GetTypeAsString(TCppType_t type)
bool ExistsMethodTemplate(TCppScope_t scope, const std::string &name)
bool HasVirtualDestructor(TCppScope_t type)
TCppScope_t GetParentScope(TCppScope_t scope)
bool IsSubclass(TCppScope_t derived, TCppScope_t base)
TCppScope_t GetNamed(const std::string &scope_name, TCppScope_t parent_scope=TCppScope_t{})
Cpp::FuncRef TCppMethod_t
Definition API.h:44
bool IsTypedefed(TCppScope_t scope)
bool IsEnumScope(TCppScope_t scope)
TCppType_t ResolveType(TCppType_t cppitem_name)
TCppScope_t GetUnderlyingScope(TCppScope_t scope)
TCppScope_t GetGlobalScope()
std::vector< TCppMethod_t > GetMethodsFromName(TCppScope_t scope, const std::string &name)
bool IsNamespace(TCppScope_t scope)
TCppScope_t GetScopeFromType(TCppType_t type)
std::string GetBaseName(TCppScope_t klass, TCppIndex_t ibase)
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)
bool IsVariable(TCppScope_t scope)
bool GetSmartPtrInfo(const std::string &, TCppScope_t *raw, TCppMethod_t *deref)
std::vector< TCppScope_t > GetUsingNamespaces(TCppScope_t)
bool IsAggregate(TCppScope_t type)
std::string GetScopedFinalName(TCppScope_t type)
void GetAllCppNames(TCppScope_t scope, std::set< std::string > &cppnames)
Cpp::DeclRef TCppScope_t
Definition API.h:41
Definition API.cxx:34
Custom "builtins," detectable by type, for pass by ref and improved performance.
PyObject_HEAD interop::TCppScope_t fCppType