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