Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
Pythonize.cxx
Go to the documentation of this file.
1// Bindings
2#include "CPyCppyy.h"
3#include "Pythonize.h"
4#include "Converters.h"
5#include "CPPInstance.h"
6#include "CPPFunction.h"
7#include "CPPOverload.h"
8#include "CustomPyTypes.h"
9#include "LowLevelViews.h"
10#include "ProxyWrappers.h"
11#include "PyCallable.h"
12#include "PyStrings.h"
13#include "TypeManip.h"
14#include "Utility.h"
15
16// Standard
17#include <algorithm>
18#include <complex>
19#include <set>
20#include <stdexcept>
21#include <sstream>
22#include <string>
23#include <utility>
24
25
26//- data and local helpers ---------------------------------------------------
27namespace CPyCppyy {
28 extern PyObject* gThisModule;
29 std::map<std::string, std::vector<PyObject*>> &pythonizations();
30}
31
32namespace {
33
34// for convenience
35using namespace CPyCppyy;
36
37//-----------------------------------------------------------------------------
39// prevents calls to Py_TYPE(pyclass)->tp_getattr, which is unnecessary for our
40// purposes here and could tickle problems w/ spurious lookups into ROOT meta
42 if (dct) {
45 if (attr) {
48 return ret;
49 }
50 }
52 return false;
53}
54
56{
57 // Check base classes in the MRO (skipping the class itself) for a CPyCppyy overload.
58 PyObject *mro = ((PyTypeObject *)pyclass)->tp_mro;
59 if (mro && PyTuple_Check(mro)) {
60 for (Py_ssize_t i = 1; i < PyTuple_GET_SIZE(mro); ++i) {
61 if (HasAttrDirect(PyTuple_GET_ITEM(mro, i), pyname, /*mustBeCPyCppyy=*/true))
62 return true;
63 }
64 }
65 return false;
66}
67
69// get an attribute without causing getattr lookups
71 if (dct) {
74 return attr;
75 }
76 return nullptr;
77}
78
79//-----------------------------------------------------------------------------
80inline bool IsTemplatedSTLClass(const std::string& name, const std::string& klass) {
81// Scan the name of the class and determine whether it is a template instantiation.
82 auto pos = name.find(klass);
83 return (pos == 0 || pos == 5) && name.find("::", name.rfind(">")) == std::string::npos;
84}
85
86// to prevent compiler warnings about const char* -> char*
87inline PyObject* CallPyObjMethod(PyObject* obj, const char* meth)
88{
89// Helper; call method with signature: obj->meth().
90 Py_INCREF(obj);
91 PyObject* result = PyObject_CallMethod(obj, const_cast<char*>(meth), const_cast<char*>(""));
92 Py_DECREF(obj);
93 return result;
94}
95
96//-----------------------------------------------------------------------------
97inline PyObject* CallPyObjMethod(PyObject* obj, const char* meth, PyObject* arg1)
98{
99// Helper; call method with signature: obj->meth(arg1).
100 Py_INCREF(obj);
102 obj, const_cast<char*>(meth), const_cast<char*>("O"), arg1);
103 Py_DECREF(obj);
104 return result;
105}
106
107//-----------------------------------------------------------------------------
109{
110// Helper; converts python index into straight C index.
112 if (idx == (Py_ssize_t)-1 && PyErr_Occurred())
113 return nullptr;
114
116 if (idx >= size || (idx < 0 && idx < -size)) {
117 PyErr_SetString(PyExc_IndexError, "index out of range");
118 return nullptr;
119 }
120
121 PyObject* pyindex = nullptr;
122 if (idx >= 0) {
124 pyindex = index;
125 } else
127
128 return pyindex;
129}
130
131//-----------------------------------------------------------------------------
132inline bool AdjustSlice(const Py_ssize_t nlen, Py_ssize_t& start, Py_ssize_t& stop, Py_ssize_t& step)
133{
134// Helper; modify slice range to match the container.
135 if ((step > 0 && stop <= start) || (step < 0 && start <= stop))
136 return false;
137
138 if (start < 0) start = 0;
139 if (start >= nlen) start = nlen-1;
140 if (step >= nlen) step = nlen;
141
142 stop = step > 0 ? std::min(nlen, stop) : (stop >= 0 ? stop : -1);
143 return true;
144}
145
146//-----------------------------------------------------------------------------
148{
149// Helper; call method with signature: meth(pyindex).
152 if (!pyindex) {
154 return nullptr;
155 }
156
160 return result;
161}
162
163//- "smart pointer" behavior ---------------------------------------------------
165{
167 PyErr_SetString(PyExc_TypeError, "getattr(): attribute name must be string");
168
170 if (!pyptr)
171 return nullptr;
172
173// prevent a potential infinite loop
174 if (Py_TYPE(pyptr) == Py_TYPE(self)) {
177 PyErr_Format(PyExc_AttributeError, "%s object has no attribute \'%s\'",
181
183 return nullptr;
184 }
185
188 return result;
189}
190
192{
193// Follow operator*() if present (available in python as __deref__), so that
194// smart pointers behave as expected.
196 // TODO: these calls come from TemplateProxy and are unlikely to be needed in practice,
197 // whereas as-is, they can accidentally dereference the result of end() on some STL
198 // containers. Obviously, this is a dumb hack that should be resolved more fundamentally.
200 return nullptr;
201 }
202
203
205}
206
207//-----------------------------------------------------------------------------
209{
210 // std::optional and std::expected require has_value() before operator*().
212 if (!has_value)
213 return nullptr;
214
217 if (contains_value < 0)
218 return nullptr;
219 if (!contains_value) {
221 return nullptr;
222 }
223
224 return DeRefGetAttr(self, name);
225}
226
227//-----------------------------------------------------------------------------
229{
230// Follow operator->() if present (available in python as __follow__), so that
231// smart pointers behave as expected.
233}
234
235//- pointer checking bool converter -------------------------------------------
237{
238 if (!CPPInstance_Check(self)) {
239 PyErr_SetString(PyExc_TypeError, "C++ object proxy expected");
240 return nullptr;
241 }
242
243 if (!((CPPInstance*)self)->GetObject())
245
247}
248
249//- vector behavior as primitives ----------------------------------------------
250#if PY_VERSION_HEX < 0x03040000
251#define PyObject_LengthHint _PyObject_LengthHint
252#endif
253
254// TODO: can probably use the below getters in the InitializerListConverter
255struct ItemGetter {
256 ItemGetter(PyObject* pyobj) : fPyObject(pyobj) { Py_INCREF(fPyObject); }
257 virtual ~ItemGetter() { Py_DECREF(fPyObject); }
258 virtual Py_ssize_t size() = 0;
259 virtual PyObject* get() = 0;
260 PyObject* fPyObject;
261};
262
263struct CountedItemGetter : public ItemGetter {
264 CountedItemGetter(PyObject* pyobj) : ItemGetter(pyobj), fCur(0) {}
265 Py_ssize_t fCur;
266};
267
268struct TupleItemGetter : public CountedItemGetter {
269 using CountedItemGetter::CountedItemGetter;
270 Py_ssize_t size() override { return PyTuple_GET_SIZE(fPyObject); }
271 PyObject* get() override {
272 if (fCur < PyTuple_GET_SIZE(fPyObject)) {
273 PyObject* item = PyTuple_GET_ITEM(fPyObject, fCur++);
275 return item;
276 }
277 PyErr_SetString(PyExc_StopIteration, "end of tuple");
278 return nullptr;
279 }
280};
281
282struct ListItemGetter : public CountedItemGetter {
283 using CountedItemGetter::CountedItemGetter;
284 Py_ssize_t size() override { return PyList_GET_SIZE(fPyObject); }
285 PyObject* get() override {
286 if (fCur < PyList_GET_SIZE(fPyObject)) {
287 PyObject* item = PyList_GET_ITEM(fPyObject, fCur++);
289 return item;
290 }
291 PyErr_SetString(PyExc_StopIteration, "end of list");
292 return nullptr;
293 }
294};
295
296struct SequenceItemGetter : public CountedItemGetter {
297 using CountedItemGetter::CountedItemGetter;
298 Py_ssize_t size() override {
299 Py_ssize_t sz = PySequence_Size(fPyObject);
300 if (sz < 0) {
301 PyErr_Clear();
302 return PyObject_LengthHint(fPyObject, 8);
303 }
304 return sz;
305 }
306 PyObject* get() override { return PySequence_GetItem(fPyObject, fCur++); }
307};
308
309struct IterItemGetter : public ItemGetter {
310 using ItemGetter::ItemGetter;
311 Py_ssize_t size() override { return PyObject_LengthHint(fPyObject, 8); }
312 PyObject* get() override { return (*(Py_TYPE(fPyObject)->tp_iternext))(fPyObject); }
313};
314
315static ItemGetter* GetGetter(PyObject* args)
316{
317// Create an ItemGetter to loop over the iterable argument, if any.
318 ItemGetter* getter = nullptr;
319
320 if (PyTuple_GET_SIZE(args) == 1) {
321 PyObject* fi = PyTuple_GET_ITEM(args, 0);
323 return nullptr; // do not accept string to fill std::vector<char>
324
325 // TODO: this only tests for new-style buffers, which is too strict, but a
326 // generic check for Py_TYPE(fi)->tp_as_buffer is too loose (note that the
327 // main use case is numpy, which offers the new interface)
329 return nullptr;
330
332 getter = new TupleItemGetter(fi);
333 else if (PyList_CheckExact(fi))
334 getter = new ListItemGetter(fi);
335 else if (PySequence_Check(fi))
336 getter = new SequenceItemGetter(fi);
337 else {
339 if (iter) {
340 getter = new IterItemGetter{iter};
341 Py_DECREF(iter);
342 }
343 else PyErr_Clear();
344 }
345 }
346
347 return getter;
348}
349
350namespace {
351
353{
354 static bool compiled = false;
355
356 if (compiled)
357 return;
358
359 compiled = true;
360
361 auto code = R"(
362namespace __cppyy_internal {
363
364template <class T>
365struct ptr_iterator {
366 T *cur;
367 T *end;
368
369 ptr_iterator(T *c, T *e) : cur(c), end(e) {}
370
371 T &operator*() const { return *cur; }
372 ptr_iterator &operator++()
373 {
374 ++cur;
375 return *this;
376 }
377 bool operator==(const ptr_iterator &other) const { return cur == other.cur; }
378 bool operator!=(const ptr_iterator &other) const { return !(*this == other); }
379};
380
381template <class T>
382ptr_iterator<T> make_iter(T *begin, T *end)
383{
384 return {begin, end};
385}
386
387} // namespace __cppyy_internal
388
389// Note: for const span<T>, T is const-qualified here
390template <class T>
391auto __cppyy_internal_begin(T &s) noexcept
392{
393 return __cppyy_internal::make_iter(s.data(), s.data() + s.size());
394}
395
396// Note: for const span<T>, T is const-qualified here
397template <class T>
398auto __cppyy_internal_end(T &s) noexcept
399{
400 // end iterator = begin iterator with cur == end
401 return __cppyy_internal::make_iter(s.data() + s.size(), s.data() + s.size());
402}
403 )";
404 Cppyy::Compile(code, /*silent*/ true);
405}
406
408{
409 static PyObject *pyFunc = nullptr;
410 if (!pyFunc) {
413 pyFunc = PyObject_GetAttrString(py_ns, "__cppyy_internal_begin");
414 if (!pyFunc) {
415 PyErr_Format(PyExc_RuntimeError, "cppyy internal error: failed to locate helper "
416 "'__cppyy_internal_begin' for std::span pythonization");
417 }
418 }
419 return pyFunc;
420}
421
423{
424 static PyObject *pyFunc = nullptr;
425 if (!pyFunc) {
428 pyFunc = PyObject_GetAttrString(py_ns, "__cppyy_internal_end");
429 if (!pyFunc) {
430 PyErr_Format(PyExc_RuntimeError, "cppyy internal error: failed to locate helper "
431 "'__cppyy_internal_end' for std::span pythonization");
432 }
433 }
434 return pyFunc;
435}
436
437} // namespace
438
440{
441 auto begin = spanBegin();
442 if (!begin)
443 return nullptr;
444 return PyObject_CallOneArg(begin, self);
445}
446
448{
449 auto end = spanEnd();
450 if (!end)
451 return nullptr;
452 return PyObject_CallOneArg(end, self);
453}
454
455static bool FillVector(PyObject* vecin, PyObject* args, ItemGetter* getter)
456{
457 Py_ssize_t sz = getter->size();
458 if (sz < 0)
459 return false;
460
461// reserve memory as applicable
462 if (0 < sz) {
463 PyObject* res = PyObject_CallMethod(vecin, (char*)"reserve", (char*)"n", sz);
464 Py_DECREF(res);
465 } else // i.e. sz == 0, so empty container: done
466 return true;
467
468 bool fill_ok = true;
469
470// two main options: a list of lists (or tuples), or a list of objects; the former
471// are emplace_back'ed, the latter push_back'ed
473 if (!fi) PyErr_Clear();
475 // use emplace_back to construct the vector entries one by one
476 PyObject* eb_call = PyObject_GetAttrString(vecin, (char*)"emplace_back");
478 bool value_is_vector = false;
480 // if the value_type is a vector, then allow for initialization from sequences
481 if (std::string(CPyCppyy_PyText_AsString(vtype)).rfind("std::vector", 0) != std::string::npos)
482 value_is_vector = true;
483 } else
484 PyErr_Clear();
486
487 if (eb_call) {
489 for (int i = 0; /* until break */; ++i) {
490 PyObject* item = getter->get();
491 if (item) {
493 eb_args = PyTuple_New(1);
495 } else if (PyTuple_CheckExact(item)) {
496 eb_args = item;
497 } else if (PyList_CheckExact(item)) {
500 for (Py_ssize_t j = 0; j < isz; ++j) {
504 }
506 } else {
508 PyErr_Format(PyExc_TypeError, "argument %d is not a tuple or list", i);
509 fill_ok = false;
510 break;
511 }
514 if (!ebres) {
515 fill_ok = false;
516 break;
517 }
519 } else {
520 if (PyErr_Occurred()) {
523 fill_ok = false;
524 else { PyErr_Clear(); }
525 }
526 break;
527 }
528 }
530 }
531 } else {
532 // use push_back to add the vector entries one by one
533 PyObject* pb_call = PyObject_GetAttrString(vecin, (char*)"push_back");
534 if (pb_call) {
535 for (;;) {
536 PyObject* item = getter->get();
537 if (item) {
540 if (!pbres) {
541 fill_ok = false;
542 break;
543 }
545 } else {
546 if (PyErr_Occurred()) {
549 fill_ok = false;
550 else { PyErr_Clear(); }
551 }
552 break;
553 }
554 }
556 }
557 }
558 Py_XDECREF(fi);
559
560 return fill_ok;
561}
562
563PyObject* VectorIAdd(PyObject* self, PyObject* args, PyObject* /* kwds */)
564{
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
576 return self;
577 }
578
579// if no getter, it could still be b/c we have a buffer (e.g. numpy); looping over
580// a buffer here is slow, so use insert() instead
581 if (PyTuple_GET_SIZE(args) == 1) {
582 PyObject* fi = PyTuple_GET_ITEM(args, 0);
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).
592
593 if (!it)
594 return nullptr;
595
596 Py_DECREF(it);
597 // Assign the result of the __iadd__ override to the std::vector
599 return self;
600 }
601 }
602 }
603
604 if (!PyErr_Occurred())
605 PyErr_SetString(PyExc_TypeError, "argument is not iterable");
606 return nullptr; // error already set
607}
608
609
610PyObject* VectorInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
611{
612// Specialized vector constructor to allow construction from containers; allowing
613// such construction from initializer_list instead would possible, but can be
614// error-prone. This use case is common enough for std::vector to implement it
615// directly, except for arrays (which can be passed wholesale) and strings (which
616// 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
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) {
633 return nullptr;
634 }
635
636 return result;
637 }
638
639// The given argument wasn't iterable: simply forward to regular constructor
641 if (realInit) {
642 PyObject* result = PyObject_Call(realInit, args, nullptr);
644 return result;
645 }
646
647 return nullptr;
648}
649
650//---------------------------------------------------------------------------
652{
653 PyObject* pydata = CallPyObjMethod(self, "__real_data");
655 return pydata;
656
658 if (!pylen) {
659 PyErr_Clear();
660 return pydata;
661 }
662
663 long clen = PyInt_AsLong(pylen);
665
667 ((CPPInstance*)pydata)->CastToArray(clen);
668 return pydata;
669 }
670
671 ((LowLevelView*)pydata)->resize((size_t)clen);
672 return pydata;
673}
674
675
676// This function implements __array__, added to std::vector python proxies and causes
677// a bug (see explanation at Utility::AddToClass(pyclass, "__array__"...) in CPyCppyy::Pythonize)
678// The recursive nature of this function, passes each subarray (pydata) to the next call and only
679// the final buffer is cast to a lowlevel view and resized (in VectorData), resulting in only the
680// first 1D array to be returned. See https://github.com/root-project/root/issues/17729
681// It is temporarily removed to prevent errors due to -Wunused-function, since it is no longer added.
682#if 0
683//---------------------------------------------------------------------------
685{
686 PyObject* pydata = VectorData(self, nullptr);
691 return newarr;
692}
693#endif
694
695//-----------------------------------------------------------------------------
696static PyObject* vector_iter(PyObject* v) {
698 if (!vi) return nullptr;
699
700 vi->ii_container = v;
701
702// tell the iterator code to set a life line if this container is a temporary
703 vi->vi_flags = vectoriterobject::kDefault;
704#if PY_VERSION_HEX >= 0x030e0000
706#else
707 if (Py_REFCNT(v) <= 1 || (((CPPInstance*)v)->fFlags & CPPInstance::kIsValue))
708#endif
710
711 Py_INCREF(v);
712
714 if (pyvalue_type) {
716 if (pyvalue_size) {
717 vi->vi_stride = PyLong_AsLong(pyvalue_size);
719 } else {
720 PyErr_Clear();
721 vi->vi_stride = 0;
722 }
723
725 std::string value_type = CPyCppyy_PyText_AsString(pyvalue_type);
726 value_type = Cppyy::ResolveName(value_type);
727 vi->vi_klass = Cppyy::GetScope(value_type);
728 if (!vi->vi_klass) {
729 // look for a special case of pointer to a class type (which is a builtin, but it
730 // is more useful to treat it polymorphically by allowing auto-downcasts)
731 const std::string& clean_type = TypeManip::clean_type(value_type, false, false);
733 if (c && TypeManip::compound(value_type) == "*") {
734 vi->vi_klass = c;
736 }
737 }
738 if (vi->vi_klass) {
739 vi->vi_converter = nullptr;
740 if (!vi->vi_flags) {
741 if (value_type.back() != '*') // meaning, object stored by-value
743 }
744 } else
745 vi->vi_converter = CPyCppyy::CreateConverter(value_type);
746 if (!vi->vi_stride) vi->vi_stride = Cppyy::SizeOf(value_type);
747
748 } else if (CPPScope_Check(pyvalue_type)) {
749 vi->vi_klass = ((CPPClass*)pyvalue_type)->fCppType;
750 vi->vi_converter = nullptr;
751 if (!vi->vi_stride) vi->vi_stride = Cppyy::SizeOf(vi->vi_klass);
752 if (!vi->vi_flags) vi->vi_flags = vectoriterobject::kNeedLifeLine;
753 }
754
755 PyObject* pydata = CallPyObjMethod(v, "__real_data");
756 if (!pydata || Utility::GetBuffer(pydata, '*', 1, vi->vi_data, false) == 0)
757 vi->vi_data = CPPInstance_Check(pydata) ? ((CPPInstance*)pydata)->GetObjectRaw() : nullptr;
759
760 } else {
761 PyErr_Clear();
762 vi->vi_data = nullptr;
763 vi->vi_stride = 0;
764 vi->vi_converter = nullptr;
765 vi->vi_klass = 0;
766 vi->vi_flags = 0;
767 }
768
770
771 vi->ii_pos = 0;
772 vi->ii_len = PySequence_Size(v);
773
775 return (PyObject*)vi;
776}
777
779{
780// Implement python's __getitem__ for std::vector<>s.
781 if (PySlice_Check(index)) {
782 if (!self->GetObject()) {
783 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
784 return nullptr;
785 }
786
789
790 Py_ssize_t start, stop, step;
792
794 if (!AdjustSlice(nlen, start, stop, step))
795 return nseq;
796
797 const Py_ssize_t sign = step < 0 ? -1 : 1;
798 for (Py_ssize_t i = start; i*sign < stop*sign; i += step) {
801 CallPyObjMethod(nseq, "push_back", item);
804 }
805
806 return nseq;
807 }
808
810}
811
812
814
816{
817// std::vector<bool> is a special-case in C++, and its return type depends on
818// the compiler: treat it special here as well
819 if (!CPPInstance_Check(self) || self->ObjectIsA() != sVectorBoolTypeID) {
821 "require object of type std::vector<bool>, but %s given",
822 Cppyy::GetScopedFinalName(self->ObjectIsA()).c_str());
823 return nullptr;
824 }
825
826 if (!self->GetObject()) {
827 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
828 return nullptr;
829 }
830
831 if (PySlice_Check(idx)) {
834
835 Py_ssize_t start, stop, step;
838 if (!AdjustSlice(nlen, start, stop, step))
839 return nseq;
840
841 const Py_ssize_t sign = step < 0 ? -1 : 1;
842 for (Py_ssize_t i = start; i*sign < stop*sign; i += step) {
845 CallPyObjMethod(nseq, "push_back", item);
848 }
849
850 return nseq;
851 }
852
854 if (!pyindex)
855 return nullptr;
856
859
860// get hold of the actual std::vector<bool> (no cast, as vector is never a base)
861 std::vector<bool>* vb = (std::vector<bool>*)self->GetObject();
862
863// finally, return the value
864 if (bool((*vb)[index]))
867}
868
870{
871// std::vector<bool> is a special-case in C++, and its return type depends on
872// the compiler: treat it special here as well
873 if (!CPPInstance_Check(self) || self->ObjectIsA() != sVectorBoolTypeID) {
875 "require object of type std::vector<bool>, but %s given",
876 Cppyy::GetScopedFinalName(self->ObjectIsA()).c_str());
877 return nullptr;
878 }
879
880 if (!self->GetObject()) {
881 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
882 return nullptr;
883 }
884
885 int bval = 0; PyObject* idx = nullptr;
886 if (!PyArg_ParseTuple(args, const_cast<char*>("Oi:__setitem__"), &idx, &bval))
887 return nullptr;
888
890 if (!pyindex)
891 return nullptr;
892
895
896// get hold of the actual std::vector<bool> (no cast, as vector is never a base)
897 std::vector<bool>* vb = (std::vector<bool>*)self->GetObject();
898
899// finally, set the value
900 (*vb)[index] = (bool)bval;
901
903}
904
905
906//- array behavior as primitives ----------------------------------------------
907PyObject* ArrayInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
908{
909// std::array is normally only constructed using aggregate initialization, which
910// is a concept that does not exist in python, so use this custom constructor to
911// to fill the array using setitem
912
913 if (args && PyTuple_GET_SIZE(args) == 1 && PySequence_Check(PyTuple_GET_ITEM(args, 0))) {
914 // construct the empty array, then fill it
916 if (!result)
917 return nullptr;
918
919 PyObject* items = PyTuple_GET_ITEM(args, 0);
921 if (PySequence_Size(self) != fillsz) {
922 PyErr_Format(PyExc_ValueError, "received sequence of size %zd where %zd expected",
925 return nullptr;
926 }
927
929 for (Py_ssize_t i = 0; i < fillsz; ++i) {
935 if (!sires) {
938 return nullptr;
939 } else
941 }
943
944 return result;
945 } else
946 PyErr_Clear();
947
948// The given argument wasn't iterable: simply forward to regular constructor
950 if (realInit) {
951 PyObject* result = PyObject_Call(realInit, args, nullptr);
953 return result;
954 }
955
956 return nullptr;
957}
958
959
960//- map behavior as primitives ------------------------------------------------
962{
963// construct an empty map, then fill it with the key, value pairs
965 if (!result)
966 return nullptr;
967
969 for (Py_ssize_t i = 0; i < PySequence_Size(pairs); ++i) {
971 PyObject* sires = nullptr;
972 if (pair && PySequence_Check(pair) && PySequence_Size(pair) == 2) {
973 PyObject* key = PySequence_GetItem(pair, 0);
977 Py_DECREF(key);
978 }
979 Py_DECREF(pair);
980 if (!sires) {
983 if (!PyErr_Occurred())
984 PyErr_SetString(PyExc_TypeError, "Failed to fill map (argument not a dict or sequence of pairs)");
985 return nullptr;
986 } else
988 }
990
991 return result;
992}
993
994PyObject* MapInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
995{
996// Specialized map constructor to allow construction from mapping containers and
997// from tuples of pairs ("initializer_list style").
998
999// PyMapping_Check is not very discriminatory, as it basically only checks for the
1000// existence of __getitem__, hence the most common cases of tuple and list are
1001// dropped straight-of-the-bat (the PyMapping_Items call will fail on them).
1002 if (PyTuple_GET_SIZE(args) == 1 && PyMapping_Check(PyTuple_GET_ITEM(args, 0)) && \
1003 !(PyTuple_Check(PyTuple_GET_ITEM(args, 0)) || PyList_Check(PyTuple_GET_ITEM(args, 0)))) {
1004 PyObject* assoc = PyTuple_GET_ITEM(args, 0);
1005#if PY_VERSION_HEX < 0x03000000
1006 // to prevent warning about literal string, expand macro
1007 PyObject* items = PyObject_CallMethod(assoc, (char*)"items", nullptr);
1008#else
1009 // in p3, PyMapping_Items isn't a macro, but a function that short-circuits dict
1011#endif
1012 if (items && PySequence_Check(items)) {
1015 return result;
1016 }
1017
1019 PyErr_Clear();
1020
1021 // okay to fall through as long as 'self' has not been created (is done in MapFromPairs)
1022 }
1023
1024// tuple of pairs case (some mapping types are sequences)
1025 if (PyTuple_GET_SIZE(args) == 1 && PySequence_Check(PyTuple_GET_ITEM(args, 0)))
1026 return MapFromPairs(self, PyTuple_GET_ITEM(args, 0));
1027
1028// The given argument wasn't a mapping or tuple of pairs: forward to regular constructor
1030 if (realInit) {
1031 PyObject* result = PyObject_Call(realInit, args, nullptr);
1033 return result;
1034 }
1035
1036 return nullptr;
1037}
1038
1039#if __cplusplus <= 202002L
1041{
1042// Implement python's __contains__ for std::map/std::set
1043 PyObject* result = nullptr;
1044
1045 PyObject* iter = CallPyObjMethod(self, "find", obj);
1046 if (CPPInstance_Check(iter)) {
1048 if (CPPInstance_Check(end)) {
1049 if (!PyObject_RichCompareBool(iter, end, Py_EQ)) {
1051 result = Py_True;
1052 }
1053 }
1054 Py_XDECREF(end);
1055 }
1056 Py_XDECREF(iter);
1057
1058 if (!result) {
1059 PyErr_Clear(); // e.g. wrong argument type, which should always lead to False
1061 result = Py_False;
1062 }
1063
1064 return result;
1065}
1066#endif
1067
1068
1069//- set behavior as primitives ------------------------------------------------
1070PyObject* SetInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
1071{
1072// Specialized set constructor to allow construction from Python sets.
1073 if (PyTuple_GET_SIZE(args) == 1 && PySet_Check(PyTuple_GET_ITEM(args, 0))) {
1074 PyObject* pyset = PyTuple_GET_ITEM(args, 0);
1075
1076 // construct an empty set, then fill it
1078 if (!result)
1079 return nullptr;
1080
1082 if (iter) {
1083 PyObject* ins_call = PyObject_GetAttrString(self, (char*)"insert");
1084
1085 IterItemGetter getter{iter};
1086 Py_DECREF(iter);
1087
1088 PyObject* item = getter.get();
1089 while (item) {
1091 Py_DECREF(item);
1092 if (!insres) {
1095 return nullptr;
1096 } else
1098 item = getter.get();
1099 }
1101 }
1102
1103 return result;
1104 }
1105
1106// The given argument wasn't iterable: simply forward to regular constructor
1108 if (realInit) {
1109 PyObject* result = PyObject_Call(realInit, args, nullptr);
1111 return result;
1112 }
1113
1114 return nullptr;
1115}
1116
1117
1118//- STL container iterator support --------------------------------------------
1119static const ptrdiff_t PS_END_ADDR = 7; // non-aligned address, so no clash
1120static const ptrdiff_t PS_FLAG_ADDR = 11; // id.
1121static const ptrdiff_t PS_COLL_ADDR = 13; // id.
1122
1124{
1125// Implement python's __iter__ for low level views used through STL-type begin()/end()
1127
1128 if (LowLevelView_Check(iter)) {
1129 // builtin pointer iteration: can only succeed if a size is available
1131 if (sz == -1) {
1132 Py_DECREF(iter);
1133 return nullptr;
1134 }
1135 PyObject* lliter = Py_TYPE(iter)->tp_iter(iter);
1136 ((indexiterobject*)lliter)->ii_len = sz;
1137 Py_DECREF(iter);
1138 return lliter;
1139 }
1140
1141 if (iter) {
1142 Py_DECREF(iter);
1143 PyErr_SetString(PyExc_TypeError, "unrecognized iterator type for low level views");
1144 }
1145
1146 return nullptr;
1147}
1148
1150{
1151// Implement python's __iter__ for std::iterator<>s
1153 if (iter) {
1155 if (end) {
1156 if (CPPInstance_Check(iter)) {
1157 // use the data member cache to store extra state on the iterator object,
1158 // without it being visible on the Python side
1159 auto& dmc = ((CPPInstance*)iter)->GetDatamemberCache();
1160 dmc.push_back(std::make_pair(PS_END_ADDR, end));
1161
1162 // set a flag, indicating first iteration (reset in __next__)
1164 dmc.push_back(std::make_pair(PS_FLAG_ADDR, Py_False));
1165
1166 // make sure the iterated over collection remains alive for the duration
1167 Py_INCREF(self);
1168 dmc.push_back(std::make_pair(PS_COLL_ADDR, self));
1169 } else {
1170 // could store "end" on the object's dictionary anyway, but if end() returns
1171 // a user-customized object, then its __next__ is probably custom, too
1172 Py_DECREF(end);
1173 }
1174 }
1175 }
1176 return iter;
1177}
1178
1179//- generic iterator support over a sequence with operator[] and size ---------
1180//-----------------------------------------------------------------------------
1181static PyObject* index_iter(PyObject* c) {
1183 if (!ii) return nullptr;
1184
1185 Py_INCREF(c);
1186 ii->ii_container = c;
1187 ii->ii_pos = 0;
1188 ii->ii_len = PySequence_Size(c);
1189
1191 return (PyObject*)ii;
1192}
1193
1194
1195//- safe indexing for STL-like vector w/o iterator dictionaries ---------------
1196/* replaced by indexiterobject iteration, but may still have some future use ...
1197PyObject* CheckedGetItem(PyObject* self, PyObject* obj)
1198{
1199// Implement a generic python __getitem__ for STL-like classes that are missing the
1200// reflection info for their iterators. This is then used for iteration by means of
1201// consecutive indices, it such index is of integer type.
1202 Py_ssize_t size = PySequence_Size(self);
1203 Py_ssize_t idx = PyInt_AsSsize_t(obj);
1204 if ((size == (Py_ssize_t)-1 || idx == (Py_ssize_t)-1) && PyErr_Occurred()) {
1205 // argument conversion problem: let method itself resolve anew and report
1206 PyErr_Clear();
1207 return PyObject_CallMethodOneArg(self, PyStrings::gGetNoCheck, obj);
1208 }
1209
1210 bool inbounds = false;
1211 if (idx < 0) idx += size;
1212 if (0 <= idx && 0 <= size && idx < size)
1213 inbounds = true;
1214
1215 if (inbounds)
1216 return PyObject_CallMethodOneArg(self, PyStrings::gGetNoCheck, obj);
1217 else
1218 PyErr_SetString( PyExc_IndexError, "index out of range" );
1219
1220 return nullptr;
1221}*/
1222
1223
1224//- pair as sequence to allow tuple unpacking --------------------------------
1226{
1227// For std::map<> iteration, unpack std::pair<>s into tuples for the loop.
1228 long idx = PyLong_AsLong(pyindex);
1229 if (idx == -1 && PyErr_Occurred())
1230 return nullptr;
1231
1232 if (!CPPInstance_Check(self) || !((CPPInstance*)self)->GetObject()) {
1233 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
1234 return nullptr;
1235 }
1236
1237 if ((int)idx == 0)
1239 else if ((int)idx == 1)
1241
1242// still here? Trigger stop iteration
1243 PyErr_SetString(PyExc_IndexError, "out of bounds");
1244 return nullptr;
1245}
1246
1247//- simplistic len() functions -----------------------------------------------
1249 return PyInt_FromLong(2);
1250}
1251
1252
1253//- shared/unique_ptr behavior -----------------------------------------------
1254PyObject* SmartPtrInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
1255{
1256// since the shared/unique pointer will take ownership, we need to relinquish it
1258 if (realInit) {
1259 PyObject* result = PyObject_Call(realInit, args, nullptr);
1261 if (result && PyTuple_GET_SIZE(args) == 1 && CPPInstance_Check(PyTuple_GET_ITEM(args, 0))) {
1263 if (!(cppinst->fFlags & CPPInstance::kIsSmartPtr)) cppinst->CppOwns();
1264 }
1265 return result;
1266 }
1267 return nullptr;
1268}
1269
1270
1271//- string behavior as primitives --------------------------------------------
1272#if PY_VERSION_HEX >= 0x03000000
1273// TODO: this is wrong, b/c it doesn't order
1276}
1277#endif
1278static inline
1279PyObject* CPyCppyy_PyString_FromCppString(std::string_view s, bool native=true) {
1280 if (native)
1281 return PyBytes_FromStringAndSize(s.data(), s.size());
1282 return CPyCppyy_PyText_FromStringAndSize(s.data(), s.size());
1283}
1284
1285static inline
1286PyObject* CPyCppyy_PyString_FromCppString(std::wstring_view s, bool native=true) {
1287 PyObject* pyobj = PyUnicode_FromWideChar(s.data(), s.size());
1288 if (pyobj && native) {
1289 PyObject* pybytes = PyUnicode_AsEncodedString(pyobj, "UTF-8", "strict");
1291 pyobj = pybytes;
1292 }
1293 return pyobj;
1294}
1295
1296#define CPPYY_IMPL_STRING_PYTHONIZATION(type, name) \
1297static inline \
1298PyObject* name##StringGetData(PyObject* self, bool native=true) \
1299{ \
1300 if (CPyCppyy::CPPInstance_Check(self)) { \
1301 type* obj = ((type*)((CPPInstance*)self)->GetObject()); \
1302 if (obj) return CPyCppyy_PyString_FromCppString(*obj, native); \
1303 } \
1304 PyErr_Format(PyExc_TypeError, "object mismatch (%s expected)", #type); \
1305 return nullptr; \
1306} \
1307 \
1308PyObject* name##StringStr(PyObject* self) \
1309{ \
1310 PyObject* pyobj = name##StringGetData(self, false); \
1311 if (!pyobj) { \
1312 /* do a native conversion to make printing possible (debatable) */ \
1313 PyErr_Clear(); \
1314 PyObject* pybytes = name##StringGetData(self, true); \
1315 if (pybytes) { /* should not fail */ \
1316 pyobj = PyObject_Str(pybytes); \
1317 Py_DECREF(pybytes); \
1318 } \
1319 } \
1320 return pyobj; \
1321} \
1322 \
1323PyObject* name##StringBytes(PyObject* self) \
1324{ \
1325 return name##StringGetData(self, true); \
1326} \
1327 \
1328PyObject* name##StringRepr(PyObject* self) \
1329{ \
1330 PyObject* data = name##StringGetData(self, true); \
1331 if (data) { \
1332 PyObject* repr = PyObject_Repr(data); \
1333 Py_DECREF(data); \
1334 return repr; \
1335 } \
1336 return nullptr; \
1337} \
1338 \
1339PyObject* name##StringIsEqual(PyObject* self, PyObject* obj) \
1340{ \
1341 PyObject* data = name##StringGetData(self, PyBytes_Check(obj)); \
1342 if (data) { \
1343 PyObject* result = PyObject_RichCompare(data, obj, Py_EQ); \
1344 Py_DECREF(data); \
1345 return result; \
1346 } \
1347 return nullptr; \
1348} \
1349 \
1350PyObject* name##StringIsNotEqual(PyObject* self, PyObject* obj) \
1351{ \
1352 PyObject* data = name##StringGetData(self, PyBytes_Check(obj)); \
1353 if (data) { \
1354 PyObject* result = PyObject_RichCompare(data, obj, Py_NE); \
1355 Py_DECREF(data); \
1356 return result; \
1357 } \
1358 return nullptr; \
1359}
1360
1361// Only define STLStringCompare:
1362#define CPPYY_IMPL_STRING_PYTHONIZATION_CMP(type, name) \
1363CPPYY_IMPL_STRING_PYTHONIZATION(type, name) \
1364PyObject* name##StringCompare(PyObject* self, PyObject* obj) \
1365{ \
1366 PyObject* data = name##StringGetData(self, PyBytes_Check(obj)); \
1367 int result = 0; \
1368 if (data) { \
1369 result = PyObject_Compare(data, obj); \
1370 Py_DECREF(data); \
1371 } \
1372 if (PyErr_Occurred()) \
1373 return nullptr; \
1374 return PyInt_FromLong(result); \
1375}
1376
1380
1381static inline std::string* GetSTLString(CPPInstance* self) {
1382 if (!CPPInstance_Check(self)) {
1383 PyErr_SetString(PyExc_TypeError, "std::string object expected");
1384 return nullptr;
1385 }
1386
1387 std::string* obj = (std::string*)self->GetObject();
1388 if (!obj)
1389 PyErr_SetString(PyExc_ReferenceError, "attempt to access a null-pointer");
1390
1391 return obj;
1392}
1393
1395{
1396 std::string* obj = GetSTLString(self);
1397 if (!obj)
1398 return nullptr;
1399
1400 char* keywords[] = {(char*)"encoding", (char*)"errors", (char*)nullptr};
1401 const char* encoding = nullptr; const char* errors = nullptr;
1403 const_cast<char*>("s|s"), keywords, &encoding, &errors))
1404 return nullptr;
1405
1406 return PyUnicode_Decode(obj->data(), obj->size(), encoding, errors);
1407}
1408
1409#if __cplusplus <= 202302L
1411{
1412 std::string* obj = GetSTLString(self);
1413 if (!obj)
1414 return nullptr;
1415
1416 const char* needle = CPyCppyy_PyText_AsString(pyobj);
1417 if (!needle)
1418 return nullptr;
1419
1420 if (obj->find(needle) != std::string::npos) {
1422 }
1423
1425}
1426#endif
1427
1429{
1430 std::string* obj = GetSTLString(self);
1431 if (!obj)
1432 return nullptr;
1433
1434// both str and std::string have a method "replace", but the Python version only
1435// accepts strings and takes no keyword arguments, whereas the C++ version has no
1436// overload that takes a string
1437
1438 if (2 <= PyTuple_GET_SIZE(args) && CPyCppyy_PyText_Check(PyTuple_GET_ITEM(args, 0))) {
1439 PyObject* pystr = CPyCppyy_PyText_FromStringAndSize(obj->data(), obj->size());
1440 PyObject* meth = PyObject_GetAttrString(pystr, (char*)"replace");
1443 Py_DECREF(meth);
1444 return result;
1445 }
1446
1447 PyObject* cppreplace = PyObject_GetAttrString((PyObject*)self, (char*)"__cpp_replace");
1448 if (cppreplace) {
1449 PyObject* result = PyObject_Call(cppreplace, args, nullptr);
1451 return result;
1452 }
1453
1454 PyErr_SetString(PyExc_AttributeError, "\'std::string\' object has no attribute \'replace\'");
1455 return nullptr;
1456}
1457
1458#define CPYCPPYY_STRING_FINDMETHOD(name, cppname, pyname) \
1459PyObject* STLString##name(CPPInstance* self, PyObject* args, PyObject* /*kwds*/) \
1460{ \
1461 std::string* obj = GetSTLString(self); \
1462 if (!obj) \
1463 return nullptr; \
1464 \
1465 PyObject* cppmeth = PyObject_GetAttrString((PyObject*)self, (char*)#cppname);\
1466 if (cppmeth) { \
1467 PyObject* result = PyObject_Call(cppmeth, args, nullptr); \
1468 Py_DECREF(cppmeth); \
1469 if (result) { \
1470 if (PyLongOrInt_AsULong64(result) == (PY_ULONG_LONG)std::string::npos) {\
1471 Py_DECREF(result); \
1472 return PyInt_FromLong(-1); \
1473 } \
1474 return result; \
1475 } \
1476 PyErr_Clear(); \
1477 } \
1478 \
1479 PyObject* pystr = CPyCppyy_PyText_FromStringAndSize(obj->data(), obj->size());\
1480 PyObject* pymeth = PyObject_GetAttrString(pystr, (char*)#pyname); \
1481 Py_DECREF(pystr); \
1482 PyObject* result = PyObject_CallObject(pymeth, args); \
1483 Py_DECREF(pymeth); \
1484 return result; \
1485}
1486
1487// both str and std::string have method "find" and "rfin"; try the C++ version first
1488// and fall back on the Python one in case of failure
1491
1493{
1494 std::string* obj = GetSTLString(self);
1495 if (!obj)
1496 return nullptr;
1497
1498 PyObject* pystr = CPyCppyy_PyText_FromStringAndSize(obj->data(), obj->size());
1501 return attr;
1502}
1503
1504
1505#if 0
1507{
1508// force C++ string types conversion to Python str per Python __repr__ requirements
1510 if (!res || CPyCppyy_PyText_Check(res))
1511 return res;
1513 Py_DECREF(res);
1514 return str_res;
1515}
1516
1518{
1519// force C++ string types conversion to Python str per Python __str__ requirements
1521 if (!res || CPyCppyy_PyText_Check(res))
1522 return res;
1524 Py_DECREF(res);
1525 return str_res;
1526}
1527#endif
1528
1530{
1531// std::string objects hash to the same values as Python strings to allow
1532// matches in dictionaries etc.
1535 Py_DECREF(data);
1536 return h;
1537}
1538
1539
1540//- string_view behavior as primitive ----------------------------------------
1542{
1543// if constructed from a Python unicode object, the constructor will convert it
1544// to a temporary byte string, which is likely to go out of scope too soon; so
1545// buffer it as needed
1547 if (realInit) {
1548 PyObject *strbuf = nullptr, *newArgs = nullptr;
1549 if (PyTuple_GET_SIZE(args) == 1) {
1550 PyObject* arg0 = PyTuple_GET_ITEM(args, 0);
1551 if (PyUnicode_Check(arg0)) {
1552 // convert to the expected bytes array to control the temporary
1553 strbuf = PyUnicode_AsEncodedString(arg0, "UTF-8", "strict");
1554 newArgs = PyTuple_New(1);
1557 } else if (PyBytes_Check(arg0)) {
1558 // tie the life time of the provided string to the string_view
1559 Py_INCREF(arg0);
1560 strbuf = arg0;
1561 }
1562 }
1563
1564 PyObject* result = PyObject_Call(realInit, newArgs ? newArgs : args, nullptr);
1565
1568
1569 // if construction was successful and a string buffer was used, add a
1570 // life line to it from the string_view bound object
1571 if (result && self && strbuf)
1574
1575 return result;
1576 }
1577 return nullptr;
1578}
1579
1580
1581//- STL iterator behavior ----------------------------------------------------
1583{
1584// Python iterator protocol __next__ for STL forward iterators.
1585 bool mustIncrement = true;
1586 PyObject* last = nullptr;
1587 if (CPPInstance_Check(self)) {
1588 auto& dmc = ((CPPInstance*)self)->GetDatamemberCache();
1589 for (auto& p: dmc) {
1590 if (p.first == PS_END_ADDR) {
1591 last = p.second;
1592 Py_INCREF(last);
1593 } else if (p.first == PS_FLAG_ADDR) {
1594 mustIncrement = p.second == Py_True;
1595 if (!mustIncrement) {
1596 Py_DECREF(p.second);
1598 p.second = Py_True;
1599 }
1600 }
1601 }
1602 }
1603
1604 PyObject* next = nullptr;
1605 if (last) {
1606 // handle special case of empty container (i.e. self is end)
1607 if (!PyObject_RichCompareBool(last, self, Py_EQ)) {
1608 bool iter_valid = true;
1609 if (mustIncrement) {
1610 // prefer preinc, but allow post-inc; in both cases, it is "self" that has
1611 // the updated state to dereference
1613 if (!iter) {
1614 PyErr_Clear();
1615 static PyObject* dummy = PyInt_FromLong(1l);
1617 }
1619 Py_XDECREF(iter);
1620 }
1621
1622 if (iter_valid) {
1624 if (!next) PyErr_Clear();
1625 }
1626 }
1627 Py_DECREF(last);
1628 }
1629
1630 if (!next) PyErr_SetString(PyExc_StopIteration, "");
1631 return next;
1632}
1633
1634
1635//- STL complex<T> behavior --------------------------------------------------
1636#define COMPLEX_METH_GETSET(name, cppname) \
1637static PyObject* name##ComplexGet(PyObject* self, void*) { \
1638 return PyObject_CallMethodNoArgs(self, cppname); \
1639} \
1640static int name##ComplexSet(PyObject* self, PyObject* value, void*) { \
1641 PyObject* result = PyObject_CallMethodOneArg(self, cppname, value); \
1642 if (result) { \
1643 Py_DECREF(result); \
1644 return 0; \
1645 } \
1646 return -1; \
1647} \
1648PyGetSetDef name##Complex{(char*)#name, (getter)name##ComplexGet, (setter)name##ComplexSet, nullptr, nullptr};
1649
1652
1655 if (!real) return nullptr;
1656 double r = PyFloat_AsDouble(real);
1657 Py_DECREF(real);
1658 if (r == -1. && PyErr_Occurred())
1659 return nullptr;
1660
1662 if (!imag) return nullptr;
1663 double i = PyFloat_AsDouble(imag);
1664 Py_DECREF(imag);
1665 if (i == -1. && PyErr_Occurred())
1666 return nullptr;
1667
1668 return PyComplex_FromDoubles(r, i);
1669}
1670
1673 if (!real) return nullptr;
1674 double r = PyFloat_AsDouble(real);
1675 Py_DECREF(real);
1676 if (r == -1. && PyErr_Occurred())
1677 return nullptr;
1678
1680 if (!imag) 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 CPyCppyy_PyText_FromString(s.str().c_str());
1689}
1690
1692{
1693 return PyFloat_FromDouble(((std::complex<double>*)self->GetObject())->real());
1694}
1695
1696static int ComplexDRealSet(CPPInstance* self, PyObject* value, void*)
1697{
1698 double d = PyFloat_AsDouble(value);
1699 if (d == -1.0 && PyErr_Occurred())
1700 return -1;
1701 ((std::complex<double>*)self->GetObject())->real(d);
1702 return 0;
1703}
1704
1705PyGetSetDef ComplexDReal{(char*)"real", (getter)ComplexDRealGet, (setter)ComplexDRealSet, nullptr, nullptr};
1706
1707
1709{
1710 return PyFloat_FromDouble(((std::complex<double>*)self->GetObject())->imag());
1711}
1712
1713static int ComplexDImagSet(CPPInstance* self, PyObject* value, void*)
1714{
1715 double d = PyFloat_AsDouble(value);
1716 if (d == -1.0 && PyErr_Occurred())
1717 return -1;
1718 ((std::complex<double>*)self->GetObject())->imag(d);
1719 return 0;
1720}
1721
1722PyGetSetDef ComplexDImag{(char*)"imag", (getter)ComplexDImagGet, (setter)ComplexDImagSet, nullptr, nullptr};
1723
1725{
1726 double r = ((std::complex<double>*)self->GetObject())->real();
1727 double i = ((std::complex<double>*)self->GetObject())->imag();
1728 return PyComplex_FromDoubles(r, i);
1729}
1730
1731
1732} // unnamed namespace
1733
1734
1735//- public functions ---------------------------------------------------------
1736namespace CPyCppyy {
1737 std::set<std::string> gIteratorTypes;
1738}
1739
1740static inline
1741bool run_pythonizors(PyObject* pyclass, PyObject* pyname, const std::vector<PyObject*>& v)
1742{
1743 PyObject* args = PyTuple_New(2);
1746
1747 bool pstatus = true;
1748 for (auto pythonizor : v) {
1750 if (!result) {
1751 pstatus = false; // TODO: detail the error handling
1752 break;
1753 }
1755 }
1756 Py_DECREF(args);
1757
1758 return pstatus;
1759}
1760
1761bool CPyCppyy::Pythonize(PyObject* pyclass, const std::string& name)
1762{
1763// Add pre-defined pythonizations (for STL and ROOT) to classes based on their
1764// signature and/or class name.
1765 if (!pyclass)
1766 return false;
1767
1769
1770//- method name based pythonization ------------------------------------------
1771
1772// for smart pointer style classes that are otherwise not known as such; would
1773// prefer operator-> as that returns a pointer (which is simpler since it never
1774// has to deal with ref-assignment), but operator* plays better with STL iters
1775// and algorithms
1776// optional and expected use operator* to access a contained value, rather than
1777// to provide pointer-like access to an object.
1778 const bool has_value_semantics = IsTemplatedSTLClass(name, "optional") || IsTemplatedSTLClass(name, "expected");
1782 else
1786
1787// for pre-check of nullptr for boolean types
1789#if PY_VERSION_HEX >= 0x03000000
1790 const char* pybool_name = "__bool__";
1791#else
1792 const char* pybool_name = "__nonzero__";
1793#endif
1795 }
1796
1797 // for STL containers, and user classes modeled after them. Guard the alias to
1798 // __len__ by verifying that size() returns an integer type and the class has
1799 // begin()/end() or operator[] (i.e. is container-like). This prevents bool()
1800 // returning False for valid objects whose size() returns non-integer types like
1801 // std::optional<std::size_t>. Skip if size() has multiple overloads, as that
1802 // indicates it is not the simple container-style size() one would map to __len__.
1803 if (HasAttrDirect(pyclass, PyStrings::gSize, /*mustBeCPyCppyy=*/true) || HasAttrInMRO(pyclass, PyStrings::gSize)) {
1804 bool sizeIsInteger = false;
1807 auto *ol = (CPPOverload *)pySizeMethod;
1808 if (ol->HasMethods() && ol->fMethodInfo->fMethods.size() == 1) {
1810 ol->fMethodInfo->fMethods[0]->Reflex(Cppyy::Reflex::RETURN_TYPE, Cppyy::Reflex::AS_STRING);
1811 if (pyrestype) {
1814 }
1815 }
1816 }
1818
1819 if (sizeIsInteger) {
1823 if (hasIterators || hasSubscript) {
1824 Utility::AddToClass(pyclass, "__len__", "size");
1825 }
1826 }
1827 }
1828
1830 Utility::AddToClass(pyclass, "__contains__", "contains");
1831 }
1832
1833 if (!IsTemplatedSTLClass(name, "vector") && // vector is dealt with below
1836 // obtain the name of the return type
1837 const auto& v = Cppyy::GetMethodIndicesFromName(klass->fCppType, "begin");
1838 if (!v.empty()) {
1839 // check return type; if not explicitly an iterator, add it to the "known" return
1840 // types to add the "next" method on use
1842 const std::string& resname = Cppyy::GetMethodResultType(meth);
1843 bool isIterator = gIteratorTypes.find(resname) != gIteratorTypes.end();
1845 if (resname.find("iterator") == std::string::npos)
1846 gIteratorTypes.insert(resname);
1847 isIterator = true;
1848 }
1849
1850 if (isIterator) {
1851 // install iterator protocol a la STL
1854 } else {
1855 // still okay if this is some pointer type of builtin persuasion (general class
1856 // won't work: the return type needs to understand the iterator protocol)
1857 std::string resolved = Cppyy::ResolveName(resname);
1858 if (resolved.back() == '*' && Cppyy::IsBuiltin(resolved.substr(0, resolved.size()-1))) {
1861 }
1862 }
1863 }
1864 }
1865 if (!((PyTypeObject*)pyclass)->tp_iter && // no iterator resolved
1867 // Python will iterate over __getitem__ using integers, but C++ operator[] will never raise
1868 // a StopIteration. A checked getitem (raising IndexError if beyond size()) works in some
1869 // cases but would mess up if operator[] is meant to implement an associative container. So,
1870 // this has to be implemented as an iterator protocol.
1873 }
1874 }
1875
1876// operator==/!= are used in op_richcompare of CPPInstance, which subsequently allows
1877// comparisons to None; if no operator is available, a hook is installed for lazy
1878// lookups in the global and/or class namespace
1879 if (HasAttrDirect(pyclass, PyStrings::gEq, true) && \
1880 Cppyy::GetMethodIndicesFromName(klass->fCppType, "__eq__").empty()) {
1882 if (!klass->fOperators) klass->fOperators = new Utility::PyOperators();
1883 klass->fOperators->fEq = cppol;
1884 // re-insert the forwarding __eq__ from the CPPInstance in case there was a Python-side
1885 // override in the base class
1886 static PyObject* top_eq = nullptr;
1887 if (!top_eq) {
1890 Py_DECREF(top_eq); // make it borrowed
1892 }
1894 }
1895
1896 if (HasAttrDirect(pyclass, PyStrings::gNe, true) && \
1897 Cppyy::GetMethodIndicesFromName(klass->fCppType, "__ne__").empty()) {
1899 if (!klass->fOperators) klass->fOperators = new Utility::PyOperators();
1900 klass->fOperators->fNe = cppol;
1901 // re-insert the forwarding __ne__ (same reason as above for __eq__)
1902 static PyObject* top_ne = nullptr;
1903 if (!top_ne) {
1906 Py_DECREF(top_ne); // make it borrowed
1908 }
1910 }
1911
1912#if 0
1914 // guarantee that the result of __repr__ is a Python string
1915 Utility::AddToClass(pyclass, "__cpp_repr", "__repr__");
1917 }
1918
1920 // guarantee that the result of __str__ is a Python string
1921 Utility::AddToClass(pyclass, "__cpp_str", "__str__");
1923 }
1924#endif
1925
1926 if (Cppyy::IsAggregate(((CPPClass*)pyclass)->fCppType) && name.compare(0, 5, "std::", 5) != 0 &&
1927 name.compare(0, 6, "tuple<", 6) != 0) {
1928 // create a pseudo-constructor to allow initializer-style object creation
1929 Cppyy::TCppType_t kls = ((CPPClass*)pyclass)->fCppType;
1931 if (ndata) {
1932 std::string rname = name;
1934
1935 std::ostringstream initdef;
1936 initdef << "namespace __cppyy_internal {\n"
1937 << "void init_" << rname << "(" << name << "** self";
1938 bool codegen_ok = true;
1939 std::vector<std::string> arg_types, arg_names, arg_defaults;
1940 arg_types.reserve(ndata); arg_names.reserve(ndata); arg_defaults.reserve(ndata);
1941 for (Cppyy::TCppIndex_t i = 0; i < ndata; ++i) {
1943 continue;
1944
1945 const std::string& txt = Cppyy::GetDatamemberType(kls, i);
1946 const std::string& res = Cppyy::IsEnum(txt) ? txt : Cppyy::ResolveName(txt);
1947 const std::string& cpd = TypeManip::compound(res);
1948 std::string res_clean = TypeManip::clean_type(res, false, true);
1949
1950 if (res_clean == "internal_enum_type_t")
1951 res_clean = txt; // restore (properly scoped name)
1952
1953 if (res.rfind(']') == std::string::npos && res.rfind(')') == std::string::npos) {
1954 if (!cpd.empty()) arg_types.push_back(res_clean+cpd);
1955 else arg_types.push_back("const "+res_clean+"&");
1956 arg_names.push_back(Cppyy::GetDatamemberName(kls, i));
1957 if ((!cpd.empty() && cpd.back() == '*') || Cppyy::IsBuiltin(res_clean))
1958 arg_defaults.push_back("0");
1959 else {
1962 }
1963 } else {
1964 codegen_ok = false; // TODO: how to support arrays, anonymous enums, etc?
1965 break;
1966 }
1967 }
1968
1969 if (codegen_ok && !arg_types.empty()) {
1970 bool defaults_ok = arg_defaults.size() == arg_types.size();
1971 for (std::vector<std::string>::size_type i = 0; i < arg_types.size(); ++i) {
1972 initdef << ", " << arg_types[i] << " " << arg_names[i];
1973 if (defaults_ok) initdef << " = " << arg_defaults[i];
1974 }
1975 initdef << ") {\n *self = new " << name << "{";
1976 for (std::vector<std::string>::size_type i = 0; i < arg_names.size(); ++i) {
1977 if (i != 0) initdef << ", ";
1978 initdef << arg_names[i];
1979 }
1980 initdef << "};\n} }";
1981
1982 if (Cppyy::Compile(initdef.str(), true /* silent */)) {
1983 Cppyy::TCppScope_t cis = Cppyy::GetScope("__cppyy_internal");
1984 const auto& mix = Cppyy::GetMethodIndicesFromName(cis, "init_"+rname);
1985 if (mix.size()) {
1986 if (!Utility::AddToClass(pyclass, "__init__",
1987 new CPPFunction(cis, Cppyy::GetMethod(cis, mix[0]))))
1988 PyErr_Clear();
1989 }
1990 }
1991 }
1992 }
1993 }
1994
1995
1996//- class name based pythonization -------------------------------------------
1997
1998 if (IsTemplatedSTLClass(name, "span")) {
1999 // libstdc++ (GCC >= 15) implements std::span::iterator using a private
2000 // nested tag type, which makes the iterator non-instantiable by
2001 // CallFunc-generated wrappers (the return type cannot be named without
2002 // violating access rules).
2003 //
2004 // To preserve correct Python iteration semantics, we replace begin()/end()
2005 // for std::span to return a custom pointer-based iterator instead. This
2006 // avoids relying on std::span::iterator while still providing a real C++
2007 // iterator object that CPyCppyy can also wrap and expose via
2008 // __iter__/__next__.
2011 }
2012
2013 if (IsTemplatedSTLClass(name, "vector")) {
2014
2015 // std::vector<bool> is a special case in C++
2017 if (klass->fCppType == sVectorBoolTypeID) {
2020 } else {
2021 // constructor that takes python collections
2022 Utility::AddToClass(pyclass, "__real_init", "__init__");
2024
2025 // data with size
2026 Utility::AddToClass(pyclass, "__real_data", "data");
2027 PyErr_Clear(); // AddToClass might have failed for data
2029
2030 // The addition of the __array__ utility to std::vector Python proxies causes a
2031 // bug where the resulting array is a single dimension, causing loss of data when
2032 // converting to numpy arrays, for >1dim vectors. Since this C++ pythonization
2033 // was added with the upgrade in 6.32, and is only defined and used recursively,
2034 // the safe option is to disable this function and no longer add it.
2035#if 0
2036 // numpy array conversion
2038#endif
2039
2040 // checked getitem
2042 Utility::AddToClass(pyclass, "_getitem__unchecked", "__getitem__");
2044 }
2045
2046 // vector-optimized iterator protocol
2048
2049 // optimized __iadd__
2051
2052 // helpers for iteration
2053 const std::string& vtype = Cppyy::ResolveName(name+"::value_type");
2054 if (vtype.rfind("value_type") == std::string::npos) { // actually resolved?
2058 }
2059
2060 size_t typesz = Cppyy::SizeOf(name+"::value_type");
2061 if (typesz) {
2065 }
2066 }
2067 }
2068
2069 else if (IsTemplatedSTLClass(name, "array")) {
2070 // constructor that takes python associative collections
2071 Utility::AddToClass(pyclass, "__real_init", "__init__");
2073 }
2074
2075 else if (IsTemplatedSTLClass(name, "map") || IsTemplatedSTLClass(name, "unordered_map")) {
2076 // constructor that takes python associative collections
2077 Utility::AddToClass(pyclass, "__real_init", "__init__");
2079#if __cplusplus <= 202002L
2080 // From C++20, std::map and std::unordered_map already implement a contains() method.
2082#endif
2083 }
2084
2085 else if (IsTemplatedSTLClass(name, "set")) {
2086 // constructor that takes python associative collections
2087 Utility::AddToClass(pyclass, "__real_init", "__init__");
2089
2090#if __cplusplus <= 202002L
2091 // From C++20, std::set already implements a contains() method.
2093#endif
2094 }
2095
2096 else if (IsTemplatedSTLClass(name, "pair")) {
2099 }
2100
2101 if (IsTemplatedSTLClass(name, "shared_ptr") || IsTemplatedSTLClass(name, "unique_ptr")) {
2102 Utility::AddToClass(pyclass, "__real_init", "__init__");
2104 }
2105
2106 else if (!((PyTypeObject*)pyclass)->tp_iter && \
2107 (name.find("iterator") != std::string::npos || gIteratorTypes.find(name) != gIteratorTypes.end())) {
2108 ((PyTypeObject*)pyclass)->tp_iternext = (iternextfunc)STLIterNext;
2112 }
2113
2114 else if (name == "string" || name == "std::string") { // TODO: ask backend as well
2121#if __cplusplus <= 202302L
2122 // From C++23, std::sting already implements a contains() method.
2124#endif
2126 Utility::AddToClass(pyclass, "__cpp_find", "find");
2128 Utility::AddToClass(pyclass, "__cpp_rfind", "rfind");
2130 Utility::AddToClass(pyclass, "__cpp_replace", "replace");
2133
2134 // to allow use of std::string in dictionaries and findable with str
2136 }
2137
2138 else if (name == "basic_string_view<char,char_traits<char> >" || name == "std::basic_string_view<char>") {
2139 Utility::AddToClass(pyclass, "__real_init", "__init__");
2147 }
2148
2149// The first condition was already present in upstream CPyCppyy. The other two
2150// are special to ROOT, because its reflection layer gives us the types without
2151// the "std::" namespace. On some platforms, that applies only to the template
2152// arguments, and on others also to the "basic_string".
2153 else if (name == "std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> >"
2154 || name == "basic_string<wchar_t,char_traits<wchar_t>,allocator<wchar_t> >"
2155 || name == "std::basic_string<wchar_t,char_traits<wchar_t>,allocator<wchar_t> >"
2156 ) {
2163 }
2164
2165 else if (name == "complex<double>" || name == "std::complex<double>") {
2166 Utility::AddToClass(pyclass, "__cpp_real", "real");
2168 Utility::AddToClass(pyclass, "__cpp_imag", "imag");
2172 }
2173
2174 else if (IsTemplatedSTLClass(name, "complex")) {
2175 Utility::AddToClass(pyclass, "__cpp_real", "real");
2177 Utility::AddToClass(pyclass, "__cpp_imag", "imag");
2181 }
2182
2183// direct user access; there are two calls here:
2184// - explicit pythonization: won't fall through to the base classes and is preferred if present
2185// - normal pythonization: only called if explicit isn't present, falls through to base classes
2186 bool bUserOk = true; PyObject* res = nullptr;
2190 bUserOk = (bool)res;
2191 } else {
2193 if (func) {
2194 res = PyObject_CallFunctionObjArgs(func, pyclass, pyname, nullptr);
2195 Py_DECREF(func);
2196 bUserOk = (bool)res;
2197 } else
2198 PyErr_Clear();
2199 }
2200 if (!bUserOk) {
2202 return false;
2203 } else {
2204 Py_XDECREF(res);
2205 // pyname handed to args tuple below
2206 }
2207
2208// call registered pythonizors, if any: first run the namespace-specific pythonizors, then
2209// the global ones (the idea is to allow writing a pythonizor that see all classes)
2210 bool pstatus = true;
2212 auto &pyzMap = pythonizations();
2213 if (!outer_scope.empty()) {
2214 auto p = pyzMap.find(outer_scope);
2215 if (p != pyzMap.end()) {
2217 name.substr(outer_scope.size()+2, std::string::npos).c_str());
2220 }
2221 }
2222
2223 if (pstatus) {
2224 auto p = pyzMap.find("");
2225 if (p != pyzMap.end())
2226 pstatus = run_pythonizors(pyclass, pyname, p->second);
2227 }
2228
2230
2231// phew! all done ...
2232 return pstatus;
2233}
#define Py_TYPE(ob)
Definition CPyCppyy.h:196
#define Py_RETURN_TRUE
Definition CPyCppyy.h:272
#define Py_RETURN_FALSE
Definition CPyCppyy.h:276
#define PyInt_FromSsize_t
Definition CPyCppyy.h:217
#define CPyCppyy_PyText_FromStringAndSize
Definition CPyCppyy.h:85
#define PyBytes_Check
Definition CPyCppyy.h:61
#define PyInt_AsSsize_t
Definition CPyCppyy.h:216
#define CPyCppyy_PySliceCast
Definition CPyCppyy.h:189
#define CPyCppyy_PyText_AsString
Definition CPyCppyy.h:76
long Py_hash_t
Definition CPyCppyy.h:114
static PyObject * PyObject_CallMethodOneArg(PyObject *obj, PyObject *name, PyObject *arg)
Definition CPyCppyy.h:377
#define PyBytes_FromStringAndSize
Definition CPyCppyy.h:70
#define Py_RETURN_NONE
Definition CPyCppyy.h:268
#define CPyCppyy_PyText_Type
Definition CPyCppyy.h:94
static PyObject * PyObject_CallMethodNoArgs(PyObject *obj, PyObject *name)
Definition CPyCppyy.h:373
#define CPPYY__next__
Definition CPyCppyy.h:112
#define CPyCppyy_PyText_FromString
Definition CPyCppyy.h:81
#define CPyCppyy_PyText_Check
Definition CPyCppyy.h:74
bool PyUnstable_Object_IsUniqueReferencedTemporary(PyObject *pyobject)
uint32_t fFlags
_object PyObject
#define CPPYY_IMPL_STRING_PYTHONIZATION_CMP(type, name)
static bool run_pythonizors(PyObject *pyclass, PyObject *pyname, const std::vector< PyObject * > &v)
#define COMPLEX_METH_GETSET(name, cppname)
#define CPYCPPYY_STRING_FINDMETHOD(name, cppname, pyname)
#define PyObject_LengthHint
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
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
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
const_iterator end() const
PyObject * gContains
Definition PyStrings.cxx:10
PyObject * gCTypesType
Definition PyStrings.cxx:41
PyObject * gRealInit
Definition PyStrings.cxx:44
PyObject * gExPythonize
Definition PyStrings.cxx:74
PyObject * gLifeLine
Definition PyStrings.cxx:31
PyObject * gGetItem
Definition PyStrings.cxx:25
PyObject * gHasValue
Definition PyStrings.cxx:24
PyObject * gCppBool
Definition PyStrings.cxx:12
PyObject * gCppReal
Definition PyStrings.cxx:66
PyObject * gPythonize
Definition PyStrings.cxx:75
PyObject * gTypeCode
Definition PyStrings.cxx:40
PyObject * gPostInc
Definition PyStrings.cxx:19
PyObject * gCppImag
Definition PyStrings.cxx:67
PyObject * gValueSize
Definition PyStrings.cxx:64
PyObject * gSetItem
Definition PyStrings.cxx:27
PyObject * gGetNoCheck
Definition PyStrings.cxx:26
PyObject * gCppRepr
Definition PyStrings.cxx:37
PyObject * gValueType
Definition PyStrings.cxx:63
void cppscope_to_legalname(std::string &cppscope)
std::string clean_type(const std::string &cppname, bool template_strip=true, bool const_strip=true)
std::string compound(const std::string &name)
std::string extract_namespace(const std::string &name)
Py_ssize_t GetBuffer(PyObject *pyobject, char tc, int size, void *&buf, bool check=true)
Definition Utility.cxx:919
bool AddToClass(PyObject *pyclass, const char *label, PyCFunction cfunc, int flags=METH_VARARGS)
Definition Utility.cxx:185
PyTypeObject VectorIter_Type
PyObject * GetScopeProxy(Cppyy::TCppScope_t)
static PyObject * GetAttrDirect(PyObject *pyclass, PyObject *pyname)
bool Pythonize(PyObject *pyclass, const std::string &name)
bool CPPOverload_Check(T *object)
Definition CPPOverload.h:94
std::map< std::string, std::vector< PyObject * > > & pythonizations()
bool CPPScope_Check(T *object)
Definition CPPScope.h:81
bool LowLevelView_Check(T *object)
bool CPPInstance_Check(T *object)
PyTypeObject IndexIter_Type
PyObject * gThisModule
Definition CPPMethod.cxx:30
CPYCPPYY_EXTERN Converter * CreateConverter(const std::string &name, cdims_t=0)
std::set< std::string > gIteratorTypes
const RequestId_t RETURN_TYPE
Definition Reflex.h:18
const FormatId_t AS_STRING
Definition Reflex.h:24
size_t TCppIndex_t
Definition cpp_cppyy.h:40
RPY_EXPORTED bool IsIntegerType(const std::string &type_name)
RPY_EXPORTED size_t SizeOf(TCppType_t klass)
intptr_t TCppMethod_t
Definition cpp_cppyy.h:38
RPY_EXPORTED bool IsDefaultConstructable(TCppType_t type)
RPY_EXPORTED bool IsEnum(const std::string &type_name)
RPY_EXPORTED std::vector< TCppIndex_t > GetMethodIndicesFromName(TCppScope_t scope, const std::string &name)
RPY_EXPORTED TCppIndex_t GetNumDatamembers(TCppScope_t scope, bool accept_namespace=false)
RPY_EXPORTED bool Compile(const std::string &code, bool silent=false)
RPY_EXPORTED TCppScope_t gGlobalScope
Definition cpp_cppyy.h:69
RPY_EXPORTED std::string ResolveName(const std::string &cppitem_name)
TCppScope_t TCppType_t
Definition cpp_cppyy.h:35
RPY_EXPORTED bool IsAggregate(TCppType_t type)
RPY_EXPORTED std::string GetScopedFinalName(TCppType_t type)
RPY_EXPORTED bool IsPublicData(TCppScope_t scope, TCppIndex_t idata)
RPY_EXPORTED bool IsBuiltin(const std::string &type_name)
RPY_EXPORTED bool IsStaticData(TCppScope_t scope, TCppIndex_t idata)
RPY_EXPORTED std::string GetDatamemberType(TCppScope_t scope, TCppIndex_t idata)
RPY_EXPORTED TCppMethod_t GetMethod(TCppScope_t scope, TCppIndex_t imeth)
RPY_EXPORTED bool IsSmartPtr(TCppType_t type)
RPY_EXPORTED TCppScope_t GetScope(const std::string &scope_name)
size_t TCppScope_t
Definition cpp_cppyy.h:34
RPY_EXPORTED std::string GetMethodResultType(TCppMethod_t)
RPY_EXPORTED std::string GetDatamemberName(TCppScope_t scope, TCppIndex_t idata)
CoordSystem::Scalar get(DisplacementVector2D< CoordSystem, Tag > const &p)