Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
Pythonize.cxx
Go to the documentation of this file.
1// Bindings
2#include "cpyrt.h"
3
4using namespace cppjit;
5#include "CPPFunction.h"
6#include "CPPInstance.h"
7#include "CPPOverload.h"
8#include "Converters.h"
9#include "CustomPyTypes.h"
10#include "LowLevelViews.h"
11#include "ProxyWrappers.h"
12#include "PyCallable.h"
13#include "PyStrings.h"
14#include "Pythonize.h"
15#include "TypeManip.h"
16#include "Utility.h"
17#include "cppjit_interop.h"
18
19// Standard
20#include <algorithm>
21#include <complex>
22#include <deque>
23#include <set>
24#include <sstream>
25#include <stdexcept>
26#include <string>
27#include <utility>
28
29//- data and local helpers ---------------------------------------------------
30namespace cppjit::cpyrt {
31extern PyObject* gThisModule;
32std::unordered_map<std::string, std::vector<PyObject*>>& pythonizations();
33} // namespace cppjit::cpyrt
34
35namespace {
36
37// for convenience
38using namespace cppjit::cpyrt;
39
40//-----------------------------------------------------------------------------
41bool HasAttrDirect(PyObject* pyclass, PyObject* pyname,
42 bool mustBecpyrt = false) {
43 // prevents calls to Py_TYPE(pyclass)->tp_getattr, which is unnecessary for
44 // our purposes here and could tickle problems w/ spurious lookups into ROOT
45 // meta
46 PyObject* dct = PyObject_GetAttr(pyclass, PyStrings::gDict);
47 if (dct) {
48 PyObject* attr = PyObject_GetItem(dct, pyname);
49 Py_DECREF(dct);
50 if (attr) {
51 bool ret = !mustBecpyrt || CPPOverload_Check(attr);
52 Py_DECREF(attr);
53 return ret;
54 }
55 }
56 PyErr_Clear();
57 return false;
58}
59
60PyObject* GetAttrDirect(PyObject* pyclass, PyObject* pyname) {
61 // get an attribute without causing getattr lookups
62 PyObject* dct = PyObject_GetAttr(pyclass, PyStrings::gDict);
63 if (dct) {
64 PyObject* attr = PyObject_GetItem(dct, pyname);
65 Py_DECREF(dct);
66 return attr;
67 }
68 return nullptr;
69}
70
71//-----------------------------------------------------------------------------
72inline bool IsTemplatedSTLClass(const std::string& name,
73 const std::string& klass) {
74 // Scan the name of the class and determine whether it is a template
75 // instantiation.
76 auto pos = name.find(klass);
77 return pos == 5 && name.rfind("std::", 0, 5) == 0 &&
78 name.find("::", name.rfind(">")) == std::string::npos;
79}
80
81// to prevent compiler warnings about const char* -> char*
82inline PyObject* CallPyObjMethod(PyObject* obj, const char* meth) {
83 // Helper; call method with signature: obj->meth().
84 Py_INCREF(obj);
86 PyObject_CallMethod(obj, const_cast<char*>(meth), const_cast<char*>(""));
87 Py_DECREF(obj);
88 return result;
89}
90
91//-----------------------------------------------------------------------------
92inline PyObject* CallPyObjMethod(PyObject* obj, const char* meth,
93 PyObject* arg1) {
94 // Helper; call method with signature: obj->meth(arg1).
95 Py_INCREF(obj);
96 PyObject* result = PyObject_CallMethod(obj, const_cast<char*>(meth),
97 const_cast<char*>("O"), arg1);
98 Py_DECREF(obj);
99 return result;
100}
101
102//-----------------------------------------------------------------------------
103PyObject* PyStyleIndex(PyObject* self, PyObject* index) {
104 // Helper; converts python index into straight C index.
105 Py_ssize_t idx = PyLong_AsSsize_t(index);
106 if (idx == (Py_ssize_t)-1 && PyErr_Occurred())
107 return nullptr;
108
109 Py_ssize_t size = PySequence_Size(self);
110 if (idx >= size || (idx < 0 && idx < -size)) {
111 PyErr_SetString(PyExc_IndexError, "index out of range");
112 return nullptr;
113 }
114
115 PyObject* pyindex = nullptr;
116 if (idx >= 0) {
117 Py_INCREF(index);
118 pyindex = index;
119 } else
120 pyindex = PyLong_FromSsize_t(size + idx);
121
122 return pyindex;
123}
124
125//-----------------------------------------------------------------------------
126inline bool AdjustSlice(const Py_ssize_t nlen, Py_ssize_t& start,
127 Py_ssize_t& stop, Py_ssize_t& step) {
128 // Helper; modify slice range to match the container.
129 if ((step > 0 && stop <= start) || (step < 0 && start <= stop))
130 return false;
131
132 if (start < 0)
133 start = 0;
134 if (start >= nlen)
135 start = nlen - 1;
136 if (step >= nlen)
137 step = nlen;
138
139 stop = step > 0 ? std::min(nlen, stop) : (stop >= 0 ? stop : -1);
140 return true;
141}
142
143//-----------------------------------------------------------------------------
144inline PyObject* CallSelfIndex(CPPInstance* self, PyObject* idx,
145 PyObject* pymeth) {
146 // Helper; call method with signature: meth(pyindex).
147 Py_INCREF((PyObject*)self);
148 PyObject* pyindex = PyStyleIndex((PyObject*)self, idx);
149 if (!pyindex) {
150 Py_DECREF((PyObject*)self);
151 return nullptr;
152 }
153
155 PyObject_CallMethodOneArg((PyObject*)self, pymeth, pyindex);
156 Py_DECREF(pyindex);
157 Py_DECREF((PyObject*)self);
158 return result;
159}
160
161//- "smart pointer" behavior ---------------------------------------------------
162static PyObject* smart_follow(PyObject* self, PyObject* name,
163 PyObject* method) {
164 if (!PyUnicode_Check(name))
165 PyErr_SetString(PyExc_TypeError,
166 "getattr(): attribute name must be string");
167
168 PyObject* pyptr = PyObject_CallMethodNoArgs(self, method);
169 if (!pyptr)
170 return nullptr;
171
172 // prevent a potential infinite loop
173 if (Py_TYPE(pyptr) == Py_TYPE(self)) {
174 PyObject* val1 = PyObject_Str((PyObject*)Py_TYPE(self));
175 PyObject* val2 = PyObject_Str(name);
176 PyErr_Format(PyExc_AttributeError, "%s object has no attribute \'%s\'",
177 PyUnicode_AsUTF8(val1), PyUnicode_AsUTF8(val2));
178 Py_DECREF(val2);
179 Py_DECREF(val1);
180
181 Py_DECREF(pyptr);
182 return nullptr;
183 }
184
185 PyObject* result = PyObject_GetAttr(pyptr, name);
186 Py_DECREF(pyptr);
187 return result;
188}
189
190PyObject* DeRefGetAttr(PyObject* self, PyObject* name) {
191 // Follow operator*() if present (available in python as __deref__), so that
192 // smart pointers behave as expected.
194 // TODO: these calls come from TemplateProxy and are unlikely to be needed
195 // in practice, whereas as-is, they can accidentally dereference the result
196 // of end() on some STL containers. Obviously, this is a dumb hack that
197 // should be resolved more fundamentally.
198 PyErr_SetString(PyExc_AttributeError, PyUnicode_AsUTF8(name));
199 return nullptr;
200 }
201
202 return smart_follow(self, name, PyStrings::gDeref);
203}
204
205//-----------------------------------------------------------------------------
206PyObject* ValueGetAttr(PyObject* self, PyObject* name) {
207 // std::optional and std::expected require has_value() before operator*().
208 PyObject* has_value = PyObject_CallMethodNoArgs(self, PyStrings::gHasValue);
209 if (!has_value)
210 return nullptr;
211
212 const int contains_value = PyObject_IsTrue(has_value);
213 Py_DECREF(has_value);
214 if (contains_value < 0)
215 return nullptr;
216 if (!contains_value) {
217 PyErr_SetObject(PyExc_AttributeError, name);
218 return nullptr;
219 }
220
221 return DeRefGetAttr(self, name);
222}
223
224//-----------------------------------------------------------------------------
225PyObject* FollowGetAttr(PyObject* self, PyObject* name) {
226 // Follow operator->() if present (available in python as __follow__), so that
227 // smart pointers behave as expected.
228 return smart_follow(self, name, PyStrings::gFollow);
229}
230
231//- pointer checking bool converter -------------------------------------------
232PyObject* NullCheckBool(PyObject* self) {
233 if (!CPPInstance_Check(self)) {
234 PyErr_SetString(PyExc_TypeError, "C++ object proxy expected");
235 return nullptr;
236 }
237
238 if (!((CPPInstance*)self)->GetObject())
240
241 return PyObject_CallMethodNoArgs(self, PyStrings::gCppBool);
242}
243
244//- vector behavior as primitives ----------------------------------------------
245
246// TODO: can probably use the below getters in the InitializerListConverter
247struct ItemGetter {
248 ItemGetter(PyObject* pyobj) : fPyObject(pyobj) { Py_INCREF(fPyObject); }
249 virtual ~ItemGetter() { Py_DECREF(fPyObject); }
250 virtual Py_ssize_t size() = 0;
251 virtual PyObject* get() = 0;
252 PyObject* fPyObject;
253};
254
255struct CountedItemGetter : public ItemGetter {
256 CountedItemGetter(PyObject* pyobj) : ItemGetter(pyobj), fCur(0) {}
257 Py_ssize_t fCur;
258};
259
260struct TupleItemGetter : public CountedItemGetter {
261 using CountedItemGetter::CountedItemGetter;
262 Py_ssize_t size() override { return PyTuple_GET_SIZE(fPyObject); }
263 PyObject* get() override {
264 if (fCur < PyTuple_GET_SIZE(fPyObject)) {
265 PyObject* item = PyTuple_GET_ITEM(fPyObject, fCur++);
266 Py_INCREF(item);
267 return item;
268 }
269 PyErr_SetString(PyExc_StopIteration, "end of tuple");
270 return nullptr;
271 }
272};
273
274struct ListItemGetter : public CountedItemGetter {
275 using CountedItemGetter::CountedItemGetter;
276 Py_ssize_t size() override { return PyList_GET_SIZE(fPyObject); }
277 PyObject* get() override {
278 if (fCur < PyList_GET_SIZE(fPyObject)) {
279 PyObject* item = PyList_GET_ITEM(fPyObject, fCur++);
280 Py_INCREF(item);
281 return item;
282 }
283 PyErr_SetString(PyExc_StopIteration, "end of list");
284 return nullptr;
285 }
286};
287
288struct SequenceItemGetter : public CountedItemGetter {
289 using CountedItemGetter::CountedItemGetter;
290 Py_ssize_t size() override {
291 Py_ssize_t sz = PySequence_Size(fPyObject);
292 if (sz < 0) {
293 PyErr_Clear();
294 return PyObject_LengthHint(fPyObject, 8);
295 }
296 return sz;
297 }
298 PyObject* get() override { return PySequence_GetItem(fPyObject, fCur++); }
299};
300
301struct IterItemGetter : public ItemGetter {
302 using ItemGetter::ItemGetter;
303 Py_ssize_t size() override { return PyObject_LengthHint(fPyObject, 8); }
304 PyObject* get() override {
305 return (*(Py_TYPE(fPyObject)->tp_iternext))(fPyObject);
306 }
307};
308
309static ItemGetter* GetGetter(PyObject* args) {
310 // Create an ItemGetter to loop over the iterable argument, if any.
311 ItemGetter* getter = nullptr;
312
313 if (PyTuple_GET_SIZE(args) == 1) {
314 PyObject* fi = PyTuple_GET_ITEM(args, 0);
315 if (PyUnicode_Check(fi) || PyBytes_Check(fi))
316 return nullptr; // do not accept string to fill std::vector<char>
317
318 // TODO: this only tests for new-style buffers, which is too strict, but a
319 // generic check for Py_TYPE(fi)->tp_as_buffer is too loose (note that the
320 // main use case is numpy, which offers the new interface)
321 if (PyObject_CheckBuffer(fi))
322 return nullptr;
323
324 if (PyTuple_CheckExact(fi))
325 getter = new TupleItemGetter(fi);
326 else if (PyList_CheckExact(fi))
327 getter = new ListItemGetter(fi);
328 else if (PySequence_Check(fi))
329 getter = new SequenceItemGetter(fi);
330 else {
331 PyObject* iter = PyObject_GetIter(fi);
332 if (iter) {
333 getter = new IterItemGetter{iter};
334 Py_DECREF(iter);
335 } else
336 PyErr_Clear();
337 }
338 }
339
340 return getter;
341}
342
343namespace {
344
345void compileSpanHelpers() {
346 static bool compiled = false;
347
348 if (compiled)
349 return;
350
351 compiled = true;
352
353 auto code = R"(
354namespace __cppjit_internal {
355
356template <class T>
357struct ptr_iterator {
358 T *cur;
359 T *end;
360
361 ptr_iterator(T *c, T *e) : cur(c), end(e) {}
362
363 T &operator*() const { return *cur; }
364 ptr_iterator &operator++()
365 {
366 ++cur;
367 return *this;
368 }
369 bool operator==(const ptr_iterator &other) const { return cur == other.cur; }
370 bool operator!=(const ptr_iterator &other) const { return !(*this == other); }
371};
372
373template <class T>
374ptr_iterator<T> make_iter(T *begin, T *end)
375{
376 return {begin, end};
377}
378
379} // namespace __cppjit_internal
380
381// Note: for const span<T>, T is const-qualified here
382template <class T>
383auto __cppjit_internal_begin(T &s) noexcept
384{
385 return __cppjit_internal::make_iter(s.data(), s.data() + s.size());
386}
387
388// Note: for const span<T>, T is const-qualified here
389template <class T>
390auto __cppjit_internal_end(T &s) noexcept
391{
392 // end iterator = begin iterator with cur == end
393 return __cppjit_internal::make_iter(s.data() + s.size(), s.data() + s.size());
394}
395 )";
396 interop::Compile(code, /*silent*/ true);
397}
398
399PyObject* spanBegin() {
400 static PyObject* pyFunc = nullptr;
401 if (!pyFunc) {
402 compileSpanHelpers();
404 pyFunc = PyObject_GetAttrString(py_ns, "__cppjit_internal_begin");
405 if (!pyFunc) {
406 PyErr_Format(PyExc_RuntimeError,
407 "cppyy internal error: failed to locate helper "
408 "'__cppjit_internal_begin' for std::span pythonization");
409 }
410 }
411 return pyFunc;
412}
413
414PyObject* spanEnd() {
415 static PyObject* pyFunc = nullptr;
416 if (!pyFunc) {
417 compileSpanHelpers();
419 pyFunc = PyObject_GetAttrString(py_ns, "__cppjit_internal_end");
420 if (!pyFunc) {
421 PyErr_Format(PyExc_RuntimeError,
422 "cppyy internal error: failed to locate helper "
423 "'__cppjit_internal_end' for std::span pythonization");
424 }
425 }
426 return pyFunc;
427}
428
429} // namespace
430
431static PyObject* SpanBegin(PyObject* self, PyObject*) {
432 auto begin = spanBegin();
433 if (!begin)
434 return nullptr;
435 return PyObject_CallOneArg(begin, self);
436}
437
438static PyObject* SpanEnd(PyObject* self, PyObject*) {
439 auto end = spanEnd();
440 if (!end)
441 return nullptr;
442 return PyObject_CallOneArg(end, self);
443}
444
445static bool FillVector(PyObject* vecin, PyObject* args, ItemGetter* getter) {
446 Py_ssize_t sz = getter->size();
447 if (sz < 0)
448 return false;
449
450 // reserve memory as applicable
451 if (0 < sz) {
452 PyObject* res =
453 PyObject_CallMethod(vecin, (char*)"reserve", (char*)"n", sz);
454 Py_DECREF(res);
455 } else // i.e. sz == 0, so empty container: done
456 return true;
457
458 bool fill_ok = true;
459
460 // two main options: a list of lists (or tuples), or a list of objects; the
461 // former are emplace_back'ed, the latter push_back'ed
462 PyObject* fi = PySequence_GetItem(PyTuple_GET_ITEM(args, 0), 0);
463 if (!fi)
464 PyErr_Clear();
465 if (fi && (PyTuple_CheckExact(fi) || PyList_CheckExact(fi))) {
466 // use emplace_back to construct the vector entries one by one
467 PyObject* eb_call = PyObject_GetAttrString(vecin, (char*)"emplace_back");
468 PyObject* vtype =
470 bool value_is_vector = false;
471 if (vtype && PyLong_Check(vtype)) {
472 // if the value_type is a vector, then allow for initialization from
473 // sequences
474 if (interop::GetTypeAsString(PyLong_AsVoidPtr(vtype))
475 .rfind("std::vector", 0) != std::string::npos)
476 value_is_vector = true;
477 } else
478 PyErr_Clear();
479 Py_XDECREF(vtype);
480
481 if (eb_call) {
482 PyObject* eb_args;
483 for (int i = 0; /* until break */; ++i) {
484 PyObject* item = getter->get();
485 if (item) {
486 if (value_is_vector && PySequence_Check(item)) {
487 eb_args = PyTuple_New(1);
488 PyTuple_SET_ITEM(eb_args, 0, item);
489 } else if (PyTuple_CheckExact(item)) {
490 eb_args = item;
491 } else if (PyList_CheckExact(item)) {
492 Py_ssize_t isz = PyList_GET_SIZE(item);
493 eb_args = PyTuple_New(isz);
494 for (Py_ssize_t j = 0; j < isz; ++j) {
495 PyObject* iarg = PyList_GET_ITEM(item, j);
496 Py_INCREF(iarg);
497 PyTuple_SET_ITEM(eb_args, j, iarg);
498 }
499 Py_DECREF(item);
500 } else {
501 Py_DECREF(item);
502 PyErr_Format(PyExc_TypeError, "argument %d is not a tuple or list",
503 i);
504 fill_ok = false;
505 break;
506 }
507 PyObject* ebres = PyObject_CallObject(eb_call, eb_args);
508 Py_DECREF(eb_args);
509 if (!ebres) {
510 fill_ok = false;
511 break;
512 }
513 Py_DECREF(ebres);
514 } else {
515 if (PyErr_Occurred()) {
516 if (!(PyErr_ExceptionMatches(PyExc_IndexError) ||
517 PyErr_ExceptionMatches(PyExc_StopIteration)))
518 fill_ok = false;
519 else {
520 PyErr_Clear();
521 }
522 }
523 break;
524 }
525 }
526 Py_DECREF(eb_call);
527 }
528 } else {
529 // use push_back to add the vector entries one by one
530 PyObject* pb_call = PyObject_GetAttrString(vecin, (char*)"push_back");
531 if (pb_call) {
532 for (;;) {
533 PyObject* item = getter->get();
534 if (item) {
535 PyObject* pbres =
536 PyObject_CallFunctionObjArgs(pb_call, item, nullptr);
537 Py_DECREF(item);
538 if (!pbres) {
539 fill_ok = false;
540 break;
541 }
542 Py_DECREF(pbres);
543 } else {
544 if (PyErr_Occurred()) {
545 if (!(PyErr_ExceptionMatches(PyExc_IndexError) ||
546 PyErr_ExceptionMatches(PyExc_StopIteration)))
547 fill_ok = false;
548 else {
549 PyErr_Clear();
550 }
551 }
552 break;
553 }
554 }
555 Py_DECREF(pb_call);
556 }
557 }
558 Py_XDECREF(fi);
559
560 return fill_ok;
561}
562
563PyObject* VectorIAdd(PyObject* self, PyObject* args, PyObject* /* kwds */) {
564 // Implement fast __iadd__ on std::vector (generic __iadd__ is in Python)
565 ItemGetter* getter = GetGetter(args);
566
567 if (getter) {
568 bool fill_ok = FillVector(self, args, getter);
569 delete getter;
570
571 if (!fill_ok)
572 return nullptr;
573
574 Py_INCREF(self);
575 return self;
576 }
577
578 // if no getter, it could still be b/c we have a buffer (e.g. numpy); looping
579 // over a buffer here is slow, so use insert() instead
580 if (PyTuple_GET_SIZE(args) == 1) {
581 PyObject* fi = PyTuple_GET_ITEM(args, 0);
582 if (PyObject_CheckBuffer(fi) &&
583 !(PyUnicode_Check(fi) || PyBytes_Check(fi))) {
584 PyObject* vend = PyObject_CallMethodNoArgs(self, PyStrings::gEnd);
585 if (vend) {
586 // when __iadd__ is overriden, the operation does not end with
587 // calling the __iadd__ method, but also assigns the result to the
588 // lhs of the iadd. For example, performing vec += arr, Python
589 // first calls our override, and then does vec = vec.iadd(arr).
590 PyObject* it = PyObject_CallMethodObjArgs(self, PyStrings::gInsert,
591 vend, fi, nullptr);
592 Py_DECREF(vend);
593
594 if (!it)
595 return nullptr;
596
597 Py_DECREF(it);
598 // Assign the result of the __iadd__ override to the std::vector
599 Py_INCREF(self);
600 return self;
601 }
602 }
603 }
604
605 if (!PyErr_Occurred())
606 PyErr_SetString(PyExc_TypeError, "argument is not iterable");
607 return nullptr; // error already set
608}
609
610PyObject* VectorInit(PyObject* self, PyObject* args, PyObject* /* kwds */) {
611 // Specialized vector constructor to allow construction from containers;
612 // allowing such construction from initializer_list instead would possible,
613 // but can be error-prone. This use case is common enough for std::vector to
614 // implement it directly, except for arrays (which can be passed wholesale)
615 // and strings (which won't convert properly as they'll be seen as buffers)
616
617 ItemGetter* getter = GetGetter(args);
618
619 if (getter) {
620 // construct an empty vector, then back-fill it
621 PyObject* result = PyObject_CallMethodNoArgs(self, PyStrings::gRealInit);
622 if (!result) {
623 delete getter;
624 return nullptr;
625 }
626
627 bool fill_ok = FillVector(self, args, getter);
628 delete getter;
629
630 if (!fill_ok) {
631 Py_DECREF(result);
632 return nullptr;
633 }
634
635 return result;
636 }
637
638 // The given argument wasn't iterable: simply forward to regular constructor
639 PyObject* realInit = PyObject_GetAttr(self, PyStrings::gRealInit);
640 if (realInit) {
641 PyObject* result = PyObject_Call(realInit, args, nullptr);
642 Py_DECREF(realInit);
643 return result;
644 }
645
646 return nullptr;
647}
648
649//---------------------------------------------------------------------------
650PyObject* VectorData(PyObject* self, PyObject*) {
651 PyObject* pydata = CallPyObjMethod(self, "__real_data");
652 if (!LowLevelView_Check(pydata) && !CPPInstance_Check(pydata))
653 return pydata;
654
655 PyObject* pylen = PyObject_CallMethodNoArgs(self, PyStrings::gSize);
656 if (!pylen) {
657 PyErr_Clear();
658 return pydata;
659 }
660
661 long clen = PyLong_AsLong(pylen);
662 Py_DECREF(pylen);
663
664 if (CPPInstance_Check(pydata)) {
665 ((CPPInstance*)pydata)->CastToArray(clen);
666 return pydata;
667 }
668
669 ((LowLevelView*)pydata)->resize((size_t)clen);
670 return pydata;
671}
672
673//---------------------------------------------------------------------------
674PyObject* VectorArray(PyObject* self, PyObject* args, PyObject* kwargs) {
675 PyObject* pydata = VectorData(self, nullptr);
676 PyObject* arrcall = PyObject_GetAttr(pydata, PyStrings::gArray);
677 PyObject* newarr = PyObject_Call(arrcall, args, kwargs);
678 Py_DECREF(arrcall);
679 Py_DECREF(pydata);
680 return newarr;
681}
682
683//-----------------------------------------------------------------------------
684static PyObject* vector_iter(PyObject* v) {
685 vectoriterobject* vi = PyObject_GC_New(vectoriterobject, &VectorIter_Type);
686 if (!vi)
687 return nullptr;
688
689 Py_INCREF(v);
690 vi->ii_container = v;
691
692 // tell the iterator code to set a life line if this container is a temporary
694#if PY_VERSION_HEX >= 0x030e0000
697#else
698 if (Py_REFCNT(v) <= 1 || (((CPPInstance*)v)->fFlags & CPPInstance::kIsValue))
699#endif
701
702 Py_INCREF(v);
703
704 PyObject* pyvalue_type =
705 PyObject_GetAttr((PyObject*)Py_TYPE(v), PyStrings::gValueTypePtr);
706 if (pyvalue_type) {
707 PyObject* pyvalue_size =
709 if (pyvalue_size) {
710 vi->vi_stride = PyLong_AsLong(pyvalue_size);
711 Py_DECREF(pyvalue_size);
712 } else {
713 PyErr_Clear();
714 vi->vi_stride = 0;
715 }
716
717 if (PyLong_Check(pyvalue_type)) {
718 interop::TCppType_t value_type = PyLong_AsVoidPtr(pyvalue_type);
719 value_type = interop::ResolveType(value_type);
720 vi->vi_klass = interop::GetScopeFromType(value_type);
721 if (!vi->vi_klass) {
722 // look for a special case of pointer to a class type (which is a
723 // builtin, but it is more useful to treat it polymorphically by
724 // allowing auto-downcasts)
725 const std::string& clean_type = TypeManip::clean_type(
726 interop::GetTypeAsString(value_type), false, false);
728 if (c &&
729 TypeManip::compound(interop::GetTypeAsString(value_type)) == "*") {
730 vi->vi_klass = c;
732 }
733 }
734 if (interop::IsPointerType(value_type))
736 if (vi->vi_klass) {
737 vi->vi_converter = nullptr;
738 if (!vi->vi_flags) {
739 value_type = interop::ResolveType(value_type);
740 if (interop::GetTypeAsString(value_type).back() !=
741 '*') // meaning, object stored by-value
743 }
744 } else
745 vi->vi_converter = CreateConverter(value_type);
746 if (!vi->vi_stride)
747 vi->vi_stride = interop::SizeOfType(value_type);
748
749 } else if (CPPScope_Check(pyvalue_type)) {
750 vi->vi_klass = ((CPPClass*)pyvalue_type)->fCppType;
751 vi->vi_converter = nullptr;
752 if (!vi->vi_stride)
754 if (!vi->vi_flags)
756 }
757
758 PyObject* pydata = CallPyObjMethod(v, "__real_data");
759 if (!pydata || Utility::GetBuffer(pydata, '*', 1, vi->vi_data, false) == 0)
760 vi->vi_data = CPPInstance_Check(pydata)
761 ? ((CPPInstance*)pydata)->GetObjectRaw()
762 : nullptr;
763 Py_XDECREF(pydata);
764
765 } else {
766 PyErr_Clear();
767 vi->vi_data = nullptr;
768 vi->vi_stride = 0;
769 vi->vi_converter = nullptr;
770 vi->vi_klass = nullptr;
771 vi->vi_flags = 0;
772 }
773
774 Py_XDECREF(pyvalue_type);
775
776 vi->ii_pos = 0;
777 vi->ii_len = PySequence_Size(v);
778
779 PyObject_GC_Track(vi);
780 return (PyObject*)vi;
781}
782
783PyObject* VectorGetItem(CPPInstance* self, PySliceObject* index) {
784 // Implement python's __getitem__ for std::vector<>s.
785 if (PySlice_Check(index)) {
786 if (!self->GetObject()) {
787 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
788 return nullptr;
789 }
790
791 PyObject* pyclass = (PyObject*)Py_TYPE((PyObject*)self);
792 PyObject* nseq = PyObject_CallObject(pyclass, nullptr);
793
794 Py_ssize_t start, stop, step;
795 PySlice_GetIndices((PyObject*)index, PyObject_Length((PyObject*)self),
796 &start, &stop, &step);
797
798 const Py_ssize_t nlen = PySequence_Size((PyObject*)self);
799 if (!AdjustSlice(nlen, start, stop, step))
800 return nseq;
801
802 const Py_ssize_t sign = step < 0 ? -1 : 1;
803 for (Py_ssize_t i = start; i * sign < stop * sign; i += step) {
804 PyObject* pyidx = PyLong_FromSsize_t(i);
805 PyObject* item = PyObject_CallMethodOneArg((PyObject*)self,
807 CallPyObjMethod(nseq, "push_back", item);
808 Py_DECREF(item);
809 Py_DECREF(pyidx);
810 }
811
812 return nseq;
813 }
814
815 return CallSelfIndex(self, (PyObject*)index, PyStrings::gGetNoCheck);
816}
817
818static interop::TCppScope_t sVectorBoolTypeID;
819
820PyObject* VectorBoolGetItem(CPPInstance* self, PyObject* idx) {
821 // std::vector<bool> is a special-case in C++, and its return type depends on
822 // the compiler: treat it special here as well
823 if (!CPPInstance_Check(self) || self->ObjectIsA() != sVectorBoolTypeID) {
824 PyErr_Format(PyExc_TypeError,
825 "require object of type std::vector<bool>, but %s given",
826 interop::GetScopedFinalName(self->ObjectIsA()).c_str());
827 return nullptr;
828 }
829
830 if (!self->GetObject()) {
831 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
832 return nullptr;
833 }
834
835 if (PySlice_Check(idx)) {
836 PyObject* pyclass = (PyObject*)Py_TYPE((PyObject*)self);
837 PyObject* nseq = PyObject_CallObject(pyclass, nullptr);
838
839 Py_ssize_t start, stop, step;
840 PySlice_GetIndices((PyObject*)idx, PyObject_Length((PyObject*)self), &start,
841 &stop, &step);
842 const Py_ssize_t nlen = PySequence_Size((PyObject*)self);
843 if (!AdjustSlice(nlen, start, stop, step))
844 return nseq;
845
846 const Py_ssize_t sign = step < 0 ? -1 : 1;
847 for (Py_ssize_t i = start; i * sign < stop * sign; i += step) {
848 PyObject* pyidx = PyLong_FromSsize_t(i);
849 PyObject* item = PyObject_CallMethodOneArg((PyObject*)self,
850 PyStrings::gGetItem, pyidx);
851 CallPyObjMethod(nseq, "push_back", item);
852 Py_DECREF(item);
853 Py_DECREF(pyidx);
854 }
855
856 return nseq;
857 }
858
859 PyObject* pyindex = PyStyleIndex((PyObject*)self, idx);
860 if (!pyindex)
861 return nullptr;
862
863 int index = (int)PyLong_AsLong(pyindex);
864 Py_DECREF(pyindex);
865
866 // get hold of the actual std::vector<bool> (no cast, as vector is never a
867 // base)
868 std::vector<bool>* vb = (std::vector<bool>*)self->GetObject();
869
870 // finally, return the value
871 if (bool((*vb)[index]))
874}
875
876PyObject* VectorBoolSetItem(CPPInstance* self, PyObject* args) {
877 // std::vector<bool> is a special-case in C++, and its return type depends on
878 // the compiler: treat it special here as well
879 if (!CPPInstance_Check(self) || self->ObjectIsA() != sVectorBoolTypeID) {
880 PyErr_Format(PyExc_TypeError,
881 "require object of type std::vector<bool>, but %s given",
882 interop::GetScopedFinalName(self->ObjectIsA()).c_str());
883 return nullptr;
884 }
885
886 if (!self->GetObject()) {
887 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
888 return nullptr;
889 }
890
891 int bval = 0;
892 PyObject* idx = nullptr;
893 if (!PyArg_ParseTuple(args, const_cast<char*>("Oi:__setitem__"), &idx, &bval))
894 return nullptr;
895
896 PyObject* pyindex = PyStyleIndex((PyObject*)self, idx);
897 if (!pyindex)
898 return nullptr;
899
900 int index = (int)PyLong_AsLong(pyindex);
901 Py_DECREF(pyindex);
902
903 // get hold of the actual std::vector<bool> (no cast, as vector is never a
904 // base)
905 std::vector<bool>* vb = (std::vector<bool>*)self->GetObject();
906
907 // finally, set the value
908 (*vb)[index] = (bool)bval;
909
911}
912
913//- array behavior as primitives ----------------------------------------------
914PyObject* ArrayInit(PyObject* self, PyObject* args, PyObject* /* kwds */) {
915 // std::array is normally only constructed using aggregate initialization,
916 // which is a concept that does not exist in python, so use this custom
917 // constructor to to fill the array using setitem
918
919 if (args && PyTuple_GET_SIZE(args) == 1 &&
920 PySequence_Check(PyTuple_GET_ITEM(args, 0))) {
921 // construct the empty array, then fill it
922 PyObject* result = PyObject_CallMethodNoArgs(self, PyStrings::gRealInit);
923 if (!result)
924 return nullptr;
925
926 PyObject* items = PyTuple_GET_ITEM(args, 0);
927 Py_ssize_t fillsz = PySequence_Size(items);
928 if (PySequence_Size(self) != fillsz) {
929 PyErr_Format(PyExc_ValueError,
930 "received sequence of size %zd where %zd expected", fillsz,
931 PySequence_Size(self));
932 Py_DECREF(result);
933 return nullptr;
934 }
935
936 PyObject* si_call = PyObject_GetAttr(self, PyStrings::gSetItem);
937 for (Py_ssize_t i = 0; i < fillsz; ++i) {
938 PyObject* item = PySequence_GetItem(items, i);
939 PyObject* index = PyLong_FromSsize_t(i);
940 PyObject* sires =
941 PyObject_CallFunctionObjArgs(si_call, index, item, nullptr);
942 Py_DECREF(index);
943 Py_DECREF(item);
944 if (!sires) {
945 Py_DECREF(si_call);
946 Py_DECREF(result);
947 return nullptr;
948 } else
949 Py_DECREF(sires);
950 }
951 Py_DECREF(si_call);
952
953 return result;
954 } else
955 PyErr_Clear();
956
957 // The given argument wasn't iterable: simply forward to regular constructor
958 PyObject* realInit = PyObject_GetAttr(self, PyStrings::gRealInit);
959 if (realInit) {
960 PyObject* result = PyObject_Call(realInit, args, nullptr);
961 Py_DECREF(realInit);
962 return result;
963 }
964
965 return nullptr;
966}
967
968//- map behavior as primitives ------------------------------------------------
969static PyObject* MapFromPairs(PyObject* self, PyObject* pairs) {
970 // construct an empty map, then fill it with the key, value pairs
971 PyObject* result = PyObject_CallMethodNoArgs(self, PyStrings::gRealInit);
972 if (!result)
973 return nullptr;
974
975 PyObject* si_call = PyObject_GetAttr(self, PyStrings::gSetItem);
976 for (Py_ssize_t i = 0; i < PySequence_Size(pairs); ++i) {
977 PyObject* pair = PySequence_GetItem(pairs, i);
978 PyObject* sires = nullptr;
979 if (pair && PySequence_Check(pair) && PySequence_Size(pair) == 2) {
980 PyObject* key = PySequence_GetItem(pair, 0);
981 PyObject* value = PySequence_GetItem(pair, 1);
982 sires = PyObject_CallFunctionObjArgs(si_call, key, value, nullptr);
983 Py_DECREF(value);
984 Py_DECREF(key);
985 }
986 Py_DECREF(pair);
987 if (!sires) {
988 Py_DECREF(si_call);
989 Py_DECREF(result);
990 if (!PyErr_Occurred())
991 PyErr_SetString(
992 PyExc_TypeError,
993 "Failed to fill map (argument not a dict or sequence of pairs)");
994 return nullptr;
995 } else
996 Py_DECREF(sires);
997 }
998 Py_DECREF(si_call);
999
1000 return result;
1001}
1002
1003PyObject* MapInit(PyObject* self, PyObject* args, PyObject* /* kwds */) {
1004 // Specialized map constructor to allow construction from mapping containers
1005 // and from tuples of pairs ("initializer_list style").
1006
1007 // PyMapping_Check is not very discriminatory, as it basically only checks for
1008 // the existence of __getitem__, hence the most common cases of tuple and
1009 // list are dropped straight-of-the-bat (the PyMapping_Items call will fail on
1010 // them).
1011 if (PyTuple_GET_SIZE(args) == 1 &&
1012 PyMapping_Check(PyTuple_GET_ITEM(args, 0)) &&
1013 !(PyTuple_Check(PyTuple_GET_ITEM(args, 0)) ||
1014 PyList_Check(PyTuple_GET_ITEM(args, 0)))) {
1015 PyObject* assoc = PyTuple_GET_ITEM(args, 0);
1016 // in p3, PyMapping_Items isn't a macro, but a function that short-circuits
1017 // dict
1018 PyObject* items = PyMapping_Items(assoc);
1019 if (items && PySequence_Check(items)) {
1020 PyObject* result = MapFromPairs(self, items);
1021 Py_DECREF(items);
1022 return result;
1023 }
1024
1025 Py_XDECREF(items);
1026 PyErr_Clear();
1027
1028 // okay to fall through as long as 'self' has not been created (is done in
1029 // MapFromPairs)
1030 }
1031
1032 // tuple of pairs case (some mapping types are sequences)
1033 if (PyTuple_GET_SIZE(args) == 1 &&
1034 PySequence_Check(PyTuple_GET_ITEM(args, 0)))
1035 return MapFromPairs(self, PyTuple_GET_ITEM(args, 0));
1036
1037 // The given argument wasn't a mapping or tuple of pairs: forward to regular
1038 // constructor
1039 PyObject* realInit = PyObject_GetAttr(self, PyStrings::gRealInit);
1040 if (realInit) {
1041 PyObject* result = PyObject_Call(realInit, args, nullptr);
1042 Py_DECREF(realInit);
1043 return result;
1044 }
1045
1046 return nullptr;
1047}
1048
1049PyObject* STLContainsWithFind(PyObject* self, PyObject* obj) {
1050 // Implement python's __contains__ for std::map/std::set
1051 PyObject* result = nullptr;
1052
1053 PyObject* iter = CallPyObjMethod(self, "find", obj);
1054 if (CPPInstance_Check(iter)) {
1055 PyObject* end = PyObject_CallMethodNoArgs(self, PyStrings::gEnd);
1056 if (CPPInstance_Check(end)) {
1057 if (!PyObject_RichCompareBool(iter, end, Py_EQ)) {
1058 Py_INCREF(Py_True);
1059 result = Py_True;
1060 }
1061 }
1062 Py_XDECREF(end);
1063 }
1064 Py_XDECREF(iter);
1065
1066 if (!result) {
1067 PyErr_Clear(); // e.g. wrong argument type, which should always lead to
1068 // False
1069 Py_INCREF(Py_False);
1070 result = Py_False;
1071 }
1072
1073 return result;
1074}
1075
1076//- set behavior as primitives ------------------------------------------------
1077PyObject* SetInit(PyObject* self, PyObject* args, PyObject* /* kwds */) {
1078 // Specialized set constructor to allow construction from Python sets.
1079 if (PyTuple_GET_SIZE(args) == 1 && PySet_Check(PyTuple_GET_ITEM(args, 0))) {
1080 PyObject* pyset = PyTuple_GET_ITEM(args, 0);
1081
1082 // construct an empty set, then fill it
1083 PyObject* result = PyObject_CallMethodNoArgs(self, PyStrings::gRealInit);
1084 if (!result)
1085 return nullptr;
1086
1087 PyObject* iter = PyObject_GetIter(pyset);
1088 if (iter) {
1089 PyObject* ins_call = PyObject_GetAttrString(self, (char*)"insert");
1090
1091 IterItemGetter getter{iter};
1092 Py_DECREF(iter);
1093
1094 PyObject* item = getter.get();
1095 while (item) {
1096 PyObject* insres =
1097 PyObject_CallFunctionObjArgs(ins_call, item, nullptr);
1098 Py_DECREF(item);
1099 if (!insres) {
1100 Py_DECREF(ins_call);
1101 Py_DECREF(result);
1102 return nullptr;
1103 } else
1104 Py_DECREF(insres);
1105 item = getter.get();
1106 }
1107 Py_DECREF(ins_call);
1108 }
1109
1110 return result;
1111 }
1112
1113 // The given argument wasn't iterable: simply forward to regular constructor
1114 PyObject* realInit = PyObject_GetAttr(self, PyStrings::gRealInit);
1115 if (realInit) {
1116 PyObject* result = PyObject_Call(realInit, args, nullptr);
1117 Py_DECREF(realInit);
1118 return result;
1119 }
1120
1121 return nullptr;
1122}
1123
1124//- STL container iterator support --------------------------------------------
1125static const ptrdiff_t PS_END_ADDR = 7; // non-aligned address, so no clash
1126static const ptrdiff_t PS_FLAG_ADDR = 11; // id.
1127static const ptrdiff_t PS_COLL_ADDR = 13; // id.
1128
1129PyObject* STLIterNext(PyObject* self); // defined below; used by STLSequenceIter
1130
1131PyObject* LLSequenceIter(PyObject* self) {
1132 // Implement python's __iter__ for low level views used through STL-type
1133 // begin()/end()
1134 PyObject* iter = PyObject_CallMethodNoArgs(self, PyStrings::gBegin);
1135
1136 if (LowLevelView_Check(iter)) {
1137 // builtin pointer iteration: can only succeed if a size is available
1138 Py_ssize_t sz = PySequence_Size(self);
1139 if (sz == -1) {
1140 Py_DECREF(iter);
1141 return nullptr;
1142 }
1143 PyObject* lliter = Py_TYPE(iter)->tp_iter(iter);
1144 ((indexiterobject*)lliter)->ii_len = sz;
1145 Py_DECREF(iter);
1146 return lliter;
1147 }
1148
1149 if (iter) {
1150 Py_DECREF(iter);
1151 PyErr_SetString(PyExc_TypeError,
1152 "unrecognized iterator type for low level views");
1153 }
1154
1155 return nullptr;
1156}
1157
1158PyObject* STLSequenceIter(PyObject* self) {
1159 // Implement python's __iter__ for std::iterator<>s
1160 PyObject* iter = PyObject_CallMethodNoArgs(self, PyStrings::gBegin);
1161 if (iter) {
1162 PyObject* end = PyObject_CallMethodNoArgs(self, PyStrings::gEnd);
1163 if (end) {
1164 if (CPPInstance_Check(iter)) {
1165 // Guarantee the returned iterator implements Python's iterator
1166 // protocol. The deferred, name-based __next__ install can miss when an
1167 // iterator's canonical return-type spelling and its scoped-final-name
1168 // diverge (e.g libc++'s std::__1::__wrap_iter on macOS) leaving the
1169 // type without
1170 // __next__, so iter() rejects it. begin()/end() resolving here proves
1171 // this is an STL forward iterator, so install the protocol now if it is
1172 // still absent
1173 PyTypeObject* itype = Py_TYPE(iter);
1174 if (!PyIter_Check(iter)) { // no tp_iternext, or the
1175 // _PyObject_NextNotImplemented sentinel
1176 itype->tp_iternext = (iternextfunc)STLIterNext;
1177 Utility::AddToClass((PyObject*)itype, "__next__",
1178 (PyCFunction)STLIterNext, METH_NOARGS);
1179 if (!itype->tp_iter) {
1180 itype->tp_iter = (getiterfunc)PyObject_SelfIter;
1181 Utility::AddToClass((PyObject*)itype, "__iter__",
1182 (PyCFunction)PyObject_SelfIter, METH_NOARGS);
1183 }
1184 PyType_Modified(itype);
1185 }
1186 // use the data member cache to store extra state on the iterator
1187 // object, without it being visible on the Python side
1188 auto& dmc = ((CPPInstance*)iter)->GetDatamemberCache();
1189 dmc.push_back(std::make_pair(PS_END_ADDR, end));
1190
1191 // set a flag, indicating first iteration (reset in __next__)
1192 Py_INCREF(Py_False);
1193 dmc.push_back(std::make_pair(PS_FLAG_ADDR, Py_False));
1194
1195 // make sure the iterated over collection remains alive for the duration
1196 Py_INCREF(self);
1197 dmc.push_back(std::make_pair(PS_COLL_ADDR, self));
1198 } else {
1199 // could store "end" on the object's dictionary anyway, but if end()
1200 // returns a user-customized object, then its __next__ is probably
1201 // custom, too
1202 Py_DECREF(end);
1203 }
1204 }
1205 }
1206 return iter;
1207}
1208
1209//- generic iterator support over a sequence with operator[] and size ---------
1210//-----------------------------------------------------------------------------
1211static PyObject* index_iter(PyObject* c) {
1212 indexiterobject* ii = PyObject_GC_New(indexiterobject, &IndexIter_Type);
1213 if (!ii)
1214 return nullptr;
1215
1216 Py_INCREF(c);
1217 ii->ii_container = c;
1218 ii->ii_pos = 0;
1219 ii->ii_len = PySequence_Size(c);
1220
1221 PyObject_GC_Track(ii);
1222 return (PyObject*)ii;
1223}
1224
1225//- safe indexing for STL-like vector w/o iterator dictionaries ---------------
1226/* replaced by indexiterobject iteration, but may still have some future use ...
1227PyObject* CheckedGetItem(PyObject* self, PyObject* obj)
1228{
1229// Implement a generic python __getitem__ for STL-like classes that are missing
1230the
1231// reflection info for their iterators. This is then used for iteration by means
1232of
1233// consecutive indices, it such index is of integer type.
1234 Py_ssize_t size = PySequence_Size(self);
1235 Py_ssize_t idx = PyLong_AsSsize_t(obj);
1236 if ((size == (Py_ssize_t)-1 || idx == (Py_ssize_t)-1) && PyErr_Occurred()) {
1237 // argument conversion problem: let method itself resolve anew and report
1238 PyErr_Clear();
1239 return PyObject_CallMethodOneArg(self, PyStrings::gGetNoCheck, obj);
1240 }
1241
1242 bool inbounds = false;
1243 if (idx < 0) idx += size;
1244 if (0 <= idx && 0 <= size && idx < size)
1245 inbounds = true;
1246
1247 if (inbounds)
1248 return PyObject_CallMethodOneArg(self, PyStrings::gGetNoCheck, obj);
1249 else
1250 PyErr_SetString( PyExc_IndexError, "index out of range" );
1251
1252 return nullptr;
1253}*/
1254
1255//- pair as sequence to allow tuple unpacking --------------------------------
1256PyObject* PairUnpack(PyObject* self, PyObject* pyindex) {
1257 // For std::map<> iteration, unpack std::pair<>s into tuples for the loop.
1258 long idx = PyLong_AsLong(pyindex);
1259 if (idx == -1 && PyErr_Occurred())
1260 return nullptr;
1261
1262 if (!CPPInstance_Check(self) || !((CPPInstance*)self)->GetObject()) {
1263 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
1264 return nullptr;
1265 }
1266
1267 if ((int)idx == 0)
1268 return PyObject_GetAttr(self, PyStrings::gFirst);
1269 else if ((int)idx == 1)
1270 return PyObject_GetAttr(self, PyStrings::gSecond);
1271
1272 // still here? Trigger stop iteration
1273 PyErr_SetString(PyExc_IndexError, "out of bounds");
1274 return nullptr;
1275}
1276
1277//- simplistic len() functions -----------------------------------------------
1278PyObject* ReturnTwo(CPPInstance*, PyObject*) { return PyLong_FromLong(2); }
1279
1280//- shared/unique_ptr behavior -----------------------------------------------
1281PyObject* SmartPtrInit(PyObject* self, PyObject* args, PyObject* /* kwds */) {
1282 // since the shared/unique pointer will take ownership, we need to relinquish
1283 // it
1284 PyObject* realInit = PyObject_GetAttr(self, PyStrings::gRealInit);
1285 if (realInit) {
1286 PyObject* result = PyObject_Call(realInit, args, nullptr);
1287 Py_DECREF(realInit);
1288 if (result && PyTuple_GET_SIZE(args) == 1 &&
1289 CPPInstance_Check(PyTuple_GET_ITEM(args, 0))) {
1290 CPPInstance* cppinst = (CPPInstance*)PyTuple_GET_ITEM(args, 0);
1291 if (!(cppinst->fFlags & CPPInstance::kIsSmartPtr))
1292 cppinst->CppOwns();
1293 }
1294 return result;
1295 }
1296 return nullptr;
1297}
1298
1299//- string behavior as primitives --------------------------------------------
1300// TODO: this is wrong, b/c it doesn't order
1301static int PyObject_Compare(PyObject* one, PyObject* other) {
1302 return !PyObject_RichCompareBool(one, other, Py_EQ);
1303}
1304static inline PyObject* cpyrt_PyString_FromCppString(std::string_view s,
1305 bool native = true) {
1306 if (native)
1307 return PyBytes_FromStringAndSize(s.data(), s.size());
1308 return PyUnicode_FromStringAndSize(s.data(), s.size());
1309}
1310
1311static inline PyObject* cpyrt_PyString_FromCppString(std::wstring_view s,
1312 bool native = true) {
1313 PyObject* pyobj = PyUnicode_FromWideChar(s.data(), s.size());
1314 if (pyobj && native) {
1315 PyObject* pybytes = PyUnicode_AsEncodedString(pyobj, "UTF-8", "strict");
1316 Py_DECREF(pyobj);
1317 pyobj = pybytes;
1318 }
1319 return pyobj;
1320}
1321
1322#define CPPJIT_IMPL_STRING_PYTHONIZATION(type, name) \
1323 static inline PyObject* name##StringGetData(PyObject* self, \
1324 bool native = true) { \
1325 if (cpyrt::CPPInstance_Check(self)) { \
1326 type* obj = ((type*)((CPPInstance*)self)->GetObject()); \
1327 if (obj) \
1328 return cpyrt_PyString_FromCppString(*obj, native); \
1329 } \
1330 PyErr_Format(PyExc_TypeError, "object mismatch (%s expected)", #type); \
1331 return nullptr; \
1332 } \
1333 \
1334 PyObject* name##StringStr(PyObject* self) { \
1335 PyObject* pyobj = name##StringGetData(self, false); \
1336 if (!pyobj) { \
1337 /* do a native conversion to make printing possible (debatable) */ \
1338 PyErr_Clear(); \
1339 PyObject* pybytes = name##StringGetData(self, true); \
1340 if (pybytes) { /* should not fail */ \
1341 pyobj = PyObject_Str(pybytes); \
1342 Py_DECREF(pybytes); \
1343 } \
1344 } \
1345 return pyobj; \
1346 } \
1347 \
1348 PyObject* name##StringBytes(PyObject* self) { \
1349 return name##StringGetData(self, true); \
1350 } \
1351 \
1352 PyObject* name##StringRepr(PyObject* self) { \
1353 PyObject* data = name##StringGetData(self, true); \
1354 if (data) { \
1355 PyObject* repr = PyObject_Repr(data); \
1356 Py_DECREF(data); \
1357 return repr; \
1358 } \
1359 return nullptr; \
1360 } \
1361 \
1362 PyObject* name##StringIsEqual(PyObject* self, PyObject* obj) { \
1363 PyObject* data = name##StringGetData(self, PyBytes_Check(obj)); \
1364 if (data) { \
1365 PyObject* result = PyObject_RichCompare(data, obj, Py_EQ); \
1366 Py_DECREF(data); \
1367 return result; \
1368 } \
1369 return nullptr; \
1370 } \
1371 \
1372 PyObject* name##StringIsNotEqual(PyObject* self, PyObject* obj) { \
1373 PyObject* data = name##StringGetData(self, PyBytes_Check(obj)); \
1374 if (data) { \
1375 PyObject* result = PyObject_RichCompare(data, obj, Py_NE); \
1376 Py_DECREF(data); \
1377 return result; \
1378 } \
1379 return nullptr; \
1380 }
1381
1382// Only define STLStringCompare:
1383#define CPPJIT_IMPL_STRING_PYTHONIZATION_CMP(type, name) \
1384 CPPJIT_IMPL_STRING_PYTHONIZATION(type, name) \
1385 PyObject* name##StringCompare(PyObject* self, PyObject* obj) { \
1386 PyObject* data = name##StringGetData(self, PyBytes_Check(obj)); \
1387 int result = 0; \
1388 if (data) { \
1389 result = PyObject_Compare(data, obj); \
1390 Py_DECREF(data); \
1391 } \
1392 if (PyErr_Occurred()) \
1393 return nullptr; \
1394 return PyLong_FromLong(result); \
1395 }
1396
1398CPPJIT_IMPL_STRING_PYTHONIZATION_CMP(std::wstring, STLW)
1399CPPJIT_IMPL_STRING_PYTHONIZATION_CMP(std::string_view, STLView)
1400
1401static inline std::string* GetSTLString(CPPInstance* self) {
1402 if (!CPPInstance_Check(self)) {
1403 PyErr_SetString(PyExc_TypeError, "std::string object expected");
1404 return nullptr;
1405 }
1406
1407 std::string* obj = (std::string*)self->GetObject();
1408 if (!obj)
1409 PyErr_SetString(PyExc_ReferenceError, "attempt to access a null-pointer");
1410
1411 return obj;
1412}
1413
1414PyObject* STLStringDecode(CPPInstance* self, PyObject* args, PyObject* kwds) {
1415 std::string* obj = GetSTLString(self);
1416 if (!obj)
1417 return nullptr;
1418
1419 char* keywords[] = {(char*)"encoding", (char*)"errors", (char*)nullptr};
1420 const char* encoding = nullptr;
1421 const char* errors = nullptr;
1422 if (!PyArg_ParseTupleAndKeywords(args, kwds, const_cast<char*>("s|s"),
1423 keywords, &encoding, &errors))
1424 return nullptr;
1425
1426 return PyUnicode_Decode(obj->data(), obj->size(), encoding, errors);
1427}
1428
1429PyObject* STLStringContains(CPPInstance* self, PyObject* pyobj) {
1430 std::string* obj = GetSTLString(self);
1431 if (!obj)
1432 return nullptr;
1433
1434 const char* needle = PyUnicode_AsUTF8(pyobj);
1435 if (!needle)
1436 return nullptr;
1437
1438 if (obj->find(needle) != std::string::npos) {
1440 }
1441
1443}
1444
1445PyObject* STLStringReplace(CPPInstance* self, PyObject* args,
1446 PyObject* /*kwds*/) {
1447 std::string* obj = GetSTLString(self);
1448 if (!obj)
1449 return nullptr;
1450
1451 // both str and std::string have a method "replace", but the Python version
1452 // only accepts strings and takes no keyword arguments, whereas the C++
1453 // version has no overload that takes a string
1454
1455 if (2 <= PyTuple_GET_SIZE(args) &&
1456 PyUnicode_Check(PyTuple_GET_ITEM(args, 0))) {
1457 PyObject* pystr = PyUnicode_FromStringAndSize(obj->data(), obj->size());
1458 PyObject* meth = PyObject_GetAttrString(pystr, (char*)"replace");
1459 Py_DECREF(pystr);
1460 PyObject* result = PyObject_CallObject(meth, args);
1461 Py_DECREF(meth);
1462 return result;
1463 }
1464
1465 PyObject* cppreplace =
1466 PyObject_GetAttrString((PyObject*)self, (char*)"__cpp_replace");
1467 if (cppreplace) {
1468 PyObject* result = PyObject_Call(cppreplace, args, nullptr);
1469 Py_DECREF(cppreplace);
1470 return result;
1471 }
1472
1473 PyErr_SetString(PyExc_AttributeError,
1474 "\'std::string\' object has no attribute \'replace\'");
1475 return nullptr;
1476}
1477
1478#define CPYRT_STRING_FINDMETHOD(name, cppname, pyname) \
1479 PyObject* STLString##name(CPPInstance* self, PyObject* args, \
1480 PyObject* /*kwds*/) { \
1481 std::string* obj = GetSTLString(self); \
1482 if (!obj) \
1483 return nullptr; \
1484 \
1485 PyObject* cppmeth = \
1486 PyObject_GetAttrString((PyObject*)self, (char*)#cppname); \
1487 if (cppmeth) { \
1488 PyObject* result = PyObject_Call(cppmeth, args, nullptr); \
1489 Py_DECREF(cppmeth); \
1490 if (result) { \
1491 if (PyLongOrInt_AsULong64(result) == \
1492 (PY_ULONG_LONG)std::string::npos) { \
1493 Py_DECREF(result); \
1494 return PyLong_FromLong(-1); \
1495 } \
1496 return result; \
1497 } \
1498 PyErr_Clear(); \
1499 } \
1500 \
1501 PyObject* pystr = PyUnicode_FromStringAndSize(obj->data(), obj->size()); \
1502 PyObject* pymeth = PyObject_GetAttrString(pystr, (char*)#pyname); \
1503 Py_DECREF(pystr); \
1504 PyObject* result = PyObject_CallObject(pymeth, args); \
1505 Py_DECREF(pymeth); \
1506 return result; \
1507 }
1508
1509// both str and std::string have method "find" and "rfin"; try the C++ version
1510// first and fall back on the Python one in case of failure
1511// clang-format off
1512CPYRT_STRING_FINDMETHOD( Find, __cpp_find, find)
1513CPYRT_STRING_FINDMETHOD(RFind, __cpp_rfind, rfind)
1514// clang-format on
1515
1516PyObject* STLStringGetAttr(CPPInstance* self, PyObject* attr_name) {
1517 std::string* obj = GetSTLString(self);
1518 if (!obj)
1519 return nullptr;
1520
1521 PyObject* pystr = PyUnicode_FromStringAndSize(obj->data(), obj->size());
1522 PyObject* attr = PyObject_GetAttr(pystr, attr_name);
1523 Py_DECREF(pystr);
1524 return attr;
1525}
1526
1527Py_hash_t STLStringHash(PyObject* self) {
1528 // std::string objects hash to the same values as Python strings to allow
1529 // matches in dictionaries etc.
1530 PyObject* data = STLStringGetData(self, false);
1531 Py_hash_t h = PyUnicode_Type.tp_hash(data);
1532 Py_DECREF(data);
1533 return h;
1534}
1535
1536//- string_view behavior as primitive ----------------------------------------
1537PyObject* StringViewInit(PyObject* self, PyObject* args, PyObject* /* kwds */) {
1538 // if constructed from a Python unicode object, the constructor will convert
1539 // it to a temporary byte string, which is likely to go out of scope too soon;
1540 // so buffer it as needed
1541 PyObject* realInit = PyObject_GetAttr(self, PyStrings::gRealInit);
1542 if (realInit) {
1543 PyObject *strbuf = nullptr, *newArgs = nullptr;
1544 if (PyTuple_GET_SIZE(args) == 1) {
1545 PyObject* arg0 = PyTuple_GET_ITEM(args, 0);
1546 if (PyUnicode_Check(arg0)) {
1547 // convert to the expected bytes array to control the temporary
1548 strbuf = PyUnicode_AsEncodedString(arg0, "UTF-8", "strict");
1549 newArgs = PyTuple_New(1);
1550 Py_INCREF(strbuf);
1551 PyTuple_SET_ITEM(newArgs, 0, strbuf);
1552 } else if (PyBytes_Check(arg0)) {
1553 // tie the life time of the provided string to the string_view
1554 Py_INCREF(arg0);
1555 strbuf = arg0;
1556 }
1557 }
1558
1559 PyObject* result =
1560 PyObject_Call(realInit, newArgs ? newArgs : args, nullptr);
1561
1562 Py_XDECREF(newArgs);
1563 Py_DECREF(realInit);
1564
1565 // if construction was successful and a string buffer was used, add a
1566 // life line to it from the string_view bound object
1567 if (result && self && strbuf)
1568 PyObject_SetAttr(self, PyStrings::gLifeLine, strbuf);
1569 Py_XDECREF(strbuf);
1570
1571 return result;
1572 }
1573 return nullptr;
1574}
1575
1576//- STL iterator behavior ----------------------------------------------------
1577PyObject* STLIterNext(PyObject* self) {
1578 // Python iterator protocol __next__ for STL forward iterators.
1579 bool mustIncrement = true;
1580 PyObject* last = nullptr;
1581 if (CPPInstance_Check(self)) {
1582 auto& dmc = ((CPPInstance*)self)->GetDatamemberCache();
1583 for (auto& p : dmc) {
1584 if (p.first == PS_END_ADDR) {
1585 last = p.second;
1586 Py_INCREF(last);
1587 } else if (p.first == PS_FLAG_ADDR) {
1588 mustIncrement = p.second == Py_True;
1589 if (!mustIncrement) {
1590 Py_DECREF(p.second);
1591 Py_INCREF(Py_True);
1592 p.second = Py_True;
1593 }
1594 }
1595 }
1596 }
1597
1598 PyObject* next = nullptr;
1599 if (last) {
1600 // handle special case of empty container (i.e. self is end)
1601 if (!PyObject_RichCompareBool(last, self, Py_EQ)) {
1602 bool iter_valid = true;
1603 if (mustIncrement) {
1604 // prefer preinc, but allow post-inc; in both cases, it is "self" that
1605 // has the updated state to dereference
1606 PyObject* iter = PyObject_CallMethodNoArgs(self, PyStrings::gPreInc);
1607 if (!iter) {
1608 PyErr_Clear();
1609 static PyObject* dummy = PyLong_FromLong(1l);
1610 iter = PyObject_CallMethodOneArg(self, PyStrings::gPostInc, dummy);
1611 }
1612 iter_valid = iter && PyObject_RichCompareBool(last, self, Py_NE);
1613 Py_XDECREF(iter);
1614 }
1615
1616 if (iter_valid) {
1617 next = PyObject_CallMethodNoArgs(self, PyStrings::gDeref);
1618 if (!next)
1619 PyErr_Clear();
1620 }
1621 }
1622 Py_DECREF(last);
1623 }
1624
1625 if (!next)
1626 PyErr_SetString(PyExc_StopIteration, "");
1627 return next;
1628}
1629
1630//- STL complex<T> behavior --------------------------------------------------
1631#define COMPLEX_METH_GETSET(name, cppname) \
1632 static PyObject* name##ComplexGet(PyObject* self, void*) { \
1633 return PyObject_CallMethodNoArgs(self, cppname); \
1634 } \
1635 static int name##ComplexSet(PyObject* self, PyObject* value, void*) { \
1636 PyObject* result = PyObject_CallMethodOneArg(self, cppname, value); \
1637 if (result) { \
1638 Py_DECREF(result); \
1639 return 0; \
1640 } \
1641 return -1; \
1642 } \
1643 PyGetSetDef name##Complex{(char*)#name, (getter)name##ComplexGet, \
1644 (setter)name##ComplexSet, nullptr, nullptr};
1645
1648
1649static PyObject* ComplexComplex(PyObject* self) {
1650 PyObject* real = PyObject_CallMethodNoArgs(self, PyStrings::gCppReal);
1651 if (!real)
1652 return nullptr;
1653 double r = PyFloat_AsDouble(real);
1654 Py_DECREF(real);
1655 if (r == -1. && PyErr_Occurred())
1656 return nullptr;
1657
1658 PyObject* imag = PyObject_CallMethodNoArgs(self, PyStrings::gCppImag);
1659 if (!imag)
1660 return nullptr;
1661 double i = PyFloat_AsDouble(imag);
1662 Py_DECREF(imag);
1663 if (i == -1. && PyErr_Occurred())
1664 return nullptr;
1665
1666 return PyComplex_FromDoubles(r, i);
1667}
1668
1669static PyObject* ComplexRepr(PyObject* self) {
1670 PyObject* real = PyObject_CallMethodNoArgs(self, PyStrings::gCppReal);
1671 if (!real)
1672 return nullptr;
1673 double r = PyFloat_AsDouble(real);
1674 Py_DECREF(real);
1675 if (r == -1. && PyErr_Occurred())
1676 return nullptr;
1677
1678 PyObject* imag = PyObject_CallMethodNoArgs(self, PyStrings::gCppImag);
1679 if (!imag)
1680 return nullptr;
1681 double i = PyFloat_AsDouble(imag);
1682 Py_DECREF(imag);
1683 if (i == -1. && PyErr_Occurred())
1684 return nullptr;
1685
1686 std::ostringstream s;
1687 s << '(' << r << '+' << i << "j)";
1688 return PyUnicode_FromString(s.str().c_str());
1689}
1690
1691static PyObject* ComplexDRealGet(CPPInstance* self, void*) {
1692 return PyFloat_FromDouble(((std::complex<double>*)self->GetObject())->real());
1693}
1694
1695static int ComplexDRealSet(CPPInstance* self, PyObject* value, void*) {
1696 double d = PyFloat_AsDouble(value);
1697 if (d == -1.0 && PyErr_Occurred())
1698 return -1;
1699 ((std::complex<double>*)self->GetObject())->real(d);
1700 return 0;
1701}
1702
1703PyGetSetDef ComplexDReal{(char*)"real", (getter)ComplexDRealGet,
1704 (setter)ComplexDRealSet, nullptr, nullptr};
1705
1706static PyObject* ComplexDImagGet(CPPInstance* self, void*) {
1707 return PyFloat_FromDouble(((std::complex<double>*)self->GetObject())->imag());
1708}
1709
1710static int ComplexDImagSet(CPPInstance* self, PyObject* value, void*) {
1711 double d = PyFloat_AsDouble(value);
1712 if (d == -1.0 && PyErr_Occurred())
1713 return -1;
1714 ((std::complex<double>*)self->GetObject())->imag(d);
1715 return 0;
1716}
1717
1718PyGetSetDef ComplexDImag{(char*)"imag", (getter)ComplexDImagGet,
1719 (setter)ComplexDImagSet, nullptr, nullptr};
1720
1721static PyObject* ComplexDComplex(CPPInstance* self) {
1722 double r = ((std::complex<double>*)self->GetObject())->real();
1723 double i = ((std::complex<double>*)self->GetObject())->imag();
1724 return PyComplex_FromDoubles(r, i);
1725}
1726
1727} // unnamed namespace
1728
1729//- public functions ---------------------------------------------------------
1730namespace cppjit::cpyrt {
1731std::set<std::string> gIteratorTypes;
1732}
1733
1734static inline bool run_pythonizors(PyObject* pyclass, PyObject* pyname,
1735 const std::vector<PyObject*>& v) {
1736 PyObject* args = PyTuple_New(2);
1737 Py_INCREF(pyclass);
1738 PyTuple_SET_ITEM(args, 0, pyclass);
1739 Py_INCREF(pyname);
1740 PyTuple_SET_ITEM(args, 1, pyname);
1741
1742 bool pstatus = true;
1743 for (auto pythonizor : v) {
1744 PyObject* result = PyObject_CallObject(pythonizor, args);
1745 if (!result) {
1746 pstatus = false; // TODO: detail the error handling
1747 break;
1748 }
1749 Py_DECREF(result);
1750 }
1751 Py_DECREF(args);
1752
1753 return pstatus;
1754}
1755
1757 const std::string& name = interop::GetScopedFinalName(scope);
1758
1759 // Add pre-defined pythonizations (for STL and ROOT) to classes based on their
1760 // signature and/or class name.
1761 if (!pyclass)
1762 return false;
1763
1764 CPPScope* klass = (CPPScope*)pyclass;
1765
1766 //- method name based pythonization ------------------------------------------
1767
1768 // for smart pointer style classes that are otherwise not known as such; would
1769 // prefer operator-> as that returns a pointer (which is simpler since it
1770 // never has to deal with ref-assignment), but operator* plays better with STL
1771 // iters and algorithms
1772 // optional and expected use operator* to access a contained value, rather
1773 // than to provide pointer-like access to an object.
1774 const bool has_value_semantics = IsTemplatedSTLClass(name, "optional") ||
1775 IsTemplatedSTLClass(name, "expected");
1776 if (HasAttrDirect(pyclass, PyStrings::gDeref) &&
1777 !interop::IsSmartPtr(klass->fCppType)) {
1778 if (has_value_semantics)
1779 Utility::AddToClass(pyclass, "__getattr__", (PyCFunction)ValueGetAttr,
1780 METH_O);
1781 else
1782 Utility::AddToClass(pyclass, "__getattr__", (PyCFunction)DeRefGetAttr,
1783 METH_O);
1784 } else if (HasAttrDirect(pyclass, PyStrings::gFollow) &&
1786 Utility::AddToClass(pyclass, "__getattr__", (PyCFunction)FollowGetAttr,
1787 METH_O);
1788
1789 // for pre-check of nullptr for boolean types
1790 if (HasAttrDirect(pyclass, PyStrings::gCppBool)) {
1791 const char* pybool_name = "__bool__";
1792 Utility::AddToClass(pyclass, pybool_name, (PyCFunction)NullCheckBool,
1793 METH_NOARGS);
1794 }
1795
1796 // for STL containers, and user classes modeled after them
1797 // the attribute must be a cpyrt overload, otherwise the check gives false
1798 // positives in the case where the class has a non-function attribute that is
1799 // called "size".
1800 {
1801 // only pythonize as __len__ if the class actually models a container,
1802 // i.e. it is iterable (begin/end) or indexable (operator[]), and size()
1803 // returns an integral value; e.g. a size() returning a custom dimension
1804 // object must not become __len__ (len() would fail on a valid object).
1805 // The size method and the container interface may come from a base class
1806 // (checked with PyObject_HasAttr), as long as at least one of them is
1807 // freshly declared here (else the base's __len__ is inherited via the
1808 // normal MRO and there is nothing to do).
1809 bool sizeDirect =
1810 HasAttrDirect(pyclass, PyStrings::gSize, /*mustBecpyrt=*/true);
1811 bool ifaceDirect = (HasAttrDirect(pyclass, PyStrings::gBegin) &&
1812 HasAttrDirect(pyclass, PyStrings::gEnd)) ||
1813 HasAttrDirect(pyclass, PyStrings::gGetItem);
1814 PyObject* size_attr = (sizeDirect || ifaceDirect)
1815 ? PyObject_GetAttr(pyclass, PyStrings::gSize)
1816 : nullptr;
1817 if (!size_attr)
1818 PyErr_Clear();
1819 else if (!CPPOverload_Check(size_attr)) {
1820 // non-function attribute called "size": not pythonizable
1821 Py_DECREF(size_attr);
1822 size_attr = nullptr;
1823 }
1824 bool isContainer = size_attr &&
1825 PyObject_HasAttr(pyclass, PyStrings::gBegin) &&
1826 PyObject_HasAttr(pyclass, PyStrings::gEnd);
1827 if (size_attr && !isContainer) {
1828 // indexable via a real C++ operator[]? (every class "has" __getitem__
1829 // through the CPPInstance defaults, so require an actual overload)
1830 PyObject* gi = PyObject_GetAttr(pyclass, PyStrings::gGetItem);
1831 if (gi) {
1832 isContainer = CPPOverload_Check(gi);
1833 Py_DECREF(gi);
1834 } else
1835 PyErr_Clear();
1836 }
1837 if (isContainer) {
1838 auto isIntegral = [](const std::string& tn) {
1839 static const char* const integrals[] = {
1840 "char", "signed char", "unsigned char", "short",
1841 "unsigned short", "int", "unsigned int", "long",
1842 "unsigned long", "long long", "unsigned long long"};
1843 for (const char* i : integrals)
1844 if (tn == i)
1845 return true;
1846 return false;
1847 };
1848 // find the scope (this class or a base) that declares size()
1849 std::vector<interop::TCppMethod_t> sizes;
1850 std::deque<interop::TCppScope_t> todo{klass->fCppType};
1851 while (sizes.empty() && !todo.empty()) {
1852 interop::TCppScope_t s = todo.front();
1853 todo.pop_front();
1854 sizes = interop::GetMethodsFromName(s, "size");
1855 for (interop::TCppIndex_t i = 0; i < interop::GetNumBases(s); ++i)
1856 todo.push_back(interop::GetBaseScope(s, i));
1857 }
1858 for (interop::TCppMethod_t m : sizes) {
1859 if (isIntegral(interop::GetMethodReturnTypeAsString(m))) {
1860 Utility::AddToClass(pyclass, "__len__", "size");
1861 break;
1862 }
1863 }
1864 }
1865 Py_XDECREF(size_attr);
1866 }
1867
1868 if (HasAttrDirect(pyclass, PyStrings::gContains)) {
1869 Utility::AddToClass(pyclass, "__contains__", "contains");
1870 }
1871
1872 if (!IsTemplatedSTLClass(name, "vector") && // vector is dealt with below
1873 !((PyTypeObject*)pyclass)->tp_iter) {
1874 if (HasAttrDirect(pyclass, PyStrings::gBegin) &&
1875 HasAttrDirect(pyclass, PyStrings::gEnd)) {
1876 // obtain the name of the return type
1877 const auto& methods =
1878 interop::GetMethodsFromName(klass->fCppType, "begin");
1879 if (!methods.empty()) {
1880 // check return type; if not explicitly an iterator, add it to the
1881 // "known" return types to add the "next" method on use
1882 interop::TCppMethod_t meth = methods[0];
1883 const std::string& resname = interop::GetMethodReturnTypeAsString(meth);
1884 bool isIterator = gIteratorTypes.find(resname) != gIteratorTypes.end();
1885 if (!isIterator && interop::GetScope(resname)) {
1886 if (resname.find("iterator") == std::string::npos)
1887 gIteratorTypes.insert(resname);
1888 isIterator = true;
1889 }
1890
1891 if (isIterator) {
1892 // install iterator protocol a la STL
1893 ((PyTypeObject*)pyclass)->tp_iter = (getiterfunc)STLSequenceIter;
1894 Utility::AddToClass(pyclass, "__iter__", (PyCFunction)STLSequenceIter,
1895 METH_NOARGS);
1896 } else {
1897 // still okay if this is some pointer type of builtin persuasion
1898 // (general class won't work: the return type needs to understand the
1899 // iterator protocol)
1900 std::string resolved = interop::ResolveName(resname);
1901 if (resolved.back() == '*' &&
1902 interop::IsBuiltin(resolved.substr(0, resolved.size() - 1))) {
1903 ((PyTypeObject*)pyclass)->tp_iter = (getiterfunc)LLSequenceIter;
1904 Utility::AddToClass(pyclass, "__iter__",
1905 (PyCFunction)LLSequenceIter, METH_NOARGS);
1906 }
1907 }
1908 }
1909 }
1910 if (!((PyTypeObject*)pyclass)->tp_iter && // no iterator resolved
1911 HasAttrDirect(pyclass, PyStrings::gGetItem) &&
1912 PyObject_HasAttr(pyclass, PyStrings::gLen)) {
1913 // Python will iterate over __getitem__ using integers, but C++ operator[]
1914 // will never raise a StopIteration. A checked getitem (raising IndexError
1915 // if beyond size()) works in some cases but would mess up if operator[]
1916 // is meant to implement an associative container. So, this has to be
1917 // implemented as an iterator protocol.
1918 ((PyTypeObject*)pyclass)->tp_iter = (getiterfunc)index_iter;
1919 Utility::AddToClass(pyclass, "__iter__", (PyCFunction)index_iter,
1920 METH_NOARGS);
1921 }
1922 }
1923
1924 // operator==/!= are used in op_richcompare of CPPInstance, which subsequently
1925 // allows comparisons to None; if no operator is available, a hook is
1926 // installed for lazy lookups in the global and/or class namespace
1927 if (HasAttrDirect(pyclass, PyStrings::gEq, true) &&
1928 interop::GetMethodsFromName(klass->fCppType, "__eq__").empty()) {
1929 PyObject* cppol = PyObject_GetAttr(pyclass, PyStrings::gEq);
1930 if (!klass->fOperators)
1931 klass->fOperators = new Utility::PyOperators();
1932 klass->fOperators->fEq = cppol;
1933 // re-insert the forwarding __eq__ from the CPPInstance in case there was a
1934 // Python-side override in the base class
1935 static PyObject* top_eq = nullptr;
1936 if (!top_eq) {
1937 PyObject* top_cls = PyObject_GetAttrString(gThisModule, "CPPInstance");
1938 top_eq = PyObject_GetAttr(top_cls, PyStrings::gEq);
1939 Py_DECREF(top_eq); // make it borrowed
1940 Py_DECREF(top_cls);
1941 }
1942 PyObject_SetAttr(pyclass, PyStrings::gEq, top_eq);
1943 }
1944
1945 if (HasAttrDirect(pyclass, PyStrings::gNe, true) &&
1946 interop::GetMethodsFromName(klass->fCppType, "__ne__").empty()) {
1947 PyObject* cppol = PyObject_GetAttr(pyclass, PyStrings::gNe);
1948 if (!klass->fOperators)
1949 klass->fOperators = new Utility::PyOperators();
1950 klass->fOperators->fNe = cppol;
1951 // re-insert the forwarding __ne__ (same reason as above for __eq__)
1952 static PyObject* top_ne = nullptr;
1953 if (!top_ne) {
1954 PyObject* top_cls = PyObject_GetAttrString(gThisModule, "CPPInstance");
1955 top_ne = PyObject_GetAttr(top_cls, PyStrings::gNe);
1956 Py_DECREF(top_ne); // make it borrowed
1957 Py_DECREF(top_cls);
1958 }
1959 PyObject_SetAttr(pyclass, PyStrings::gNe, top_ne);
1960 }
1961
1962 // The pseudo-constructor is not meant for standard-library aggregates;
1963 // detect those by walking the enclosing scopes instead of testing the
1964 // exposed name, which may or may not be qualified (e.g. std::tuple is
1965 // reported without its namespace here).
1966 bool stdclass = false;
1967 for (interop::TCppScope_t p =
1968 interop::GetParentScope(((CPPClass*)pyclass)->fCppType);
1970 if (interop::IsNamespace(p) && interop::GetName(p) == "std") {
1971 stdclass = true;
1972 break;
1973 }
1974 }
1975
1976 if (interop::IsAggregate(((CPPClass*)pyclass)->fCppType) && !stdclass) {
1977 // create a pseudo-constructor to allow initializer-style object creation
1978 interop::TCppScope_t kls = ((CPPClass*)pyclass)->fCppType;
1979 std::vector<interop::TCppScope_t> datamems;
1980 interop::GetDatamembers(kls, datamems);
1981 if (!datamems.empty()) {
1982 std::string rname = name;
1984
1985 std::ostringstream initdef;
1986 initdef << "namespace __cppjit_internal {\n"
1987 << "void init_" << rname << "(" << name << "** self";
1988 bool codegen_ok = true;
1989 std::vector<std::string> arg_types, arg_names, arg_defaults;
1990 const int ndata = datamems.size();
1991 arg_types.reserve(ndata);
1992 arg_names.reserve(ndata);
1993 arg_defaults.reserve(ndata);
1994 for (auto data : datamems) {
1996 continue;
1997
1999 const std::string& res =
2000 interop::IsEnumType(datammember_type)
2002 interop::GetScopeFromType(datammember_type))
2004 interop::ResolveType(datammember_type));
2005 const std::string& cpd = TypeManip::compound(res);
2006 std::string res_clean = TypeManip::clean_type(res, false, true);
2007
2008 if (res_clean == "internal_enum_type_t")
2009 res_clean = res; // restore (properly scoped name)
2010
2011 if (res.rfind(']') == std::string::npos &&
2012 res.rfind(')') == std::string::npos) {
2013 if (!cpd.empty())
2014 arg_types.push_back(res_clean + cpd);
2015 else
2016 arg_types.push_back("const " + res_clean + "&");
2017 arg_names.push_back(interop::GetFinalName(data));
2018 if ((!cpd.empty() && cpd.back() == '*') ||
2019 interop::IsBuiltin(res_clean))
2020 arg_defaults.push_back("0");
2021 else {
2022 interop::TCppScope_t klsid = interop::GetScope(res_clean);
2024 arg_defaults.push_back(res_clean + "{}");
2025 }
2026 } else {
2027 codegen_ok =
2028 false; // TODO: how to support arrays, anonymous enums, etc?
2029 break;
2030 }
2031 }
2032
2033 if (codegen_ok && !arg_types.empty()) {
2034 bool defaults_ok = arg_defaults.size() == arg_types.size();
2035 for (std::vector<std::string>::size_type i = 0; i < arg_types.size();
2036 ++i) {
2037 initdef << ", " << arg_types[i] << " " << arg_names[i];
2038 if (defaults_ok)
2039 initdef << " = " << arg_defaults[i];
2040 }
2041 initdef << ") {\n *self = new " << name << "{";
2042 for (std::vector<std::string>::size_type i = 0; i < arg_names.size();
2043 ++i) {
2044 if (i != 0)
2045 initdef << ", ";
2046 initdef << arg_names[i];
2047 }
2048 initdef << "};\n} }";
2049
2050 if (interop::Compile(initdef.str(), true /* silent */)) {
2051 interop::TCppScope_t cis = interop::GetScope("__cppjit_internal");
2052 const auto& methods =
2053 interop::GetMethodsFromName(cis, "init_" + rname);
2054 if (methods.size()) {
2055 if (!Utility::AddToClass(pyclass, "__init__",
2056 new CPPFunction(cis, methods[0])))
2057 PyErr_Clear();
2058 }
2059 }
2060 }
2061 }
2062 }
2063
2064 //- class name based pythonization -------------------------------------------
2065
2066 if (IsTemplatedSTLClass(name, "span")) {
2067 // libstdc++ (GCC >= 15) implements std::span::iterator using a private
2068 // nested tag type, which makes the iterator non-instantiable by
2069 // CallFunc-generated wrappers (the return type cannot be named without
2070 // violating access rules).
2071 //
2072 // To preserve correct Python iteration semantics, we replace begin()/end()
2073 // for std::span to return a custom pointer-based iterator instead.
2074 Utility::AddToClass(pyclass, "begin", (PyCFunction)SpanBegin, METH_NOARGS);
2075 Utility::AddToClass(pyclass, "end", (PyCFunction)SpanEnd, METH_NOARGS);
2076 }
2077
2078 if (IsTemplatedSTLClass(name, "vector")) {
2079
2080 // std::vector<bool> is a special case in C++
2081 if (!sVectorBoolTypeID)
2082 sVectorBoolTypeID = interop::GetScope("std::vector<bool>");
2083 if (klass->fCppType == sVectorBoolTypeID) {
2084 Utility::AddToClass(pyclass, "__getitem__",
2085 (PyCFunction)VectorBoolGetItem, METH_O);
2086 Utility::AddToClass(pyclass, "__setitem__",
2087 (PyCFunction)VectorBoolSetItem);
2088 } else {
2089 // constructor that takes python collections
2090 Utility::AddToClass(pyclass, "__real_init", "__init__");
2091 Utility::AddToClass(pyclass, "__init__", (PyCFunction)VectorInit,
2092 METH_VARARGS | METH_KEYWORDS);
2093
2094 // data with size
2095 if (!Utility::AddToClass(pyclass, "__real_data", "data"))
2096 PyErr_Clear(); // no 'data' method to alias
2097 Utility::AddToClass(pyclass, "data", (PyCFunction)VectorData);
2098
2099 // checked getitem
2100 if (HasAttrDirect(pyclass, PyStrings::gLen)) {
2101 Utility::AddToClass(pyclass, "_getitem__unchecked", "__getitem__");
2102 Utility::AddToClass(pyclass, "__getitem__", (PyCFunction)VectorGetItem,
2103 METH_O);
2104 }
2105
2106 // vector-optimized iterator protocol
2107 ((PyTypeObject*)pyclass)->tp_iter = (getiterfunc)vector_iter;
2108
2109 // optimized __iadd__
2110 Utility::AddToClass(pyclass, "__iadd__", (PyCFunction)VectorIAdd,
2111 METH_VARARGS | METH_KEYWORDS);
2112
2113 // helpers for iteration
2114 interop::TCppType_t value_type =
2115 interop::GetTypeFromScope(interop::GetNamed("value_type", scope));
2116 interop::TCppType_t vtype = interop::ResolveType(value_type);
2117
2118 // numpy array conversion; only for vectors of non-class types:
2119 // data() on a vector of class instances hands back a proxy of the
2120 // first element, so forwarding __array__ to it would yield that
2121 // element instead of the full vector (numpy's generic sequence
2122 // protocol handles such vectors correctly on its own)
2123 if (vtype && !interop::GetScopeFromType(vtype))
2124 Utility::AddToClass(pyclass, "__array__", (PyCFunction)VectorArray,
2125 METH_VARARGS | METH_KEYWORDS /* unused */);
2126
2127 if (vtype) { // actually resolved?
2128 PyObject* pyvalue_type = PyLong_FromVoidPtr(vtype.data);
2129 PyObject_SetAttr(pyclass, PyStrings::gValueTypePtr, pyvalue_type);
2130 Py_DECREF(pyvalue_type);
2131 pyvalue_type =
2132 PyUnicode_FromString(interop::GetTypeAsString(vtype).c_str());
2133 PyObject_SetAttr(pyclass, PyStrings::gValueType, pyvalue_type);
2134 Py_DECREF(pyvalue_type);
2135 }
2136
2137 size_t typesz = interop::SizeOfType(vtype);
2138 if (typesz) {
2139 PyObject* pyvalue_size = PyLong_FromSsize_t(typesz);
2140 PyObject_SetAttr(pyclass, PyStrings::gValueSize, pyvalue_size);
2141 Py_DECREF(pyvalue_size);
2142 }
2143 }
2144 }
2145
2146 else if (IsTemplatedSTLClass(name, "array")) {
2147 // constructor that takes python associative collections
2148 Utility::AddToClass(pyclass, "__real_init", "__init__");
2149 Utility::AddToClass(pyclass, "__init__", (PyCFunction)ArrayInit,
2150 METH_VARARGS | METH_KEYWORDS);
2151 }
2152
2153 else if (IsTemplatedSTLClass(name, "map") ||
2154 IsTemplatedSTLClass(name, "unordered_map")) {
2155 // constructor that takes python associative collections
2156 Utility::AddToClass(pyclass, "__real_init", "__init__");
2157 Utility::AddToClass(pyclass, "__init__", (PyCFunction)MapInit,
2158 METH_VARARGS | METH_KEYWORDS);
2159 // From C++20, std::map/unordered_map have a native contains() that the
2160 // generic contains->__contains__ mapping above will pick up. Strong-types
2161 // reflection does not always expose it, so fall back to a find()-based
2162 // __contains__ when none was reflected (still O(log n)/O(1), never
2163 // iterating).
2164 if (!HasAttrDirect(pyclass, PyStrings::gContains))
2165 Utility::AddToClass(pyclass, "__contains__",
2166 (PyCFunction)STLContainsWithFind, METH_O);
2167 }
2168
2169 else if (IsTemplatedSTLClass(name, "set")) {
2170 // constructor that takes python associative collections
2171 Utility::AddToClass(pyclass, "__real_init", "__init__");
2172 Utility::AddToClass(pyclass, "__init__", (PyCFunction)SetInit,
2173 METH_VARARGS | METH_KEYWORDS);
2174 // From C++20, std::set has a native contains() that the generic
2175 // contains->__contains__ mapping above will pick up. Strong-types
2176 // reflection does not always expose it, so fall back to a find()-based
2177 // __contains__ when none was reflected (still O(log n), never iterating).
2178 if (!HasAttrDirect(pyclass, PyStrings::gContains))
2179 Utility::AddToClass(pyclass, "__contains__",
2180 (PyCFunction)STLContainsWithFind, METH_O);
2181 }
2182
2183 else if (IsTemplatedSTLClass(name, "pair")) {
2184 Utility::AddToClass(pyclass, "__getitem__", (PyCFunction)PairUnpack,
2185 METH_O);
2186 Utility::AddToClass(pyclass, "__len__", (PyCFunction)ReturnTwo,
2187 METH_NOARGS);
2188 }
2189
2190 if (IsTemplatedSTLClass(name, "shared_ptr") ||
2191 IsTemplatedSTLClass(name, "unique_ptr")) {
2192 Utility::AddToClass(pyclass, "__real_init", "__init__");
2193 Utility::AddToClass(pyclass, "__init__", (PyCFunction)SmartPtrInit,
2194 METH_VARARGS | METH_KEYWORDS);
2195 }
2196
2197 else if (!((PyTypeObject*)pyclass)->tp_iter &&
2198 (name.find("iterator") != std::string::npos ||
2199 gIteratorTypes.find(name) != gIteratorTypes.end())) {
2200 ((PyTypeObject*)pyclass)->tp_iternext = (iternextfunc)STLIterNext;
2201 Utility::AddToClass(pyclass, "__next__", (PyCFunction)STLIterNext,
2202 METH_NOARGS);
2203 ((PyTypeObject*)pyclass)->tp_iter = (getiterfunc)PyObject_SelfIter;
2204 Utility::AddToClass(pyclass, "__iter__", (PyCFunction)PyObject_SelfIter,
2205 METH_NOARGS);
2206 }
2207
2208 else if (name == "std::basic_string<char>" ||
2209 name == "std::basic_string<char,std::char_traits<char>,std::"
2210 "allocator<char> >" ||
2211 name == "std::basic_string<char, std::char_traits<char>, "
2212 "std::allocator<char> >" ||
2213 name == "std::__1::basic_string<char>" || // libc++ inline namespace
2214 name == "std::string") { // typedef preserved by GetScopedFinalName
2215 // on libc++
2216 Utility::AddToClass(pyclass, "__repr__", (PyCFunction)STLStringRepr,
2217 METH_NOARGS);
2218 Utility::AddToClass(pyclass, "__str__", (PyCFunction)STLStringStr,
2219 METH_NOARGS);
2220 Utility::AddToClass(pyclass, "__bytes__", (PyCFunction)STLStringBytes,
2221 METH_NOARGS);
2222 Utility::AddToClass(pyclass, "__cmp__", (PyCFunction)STLStringCompare,
2223 METH_O);
2224 Utility::AddToClass(pyclass, "__eq__", (PyCFunction)STLStringIsEqual,
2225 METH_O);
2226 Utility::AddToClass(pyclass, "__ne__", (PyCFunction)STLStringIsNotEqual,
2227 METH_O);
2228 // Python's `in` operator needs __contains__ on the proxy regardless of the
2229 // C++ standard cpyrt is built with; it never dispatches to C++'s
2230 // std::string::contains (C++23+). The old `__cplusplus <= 202302L` guard
2231 // wrongly dropped it when built with -std=c++2c (__cplusplus == 202400L).
2232 Utility::AddToClass(pyclass, "__contains__", (PyCFunction)STLStringContains,
2233 METH_O);
2234 Utility::AddToClass(pyclass, "decode", (PyCFunction)STLStringDecode,
2235 METH_VARARGS | METH_KEYWORDS);
2236 Utility::AddToClass(pyclass, "__cpp_find", "find");
2237 Utility::AddToClass(pyclass, "find", (PyCFunction)STLStringFind,
2238 METH_VARARGS | METH_KEYWORDS);
2239 Utility::AddToClass(pyclass, "__cpp_rfind", "rfind");
2240 Utility::AddToClass(pyclass, "rfind", (PyCFunction)STLStringRFind,
2241 METH_VARARGS | METH_KEYWORDS);
2242 Utility::AddToClass(pyclass, "__cpp_replace", "replace");
2243 Utility::AddToClass(pyclass, "replace", (PyCFunction)STLStringReplace,
2244 METH_VARARGS | METH_KEYWORDS);
2245 Utility::AddToClass(pyclass, "__getattr__", (PyCFunction)STLStringGetAttr,
2246 METH_O);
2247
2248 // to allow use of std::string in dictionaries and findable with str
2249 ((PyTypeObject*)pyclass)->tp_hash = (hashfunc)STLStringHash;
2250 }
2251
2252 else if (name == "std::basic_string_view<char>" ||
2253 name == "std::basic_string_view<char,std::char_traits<char> >" ||
2254 name == "std::basic_string_view<char, std::char_traits<char> >" ||
2255 name ==
2256 "std::__1::basic_string_view<char>" || // libc++ inline namespace
2257 name == "std::string_view") { // typedef preserved by
2258 // GetScopedFinalName on libc++
2259 Utility::AddToClass(pyclass, "__real_init", "__init__");
2260 Utility::AddToClass(pyclass, "__init__", (PyCFunction)StringViewInit,
2261 METH_VARARGS | METH_KEYWORDS);
2262 Utility::AddToClass(pyclass, "__bytes__", (PyCFunction)STLViewStringBytes,
2263 METH_NOARGS);
2264 Utility::AddToClass(pyclass, "__cmp__", (PyCFunction)STLViewStringCompare,
2265 METH_O);
2266 Utility::AddToClass(pyclass, "__eq__", (PyCFunction)STLViewStringIsEqual,
2267 METH_O);
2268 Utility::AddToClass(pyclass, "__ne__", (PyCFunction)STLViewStringIsNotEqual,
2269 METH_O);
2270 Utility::AddToClass(pyclass, "__repr__", (PyCFunction)STLViewStringRepr,
2271 METH_NOARGS);
2272 Utility::AddToClass(pyclass, "__str__", (PyCFunction)STLViewStringStr,
2273 METH_NOARGS);
2274 }
2275
2276 // The first condition was already present in upstream cpyrt. The others
2277 // are special to ROOT, because its reflection layer gives us the types
2278 // without the "std::" namespace. On some platforms, that applies only to the
2279 // template arguments, and on others also to the "basic_string".
2280 else if (name == "std::basic_string<wchar_t,std::char_traits<wchar_t>,std::"
2281 "allocator<wchar_t> >" ||
2282 name == "std::basic_string<wchar_t, std::char_traits<wchar_t>, "
2283 "std::allocator<wchar_t> >" ||
2284 name == "basic_string<wchar_t,char_traits<wchar_t>,allocator<wchar_"
2285 "t> >" ||
2286 name == "std::basic_string<wchar_t,char_traits<wchar_t>,allocator<"
2287 "wchar_t> >" ||
2288 name == "std::__1::basic_string<wchar_t,std::__1::char_traits<wchar_"
2289 "t>,std::__1::allocator<wchar_t> >" ||
2290 name == "std::wstring") {
2291 Utility::AddToClass(pyclass, "__repr__", (PyCFunction)STLWStringRepr,
2292 METH_NOARGS);
2293 Utility::AddToClass(pyclass, "__str__", (PyCFunction)STLWStringStr,
2294 METH_NOARGS);
2295 Utility::AddToClass(pyclass, "__bytes__", (PyCFunction)STLWStringBytes,
2296 METH_NOARGS);
2297 Utility::AddToClass(pyclass, "__cmp__", (PyCFunction)STLWStringCompare,
2298 METH_O);
2299 Utility::AddToClass(pyclass, "__eq__", (PyCFunction)STLWStringIsEqual,
2300 METH_O);
2301 Utility::AddToClass(pyclass, "__ne__", (PyCFunction)STLWStringIsNotEqual,
2302 METH_O);
2303 }
2304
2305 else if (name == "complex<double>" || name == "std::complex<double>") {
2306 Utility::AddToClass(pyclass, "__cpp_real", "real");
2307 PyObject_SetAttrString(
2308 pyclass, "real",
2309 PyDescr_NewGetSet((PyTypeObject*)pyclass, &ComplexDReal));
2310 Utility::AddToClass(pyclass, "__cpp_imag", "imag");
2311 PyObject_SetAttrString(
2312 pyclass, "imag",
2313 PyDescr_NewGetSet((PyTypeObject*)pyclass, &ComplexDImag));
2314 Utility::AddToClass(pyclass, "__complex__", (PyCFunction)ComplexDComplex,
2315 METH_NOARGS);
2316 Utility::AddToClass(pyclass, "__repr__", (PyCFunction)ComplexRepr,
2317 METH_NOARGS);
2318 }
2319
2320 else if (IsTemplatedSTLClass(name, "complex")) {
2321 Utility::AddToClass(pyclass, "__cpp_real", "real");
2322 PyObject_SetAttrString(
2323 pyclass, "real",
2324 PyDescr_NewGetSet((PyTypeObject*)pyclass, &realComplex));
2325 Utility::AddToClass(pyclass, "__cpp_imag", "imag");
2326 PyObject_SetAttrString(
2327 pyclass, "imag",
2328 PyDescr_NewGetSet((PyTypeObject*)pyclass, &imagComplex));
2329 Utility::AddToClass(pyclass, "__complex__", (PyCFunction)ComplexComplex,
2330 METH_NOARGS);
2331 Utility::AddToClass(pyclass, "__repr__", (PyCFunction)ComplexRepr,
2332 METH_NOARGS);
2333 }
2334
2335 // direct user access; there are two calls here:
2336 // - explicit pythonization: won't fall through to the base classes and is
2337 // preferred if present
2338 // - normal pythonization: only called if explicit isn't present, falls
2339 // through to base classes
2340 bool bUserOk = true;
2341 PyObject* res = nullptr;
2342 PyObject* pyname = PyUnicode_FromString(name.c_str());
2343 if (HasAttrDirect(pyclass, PyStrings::gExPythonize)) {
2344 res = PyObject_CallMethodObjArgs(pyclass, PyStrings::gExPythonize, pyclass,
2345 pyname, nullptr);
2346 bUserOk = (bool)res;
2347 } else {
2348 PyObject* func = PyObject_GetAttr(pyclass, PyStrings::gPythonize);
2349 if (func) {
2350 res = PyObject_CallFunctionObjArgs(func, pyclass, pyname, nullptr);
2351 Py_DECREF(func);
2352 bUserOk = (bool)res;
2353 } else
2354 PyErr_Clear();
2355 }
2356 if (!bUserOk) {
2357 Py_DECREF(pyname);
2358 return false;
2359 } else {
2360 Py_XDECREF(res);
2361 // pyname handed to args tuple below
2362 }
2363
2364 // call registered pythonizors, if any: first run the namespace-specific
2365 // pythonizors, then the global ones (the idea is to allow writing a
2366 // pythonizor that see all classes)
2367 bool pstatus = true;
2368 std::string outer_scope = TypeManip::extract_namespace(name);
2369 auto& pyzMap = pythonizations();
2370 if (!outer_scope.empty()) {
2371 auto p = pyzMap.find(outer_scope);
2372 if (p != pyzMap.end()) {
2373 PyObject* subname = PyUnicode_FromString(
2374 name.substr(outer_scope.size() + 2, std::string::npos).c_str());
2375 pstatus = run_pythonizors(pyclass, subname, p->second);
2376 Py_DECREF(subname);
2377 }
2378 }
2379
2380 if (pstatus) {
2381 auto p = pyzMap.find("");
2382 if (p != pyzMap.end())
2383 pstatus = run_pythonizors(pyclass, pyname, p->second);
2384 }
2385
2386 Py_DECREF(pyname);
2387
2388 // phew! all done ...
2389 return pstatus;
2390}
bool PyUnstable_Object_IsUniqueReferencedTemporary(PyObject *pyobject)
uint32_t fFlags
_object PyObject
#define CPPJIT_IMPL_STRING_PYTHONIZATION_CMP(type, name)
#define CPYRT_STRING_FINDMETHOD(name, cppname, pyname)
static bool run_pythonizors(PyObject *pyclass, PyObject *pyname, const std::vector< PyObject * > &v)
#define COMPLEX_METH_GETSET(name, cppname)
void FillVector(std::vector< double > &v, int size, T *a)
#define d(i)
Definition RSha256.hxx:102
#define c(i)
Definition RSha256.hxx:101
#define h(i)
Definition RSha256.hxx:106
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
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 attr
char name[80]
Definition TGX11.cxx:142
interop::TCppScope_t ObjectIsA(bool check_smart=true) const
Utility::PyOperators * fOperators
Definition CPPScope.h:68
interop::TCppScope_t fCppType
Definition CPPScope.h:62
#define Py_RETURN_TRUE
Definition cpyrt.h:73
#define Py_RETURN_FALSE
Definition cpyrt.h:77
#define Py_RETURN_NONE
Definition cpyrt.h:69
CoordSystem::Scalar get(DisplacementVector2D< CoordSystem, Tag > const &p)
std::string extract_namespace(const std::string &name)
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_legalname(std::string &cppscope)
bool AddToClass(PyObject *pyclass, const char *label, PyCFunction cfunc, int flags=METH_VARARGS)
Definition Utility.cxx:178
Py_ssize_t GetBuffer(PyObject *pyobject, char tc, int size, void *&buf, bool check=true)
Definition Utility.cxx:1204
bool CPPOverload_Check(T *object)
Definition CPPOverload.h:94
CPYRT_EXTERN Converter * CreateConverter(const std::string &name, cdims_t=0)
bool LowLevelView_Check(T *object)
PyObject * gThisModule
Definition CPPMethod.cxx:30
PyObject * GetScopeProxy(interop::TCppScope_t)
std::set< std::string > gIteratorTypes
bool CPPScope_Check(T *object)
Definition CPPScope.h:86
bool CPPInstance_Check(T *object)
PyTypeObject VectorIter_Type
PyTypeObject IndexIter_Type
std::unordered_map< std::string, std::vector< PyObject * > > & pythonizations()
bool Pythonize(PyObject *pyclass, interop::TCppScope_t scope)
static PyObject * GetAttrDirect(PyObject *pyclass, PyObject *pyname)
Cpp::TypeRef TCppType_t
Definition API.h:42
void GetDatamembers(TCppScope_t scope, std::vector< TCppScope_t > &datamembers)
TCppType_t GetTypeFromScope(TCppScope_t klass)
std::string GetTypeAsString(TCppType_t type)
TCppType_t GetDatamemberType(TCppScope_t data)
bool IsBuiltin(const std::string &type_name)
std::string GetName(TCppScope_t)
std::string ResolveName(const std::string &cppitem_name)
TCppScope_t GetParentScope(TCppScope_t scope)
std::string GetMethodReturnTypeAsString(TCppMethod_t)
bool IsStaticDatamember(TCppScope_t var)
TCppIndex_t GetNumBases(TCppScope_t klass)
TCppScope_t GetNamed(const std::string &scope_name, TCppScope_t parent_scope=TCppScope_t{})
size_t SizeOfType(TCppType_t type)
Cpp::FuncRef TCppMethod_t
Definition API.h:44
bool IsPublicData(TCppScope_t var)
TCppScope_t GetBaseScope(TCppScope_t klass, TCppIndex_t ibase)
bool IsPointerType(TCppType_t type)
size_t SizeOf(TCppScope_t klass)
TCppType_t ResolveType(TCppType_t cppitem_name)
TCppScope_t GetGlobalScope()
bool Compile(const std::string &code, bool silent=false)
std::vector< TCppMethod_t > GetMethodsFromName(TCppScope_t scope, const std::string &name)
size_t TCppIndex_t
Definition API.h:45
bool IsDefaultConstructable(TCppScope_t scope)
bool IsNamespace(TCppScope_t scope)
TCppScope_t GetScopeFromType(TCppType_t type)
TCppScope_t GetScope(const std::string &scope_name, TCppScope_t parent_scope=TCppScope_t{})
std::string GetFinalName(TCppScope_t type)
bool IsSmartPtr(TCppScope_t klass)
bool IsAggregate(TCppScope_t type)
std::string GetScopedFinalName(TCppScope_t type)
Cpp::DeclRef TCppScope_t
Definition API.h:41
bool IsEnumType(TCppType_t type)
Definition API.cxx:34
PyObject_HEAD PyObject * ii_container
interop::TCppScope_t vi_klass
TMarker m
Definition textangle.C:8