Logo ROOT  
Reference Guide
 
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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// get an attribute without causing getattr lookups
58 if (dct) {
61 return attr;
62 }
63 return nullptr;
64}
65
66//-----------------------------------------------------------------------------
67inline bool IsTemplatedSTLClass(const std::string& name, const std::string& klass) {
68// Scan the name of the class and determine whether it is a template instantiation.
69 auto pos = name.find(klass);
70 return (pos == 0 || pos == 5) && name.find("::", name.rfind(">")) == std::string::npos;
71}
72
73// to prevent compiler warnings about const char* -> char*
74inline PyObject* CallPyObjMethod(PyObject* obj, const char* meth)
75{
76// Helper; call method with signature: obj->meth().
77 Py_INCREF(obj);
78 PyObject* result = PyObject_CallMethod(obj, const_cast<char*>(meth), const_cast<char*>(""));
79 Py_DECREF(obj);
80 return result;
81}
82
83//-----------------------------------------------------------------------------
84inline PyObject* CallPyObjMethod(PyObject* obj, const char* meth, PyObject* arg1)
85{
86// Helper; call method with signature: obj->meth(arg1).
87 Py_INCREF(obj);
89 obj, const_cast<char*>(meth), const_cast<char*>("O"), arg1);
90 Py_DECREF(obj);
91 return result;
92}
93
94//-----------------------------------------------------------------------------
96{
97// Helper; converts python index into straight C index.
99 if (idx == (Py_ssize_t)-1 && PyErr_Occurred())
100 return nullptr;
101
103 if (idx >= size || (idx < 0 && idx < -size)) {
104 PyErr_SetString(PyExc_IndexError, "index out of range");
105 return nullptr;
106 }
107
108 PyObject* pyindex = nullptr;
109 if (idx >= 0) {
111 pyindex = index;
112 } else
114
115 return pyindex;
116}
117
118//-----------------------------------------------------------------------------
119inline bool AdjustSlice(const Py_ssize_t nlen, Py_ssize_t& start, Py_ssize_t& stop, Py_ssize_t& step)
120{
121// Helper; modify slice range to match the container.
122 if ((step > 0 && stop <= start) || (step < 0 && start <= stop))
123 return false;
124
125 if (start < 0) start = 0;
126 if (start >= nlen-1;
127 if (step >= nlen) step = nlen;
128
129 stop = step > 0 ? std::min(nlen, stop) : (stop >= 0 ? stop : -1);
130 return true;
131}
132
133//-----------------------------------------------------------------------------
135{
136// Helper; call method with signature: meth(pyindex).
139 if (!pyindex) {
141 return nullptr;
142 }
143
147 return result;
148}
149
150//- "smart pointer" behavior ---------------------------------------------------
152{
153// Follow operator*() if present (available in python as __deref__), so that
154// smart pointers behave as expected.
156 // TODO: these calls come from TemplateProxy and are unlikely to be needed in practice,
157 // whereas as-is, they can accidentally dereference the result of end() on some STL
158 // containers. Obviously, this is a dumb hack that should be resolved more fundamentally.
160 return nullptr;
161 }
162
164 PyErr_SetString(PyExc_TypeError, "getattr(): attribute name must be string");
165
167 if (!pyptr)
168 return nullptr;
169
170// prevent a potential infinite loop
171 if (Py_TYPE(pyptr) == Py_TYPE(self)) {
174 PyErr_Format(PyExc_AttributeError, "%s has no attribute \'%s\'",
178
180 return nullptr;
181 }
182
185 return result;
186}
187
188//-----------------------------------------------------------------------------
190{
191// Follow operator->() if present (available in python as __follow__), so that
192// smart pointers behave as expected.
194 PyErr_SetString(PyExc_TypeError, "getattr(): attribute name must be string");
195
197 if (!pyptr)
198 return nullptr;
199
202 return result;
203}
204
205//- pointer checking bool converter -------------------------------------------
207{
208 if (!CPPInstance_Check(self)) {
209 PyErr_SetString(PyExc_TypeError, "C++ object proxy expected");
210 return nullptr;
211 }
212
213 if (!((CPPInstance*)self)->GetObject())
215
217}
218
219//- vector behavior as primitives ----------------------------------------------
220#if PY_VERSION_HEX < 0x03040000
221#define PyObject_LengthHint _PyObject_LengthHint
222#endif
223
224// TODO: can probably use the below getters in the InitializerListConverter
225struct ItemGetter {
226 ItemGetter(PyObject* pyobj) : fPyObject(pyobj) { Py_INCREF(fPyObject); }
227 virtual ~ItemGetter() { Py_DECREF(fPyObject); }
228 virtual Py_ssize_t size() = 0;
229 virtual PyObject* get() = 0;
230 PyObject* fPyObject;
231};
232
233struct CountedItemGetter : public ItemGetter {
234 CountedItemGetter(PyObject* pyobj) : ItemGetter(pyobj), fCur(0) {}
235 Py_ssize_t fCur;
236};
237
238struct TupleItemGetter : public CountedItemGetter {
239 using CountedItemGetter::CountedItemGetter;
240 virtual Py_ssize_t size() { return PyTuple_GET_SIZE(fPyObject); }
241 virtual PyObject* get() {
242 if (fCur < PyTuple_GET_SIZE(fPyObject)) {
243 PyObject* item = PyTuple_GET_ITEM(fPyObject, fCur++);
245 return item;
246 }
247 PyErr_SetString(PyExc_StopIteration, "end of tuple");
248 return nullptr;
249 }
250};
251
252struct ListItemGetter : public CountedItemGetter {
253 using CountedItemGetter::CountedItemGetter;
254 virtual Py_ssize_t size() { return PyList_GET_SIZE(fPyObject); }
255 virtual PyObject* get() {
256 if (fCur < PyList_GET_SIZE(fPyObject)) {
257 PyObject* item = PyList_GET_ITEM(fPyObject, fCur++);
259 return item;
260 }
261 PyErr_SetString(PyExc_StopIteration, "end of list");
262 return nullptr;
263 }
264};
265
266struct SequenceItemGetter : public CountedItemGetter {
267 using CountedItemGetter::CountedItemGetter;
268 virtual Py_ssize_t size() {
269 Py_ssize_t sz = PySequence_Size(fPyObject);
270 if (sz < 0) {
271 PyErr_Clear();
272 return PyObject_LengthHint(fPyObject, 8);
273 }
274 return sz;
275 }
276 virtual PyObject* get() { return PySequence_GetItem(fPyObject, fCur++); }
277};
278
279struct IterItemGetter : public ItemGetter {
280 using ItemGetter::ItemGetter;
281 virtual Py_ssize_t size() { return PyObject_LengthHint(fPyObject, 8); }
282 virtual PyObject* get() { return (*(Py_TYPE(fPyObject)->tp_iternext))(fPyObject); }
283};
284
285static ItemGetter* GetGetter(PyObject* args)
286{
287// Create an ItemGetter to loop over the iterable argument, if any.
288 ItemGetter* getter = nullptr;
289
290 if (PyTuple_GET_SIZE(args) == 1) {
291 PyObject* fi = PyTuple_GET_ITEM(args, 0);
293 return nullptr; // do not accept string to fill std::vector<char>
294
295 // TODO: this only tests for new-style buffers, which is too strict, but a
296 // generic check for Py_TYPE(fi)->tp_as_buffer is too loose (note that the
297 // main use case is numpy, which offers the new interface)
299 return nullptr;
300
302 getter = new TupleItemGetter(fi);
303 else if (PyList_CheckExact(fi))
304 getter = new ListItemGetter(fi);
305 else if (PySequence_Check(fi))
306 getter = new SequenceItemGetter(fi);
307 else {
309 if (iter) {
310 getter = new IterItemGetter{iter};
311 Py_DECREF(iter);
312 }
313 else PyErr_Clear();
314 }
315 }
316
317 return getter;
318}
319
320static bool FillVector(PyObject* vecin, PyObject* args, ItemGetter* getter)
321{
322 Py_ssize_t sz = getter->size();
323 if (sz < 0)
324 return false;
325
326// reserve memory as applicable
327 if (0 < sz) {
328 PyObject* res = PyObject_CallMethod(vecin, (char*)"reserve", (char*)"n", sz);
329 Py_DECREF(res);
330 } else // i.e. sz == 0, so empty container: done
331 return true;
332
333 bool fill_ok = true;
334
335// two main options: a list of lists (or tuples), or a list of objects; the former
336// are emplace_back'ed, the latter push_back'ed
338 if (!fi) PyErr_Clear();
340 // use emplace_back to construct the vector entries one by one
341 PyObject* eb_call = PyObject_GetAttrString(vecin, (char*)"emplace_back");
343 bool value_is_vector = false;
345 // if the value_type is a vector, then allow for initialization from sequences
346 if (std::string(CPyCppyy_PyText_AsString(vtype)).rfind("std::vector", 0) != std::string::npos)
347 value_is_vector = true;
348 } else
349 PyErr_Clear();
351
352 if (eb_call) {
354 for (int i = 0; /* until break */; ++i) {
355 PyObject* item = getter->get();
356 if (item) {
358 eb_args = PyTuple_New(1);
360 } else if (PyTuple_CheckExact(item)) {
361 eb_args = item;
362 } else if (PyList_CheckExact(item)) {
365 for (Py_ssize_t j = 0; j < isz; ++j) {
369 }
371 } else {
373 PyErr_Format(PyExc_TypeError, "argument %d is not a tuple or list", i);
374 fill_ok = false;
375 break;
376 }
379 if (!ebres) {
380 fill_ok = false;
381 break;
382 }
384 } else {
385 if (PyErr_Occurred()) {
388 fill_ok = false;
389 else { PyErr_Clear(); }
390 }
391 break;
392 }
393 }
395 }
396 } else {
397 // use push_back to add the vector entries one by one
398 PyObject* pb_call = PyObject_GetAttrString(vecin, (char*)"push_back");
399 if (pb_call) {
400 for (;;) {
401 PyObject* item = getter->get();
402 if (item) {
405 if (!pbres) {
406 fill_ok = false;
407 break;
408 }
410 } else {
411 if (PyErr_Occurred()) {
414 fill_ok = false;
415 else { PyErr_Clear(); }
416 }
417 break;
418 }
419 }
421 }
422 }
423 Py_XDECREF(fi);
424
425 return fill_ok;
426}
427
428PyObject* VectorIAdd(PyObject* self, PyObject* args, PyObject* /* kwds */)
429{
430// Implement fast __iadd__ on std::vector (generic __iadd__ is in Python)
431 ItemGetter* getter = GetGetter(args);
432
433 if (getter) {
434 bool fill_ok = FillVector(self, args, getter);
435 delete getter;
436
437 if (!fill_ok)
438 return nullptr;
439
441 return self;
442 }
443
444// if no getter, it could still be b/c we have a buffer (e.g. numpy); looping over
445// a buffer here is slow, so use insert() instead
446 if (PyTuple_GET_SIZE(args) == 1) {
447 PyObject* fi = PyTuple_GET_ITEM(args, 0);
450 if (vend) {
453 return result;
454 }
455 }
456 }
457
458 if (!PyErr_Occurred())
459 PyErr_SetString(PyExc_TypeError, "argument is not iterable");
460 return nullptr; // error already set
461}
462
463
464PyObject* VectorInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
465{
466// Specialized vector constructor to allow construction from containers; allowing
467// such construction from initializer_list instead would possible, but can be
468// error-prone. This use case is common enough for std::vector to implement it
469// directly, except for arrays (which can be passed wholesale) and strings (which
470// won't convert properly as they'll be seen as buffers)
471
472 ItemGetter* getter = GetGetter(args);
473
474 if (getter) {
475 // construct an empty vector, then back-fill it
477 if (!result) {
478 delete getter;
479 return nullptr;
480 }
481
482 bool fill_ok = FillVector(self, args, getter);
483 delete getter;
484
485 if (!fill_ok) {
487 return nullptr;
488 }
489
490 return result;
491 }
492
493// The given argument wasn't iterable: simply forward to regular constructor
495 if (realInit) {
496 PyObject* result = PyObject_Call(realInit, args, nullptr);
498 return result;
499 }
500
501 return nullptr;
502}
503
504//---------------------------------------------------------------------------
506{
507 PyObject* pydata = CallPyObjMethod(self, "__real_data");
509 return pydata;
510
512 if (!pylen) {
513 PyErr_Clear();
514 return pydata;
515 }
516
517 long clen = PyInt_AsLong(pylen);
519
521 ((CPPInstance*)pydata)->CastToArray(clen);
522 return pydata;
523 }
524
525 ((LowLevelView*)pydata)->resize((size_t)clen);
526 return pydata;
527}
528
529
530// This function implements __array__, added to std::vector python proxies and causes
531// a bug (see explanation at Utility::AddToClass(pyclass, "__array__"...) in CPyCppyy::Pythonize)
532// The recursive nature of this function, passes each subarray (pydata) to the next call and only
533// the final buffer is cast to a lowlevel view and resized (in VectorData), resulting in only the
534// first 1D array to be returned. See https://github.com/root-project/root/issues/17729
535// It is temporarily removed to prevent errors due to -Wunused-function, since it is no longer added.
536#if 0
537//---------------------------------------------------------------------------
539{
540 PyObject* pydata = VectorData(self, nullptr);
545 return newarr;
546}
547#endif
548
549//-----------------------------------------------------------------------------
550static PyObject* vector_iter(PyObject* v) {
552 if (!vi) return nullptr;
553
554 Py_INCREF(v);
555 vi->ii_container = v;
556
557// tell the iterator code to set a life line if this container is a temporary
558 vi->vi_flags = vectoriterobject::kDefault;
559 if (Py_REFCNT(v) <= 2 || (((CPPInstance*)v)->fFlags & CPPInstance::kIsValue))
561
563 if (pyvalue_type) {
565 if (pyvalue_size) {
566 vi->vi_stride = PyLong_AsLong(pyvalue_size);
568 } else {
569 PyErr_Clear();
570 vi->vi_stride = 0;
571 }
572
574 std::string value_type = CPyCppyy_PyText_AsString(pyvalue_type);
575 value_type = Cppyy::ResolveName(value_type);
576 vi->vi_klass = Cppyy::GetScope(value_type);
577 if (!vi->vi_klass) {
578 // look for a special case of pointer to a class type (which is a builtin, but it
579 // is more useful to treat it polymorphically by allowing auto-downcasts)
580 const std::string& clean_type = TypeManip::clean_type(value_type, false, false);
582 if (c && TypeManip::compound(value_type) == "*") {
583 vi->vi_klass = c;
585 }
586 }
587 if (vi->vi_klass) {
588 vi->vi_converter = nullptr;
589 if (!vi->vi_flags) {
590 if (value_type.back() != '*') // meaning, object stored by-value
592 }
593 } else
594 vi->vi_converter = CPyCppyy::CreateConverter(value_type);
595 if (!vi->vi_stride) vi->vi_stride = Cppyy::SizeOf(value_type);
596
597 } else if (CPPScope_Check(pyvalue_type)) {
598 vi->vi_klass = ((CPPClass*)pyvalue_type)->fCppType;
599 vi->vi_converter = nullptr;
600 if (!vi->vi_stride) vi->vi_stride = Cppyy::SizeOf(vi->vi_klass);
601 if (!vi->vi_flags) vi->vi_flags = vectoriterobject::kNeedLifeLine;
602 }
603
604 PyObject* pydata = CallPyObjMethod(v, "__real_data");
605 if (!pydata || Utility::GetBuffer(pydata, '*', 1, vi->vi_data, false) == 0)
606 vi->vi_data = CPPInstance_Check(pydata) ? ((CPPInstance*)pydata)->GetObjectRaw() : nullptr;
608
609 } else {
610 PyErr_Clear();
611 vi->vi_data = nullptr;
612 vi->vi_stride = 0;
613 vi->vi_converter = nullptr;
614 vi->vi_klass = 0;
615 vi->vi_flags = 0;
616 }
617
619
620 vi->ii_pos = 0;
621 vi->ii_len = PySequence_Size(v);
622
624 return (PyObject*)vi;
625}
626
628{
629// Implement python's __getitem__ for std::vector<>s.
630 if (PySlice_Check(index)) {
631 if (!self->GetObject()) {
632 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
633 return nullptr;
634 }
635
638
639 start, stop, step;
641
643 if (!AdjustSlice(nlen, start, stop, step))
644 return nseq;
645
646 const Py_ssize_t sign = step < 0 ? -1 : 1;
647 for (Py_ssize_t i = start; i*sign < stop*sign; i += step) {
650 CallPyObjMethod(nseq, "push_back", item);
653 }
654
655 return nseq;
656 }
657
659}
660
661
663
665{
666// std::vector<bool> is a special-case in C++, and its return type depends on
667// the compiler: treat it special here as well
668 if (!CPPInstance_Check(self) || self->ObjectIsA() != sVectorBoolTypeID) {
670 "require object of type std::vector<bool>, but %s given",
671 Cppyy::GetScopedFinalName(self->ObjectIsA()).c_str());
672 return nullptr;
673 }
674
675 if (!self->GetObject()) {
676 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
677 return nullptr;
678 }
679
680 if (PySlice_Check(idx)) {
683
684 start, stop, step;
687 if (!AdjustSlice(nlen, start, stop, step))
688 return nseq;
689
690 const Py_ssize_t sign = step < 0 ? -1 : 1;
691 for (Py_ssize_t i = start; i*sign < stop*sign; i += step) {
694 CallPyObjMethod(nseq, "push_back", item);
697 }
698
699 return nseq;
700 }
701
703 if (!pyindex)
704 return nullptr;
705
708
709// get hold of the actual std::vector<bool> (no cast, as vector is never a base)
710 std::vector<bool>* vb = (std::vector<bool>*)self->GetObject();
711
712// finally, return the value
713 if (bool((*vb)[index]))
716}
717
719{
720// std::vector<bool> is a special-case in C++, and its return type depends on
721// the compiler: treat it special here as well
722 if (!CPPInstance_Check(self) || self->ObjectIsA() != sVectorBoolTypeID) {
724 "require object of type std::vector<bool>, but %s given",
725 Cppyy::GetScopedFinalName(self->ObjectIsA()).c_str());
726 return nullptr;
727 }
728
729 if (!self->GetObject()) {
730 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
731 return nullptr;
732 }
733
734 int bval = 0; PyObject* idx = nullptr;
735 if (!PyArg_ParseTuple(args, const_cast<char*>("Oi:__setitem__"), &idx, &bval))
736 return nullptr;
737
739 if (!pyindex)
740 return nullptr;
741
744
745// get hold of the actual std::vector<bool> (no cast, as vector is never a base)
746 std::vector<bool>* vb = (std::vector<bool>*)self->GetObject();
747
748// finally, set the value
749 (*vb)[index] = (bool)bval;
750
752}
753
754
755//- array behavior as primitives ----------------------------------------------
756PyObject* ArrayInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
757{
758// std::array is normally only constructed using aggregate initialization, which
759// is a concept that does not exist in python, so use this custom constructor to
760// to fill the array using setitem
761
762 if (args && PyTuple_GET_SIZE(args) == 1 && PySequence_Check(PyTuple_GET_ITEM(args, 0))) {
763 // construct the empty array, then fill it
765 if (!result)
766 return nullptr;
767
768 PyObject* items = PyTuple_GET_ITEM(args, 0);
770 if (PySequence_Size(self) != fillsz) {
771 PyErr_Format(PyExc_ValueError, "received sequence of size %zd where %zd expected",
774 return nullptr;
775 }
776
778 for (Py_ssize_t i = 0; i < fillsz; ++i) {
784 if (!sires) {
787 return nullptr;
788 } else
790 }
792
793 return result;
794 } else
795 PyErr_Clear();
796
797// The given argument wasn't iterable: simply forward to regular constructor
799 if (realInit) {
800 PyObject* result = PyObject_Call(realInit, args, nullptr);
802 return result;
803 }
804
805 return nullptr;
806}
807
808
809//- map behavior as primitives ------------------------------------------------
811{
812// construct an empty map, then fill it with the key, value pairs
814 if (!result)
815 return nullptr;
816
818 for (Py_ssize_t i = 0; i < PySequence_Size(pairs); ++i) {
820 PyObject* sires = nullptr;
821 if (pair && PySequence_Check(pair) && PySequence_Size(pair) == 2) {
822 PyObject* key = PySequence_GetItem(pair, 0);
826 Py_DECREF(key);
827 }
828 Py_DECREF(pair);
829 if (!sires) {
832 if (!PyErr_Occurred())
833 PyErr_SetString(PyExc_TypeError, "Failed to fill map (argument not a dict or sequence of pairs)");
834 return nullptr;
835 } else
837 }
839
840 return result;
841}
842
843PyObject* MapInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
844{
845// Specialized map constructor to allow construction from mapping containers and
846// from tuples of pairs ("initializer_list style").
847
848// PyMapping_Check is not very discriminatory, as it basically only checks for the
849// existence of __getitem__, hence the most common cases of tuple and list are
850// dropped straight-of-the-bat (the PyMapping_Items call will fail on them).
851 if (PyTuple_GET_SIZE(args) == 1 && PyMapping_Check(PyTuple_GET_ITEM(args, 0)) && \
853 PyObject* assoc = PyTuple_GET_ITEM(args, 0);
854#if PY_VERSION_HEX < 0x03000000
855 // to prevent warning about literal string, expand macro
856 PyObject* items = PyObject_CallMethod(assoc, (char*)"items", nullptr);
857#else
858 // in p3, PyMapping_Items isn't a macro, but a function that short-circuits dict
860#endif
861 if (items && PySequence_Check(items)) {
864 return result;
865 }
866
868 PyErr_Clear();
869
870 // okay to fall through as long as 'self' has not been created (is done in MapFromPairs)
871 }
872
873// tuple of pairs case (some mapping types are sequences)
874 if (PyTuple_GET_SIZE(args) == 1 && PySequence_Check(PyTuple_GET_ITEM(args, 0)))
875 return MapFromPairs(self, PyTuple_GET_ITEM(args, 0));
876
877// The given argument wasn't a mapping or tuple of pairs: forward to regular constructor
879 if (realInit) {
880 PyObject* result = PyObject_Call(realInit, args, nullptr);
882 return result;
883 }
884
885 return nullptr;
886}
887
889{
890// Implement python's __contains__ for std::map/std::set
891 PyObject* result = nullptr;
892
893 PyObject* iter = CallPyObjMethod(self, "find", obj);
894 if (CPPInstance_Check(iter)) {
895 PyStrings::gEnd);
896 if (CPPInstance_Check(end)) {
897 if (!PyObject_RichCompareBool(iter, end, Py_EQ)) {
899 result = Py_True;
900 }
901 }
902 Py_XDECREF(end);
903 }
904 Py_XDECREF(iter);
905
906 if (!result) {
907 PyErr_Clear(); // e.g. wrong argument type, which should always lead to False
910 }
911
912 return result;
913}
914
915
916//- set behavior as primitives ------------------------------------------------
917PyObject* SetInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
918{
919// Specialized set constructor to allow construction from Python sets.
920 if (PyTuple_GET_SIZE(args) == 1 && PySet_Check(PyTuple_GET_ITEM(args, 0))) {
921 PyObject* pyset = PyTuple_GET_ITEM(args, 0);
922
923 // construct an empty set, then fill it
925 if (!result)
926 return nullptr;
927
929 if (iter) {
930 PyObject* ins_call = PyObject_GetAttrString(self, (char*)"insert");
931
932 IterItemGetter getter{iter};
933 Py_DECREF(iter);
934
935 PyObject* item = getter.get();
936 while (item) {
939 if (!insres) {
942 return nullptr;
943 } else
945 item = getter.get();
946 }
948 }
949
950 return result;
951 }
952
953// The given argument wasn't iterable: simply forward to regular constructor
955 if (realInit) {
956 PyObject* result = PyObject_Call(realInit, args, nullptr);
958 return result;
959 }
960
961 return nullptr;
962}
963
964
965//- STL container iterator support --------------------------------------------
966static const ptrdiff_t PS_END_ADDR = 7; // non-aligned address, so no clash
967static const ptrdiff_t PS_FLAG_ADDR = 11; // id.
968static const ptrdiff_t PS_COLL_ADDR = 13; // id.
969
971{
972// Implement python's __iter__ for low level views used through STL-type begin()/end()
974
975 if (LowLevelView_Check(iter)) {
976 // builtin pointer iteration: can only succeed if a size is available
978 if (sz == -1) {
979 Py_DECREF(iter);
980 return nullptr;
981 }
982 PyObject* lliter = Py_TYPE(iter)->tp_iter(iter);
983 ((indexiterobject*)lliter)->ii_len = sz;
984 Py_DECREF(iter);
985 return lliter;
986 }
987
988 if (iter) {
989 Py_DECREF(iter);
990 PyErr_SetString(PyExc_TypeError, "unrecognized iterator type for low level views");
991 }
992
993 return nullptr;
994}
995
997{
998// Implement python's __iter__ for std::iterator<>s
1000 if (iter) {
1001 PyStrings::gEnd);
1002 if (end) {
1003 if (CPPInstance_Check(iter)) {
1004 // use the data member cache to store extra state on the iterator object,
1005 // without it being visible on the Python side
1006 auto& dmc = ((CPPInstance*)iter)->GetDatamemberCache();
1007 dmc.push_back(std::make_pair(PS_END_ADDR, end));
1008
1009 // set a flag, indicating first iteration (reset in __next__)
1011 dmc.push_back(std::make_pair(PS_FLAG_ADDR, Py_False));
1012
1013 // make sure the iterated over collection remains alive for the duration
1014 Py_INCREF(self);
1015 dmc.push_back(std::make_pair(PS_COLL_ADDR, self));
1016 } else {
1017 // could store "end" on the object's dictionary anyway, but if end() returns
1018 // a user-customized object, then its __next__ is probably custom, too
1019 Py_DECREF(end);
1020 }
1021 }
1022 }
1023 return iter;
1024}
1025
1026//- generic iterator support over a sequence with operator[] and size ---------
1027//-----------------------------------------------------------------------------
1028static PyObject* index_iter(PyObject* c) {
1030 if (!ii) return nullptr;
1031
1032 Py_INCREF(c);
1033 ii->ii_container = c;
1034 ii->ii_pos = 0;
1035 ii->ii_len = PySequence_Size(c);
1036
1038 return (PyObject*)ii;
1039}
1040
1041
1042//- safe indexing for STL-like vector w/o iterator dictionaries ---------------
1043/* replaced by indexiterobject iteration, but may still have some future use ...
1044PyObject* CheckedGetItem(PyObject* self, PyObject* obj)
1045{
1046// Implement a generic python __getitem__ for STL-like classes that are missing the
1047// reflection info for their iterators. This is then used for iteration by means of
1048// consecutive indices, it such index is of integer type.
1049 Py_ssize_t size = PySequence_Size(self);
1050 Py_ssize_t idx = PyInt_AsSsize_t(obj);
1051 if ((size == (Py_ssize_t)-1 || idx == (Py_ssize_t)-1) && PyErr_Occurred()) {
1052 // argument conversion problem: let method itself resolve anew and report
1053 PyErr_Clear();
1054 return PyObject_CallMethodOneArg(self, PyStrings::gGetNoCheck, obj);
1055 }
1056
1057 bool inbounds = false;
1058 if (idx < 0) idx += size;
1059 if (0 <= idx && 0 <= size && idx < size)
1060 inbounds = true;
1061
1062 if (inbounds)
1063 return PyObject_CallMethodOneArg(self, PyStrings::gGetNoCheck, obj);
1064 else
1065 PyErr_SetString( PyExc_IndexError, "index out of range" );
1066
1067 return nullptr;
1068}*/
1069
1070
1071//- pair as sequence to allow tuple unpacking --------------------------------
1073{
1074// For std::map<> iteration, unpack std::pair<>s into tuples for the loop.
1075 long idx = PyLong_AsLong(pyindex);
1076 if (idx == -1 && PyErr_Occurred())
1077 return nullptr;
1078
1079 if (!CPPInstance_Check(self) || !((CPPInstance*)self)->GetObject()) {
1080 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
1081 return nullptr;
1082 }
1083
1084 if ((int)idx == 0)
1086 else if ((int)idx == 1)
1088
1089// still here? Trigger stop iteration
1090 PyErr_SetString(PyExc_IndexError, "out of bounds");
1091 return nullptr;
1092}
1093
1094//- simplistic len() functions -----------------------------------------------
1096 return PyInt_FromLong(2);
1097}
1098
1099
1100//- shared/unique_ptr behavior -----------------------------------------------
1101PyObject* SmartPtrInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
1102{
1103// since the shared/unique pointer will take ownership, we need to relinquish it
1105 if (realInit) {
1106 PyObject* result = PyObject_Call(realInit, args, nullptr);
1108 if (result && PyTuple_GET_SIZE(args) == 1 && CPPInstance_Check(PyTuple_GET_ITEM(args, 0))) {
1110 if (!(cppinst->fFlags & CPPInstance::kIsSmartPtr)) cppinst->CppOwns();
1111 }
1112 return result;
1113 }
1114 return nullptr;
1115}
1116
1117
1118//- string behavior as primitives --------------------------------------------
1119#if PY_VERSION_HEX >= 0x03000000
1120// TODO: this is wrong, b/c it doesn't order
1123}
1124#endif
1125static inline
1126PyObject* CPyCppyy_PyString_FromCppString(std::string* s, bool native=true) {
1127 if (native)
1128 return PyBytes_FromStringAndSize(s->data(), s->size());
1129 return CPyCppyy_PyText_FromStringAndSize(s->data(), s->size());
1130}
1131
1132static inline
1133PyObject* CPyCppyy_PyString_FromCppString(std::wstring* s, bool native=true) {
1134 PyObject* pyobj = PyUnicode_FromWideChar(s->data(), s->size());
1135 if (pyobj && native) {
1136 PyObject* pybytes = PyUnicode_AsEncodedString(pyobj, "UTF-8", "strict");
1138 pyobj = pybytes;
1139 }
1140 return pyobj;
1141}
1142
1143#define CPPYY_IMPL_STRING_PYTHONIZATION(type, name) \
1144static inline \
1145PyObject* name##StringGetData(PyObject* self, bool native=true) \
1146{ \
1147 if (CPyCppyy::CPPInstance_Check(self)) { \
1148 type* obj = ((type*)((CPPInstance*)self)->GetObject()); \
1149 if (obj) return CPyCppyy_PyString_FromCppString(obj, native); \
1150 } \
1151 PyErr_Format(PyExc_TypeError, "object mismatch (%s expected)", #type); \
1152 return nullptr; \
1153} \
1154 \
1155PyObject* name##StringStr(PyObject* self) \
1156{ \
1157 PyObject* pyobj = name##StringGetData(self, false); \
1158 if (!pyobj) { \
1159 /* do a native conversion to make printing possible (debatable) */ \
1160 PyErr_Clear(); \
1161 PyObject* pybytes = name##StringGetData(self, true); \
1162 if (pybytes) { /* should not fail */ \
1163 pyobj = PyObject_Str(pybytes); \
1164 Py_DECREF(pybytes); \
1165 } \
1166 } \
1167 return pyobj; \
1168} \
1169 \
1170PyObject* name##StringBytes(PyObject* self) \
1171{ \
1172 return name##StringGetData(self, true); \
1173} \
1174 \
1175PyObject* name##StringRepr(PyObject* self) \
1176{ \
1177 PyObject* data = name##StringGetData(self, true); \
1178 if (data) { \
1179 PyObject* repr = PyObject_Repr(data); \
1180 Py_DECREF(data); \
1181 return repr; \
1182 } \
1183 return nullptr; \
1184} \
1185 \
1186PyObject* name##StringIsEqual(PyObject* self, PyObject* obj) \
1187{ \
1188 PyObject* data = name##StringGetData(self, PyBytes_Check(obj)); \
1189 if (data) { \
1190 PyObject* result = PyObject_RichCompare(data, obj, Py_EQ); \
1191 Py_DECREF(data); \
1192 return result; \
1193 } \
1194 return nullptr; \
1195} \
1196 \
1197PyObject* name##StringIsNotEqual(PyObject* self, PyObject* obj) \
1198{ \
1199 PyObject* data = name##StringGetData(self, PyBytes_Check(obj)); \
1200 if (data) { \
1201 PyObject* result = PyObject_RichCompare(data, obj, Py_NE); \
1202 Py_DECREF(data); \
1203 return result; \
1204 } \
1205 return nullptr; \
1206}
1207
1208// Only define STLStringCompare:
1209#define CPPYY_IMPL_STRING_PYTHONIZATION_CMP(type, name) \
1210CPPYY_IMPL_STRING_PYTHONIZATION(type, name) \
1211PyObject* name##StringCompare(PyObject* self, PyObject* obj) \
1212{ \
1213 PyObject* data = name##StringGetData(self, PyBytes_Check(obj)); \
1214 int result = 0; \
1215 if (data) { \
1216 result = PyObject_Compare(data, obj); \
1217 Py_DECREF(data); \
1218 } \
1219 if (PyErr_Occurred()) \
1220 return nullptr; \
1221 return PyInt_FromLong(result); \
1222}
1223
1226
1227static inline std::string* GetSTLString(CPPInstance* self) {
1228 if (!CPPInstance_Check(self)) {
1229 PyErr_SetString(PyExc_TypeError, "std::string object expected");
1230 return nullptr;
1231 }
1232
1233 std::string* obj = (std::string*)self->GetObject();
1234 if (!obj)
1235 PyErr_SetString(PyExc_ReferenceError, "attempt to access a null-pointer");
1236
1237 return obj;
1238}
1239
1241{
1242 std::string* obj = GetSTLString(self);
1243 if (!obj)
1244 return nullptr;
1245
1246 char* keywords[] = {(char*)"encoding", (char*)"errors", (char*)nullptr};
1247 const char* encoding = nullptr; const char* errors = nullptr;
1249 const_cast<char*>("s|s"), keywords, &encoding, &errors))
1250 return nullptr;
1251
1252 return PyUnicode_Decode(obj->data(), obj->size(), encoding, errors);
1253}
1254
1256{
1257 std::string* obj = GetSTLString(self);
1258 if (!obj)
1259 return nullptr;
1260
1261 const char* needle = CPyCppyy_PyText_AsString(pyobj);
1262 if (!needle)
1263 return nullptr;
1264
1265 if (obj->find(needle) != std::string::npos) {
1267 }
1268
1270}
1271
1273{
1274 std::string* obj = GetSTLString(self);
1275 if (!obj)
1276 return nullptr;
1277
1278// both str and std::string have a method "replace", but the Python version only
1279// accepts strings and takes no keyword arguments, whereas the C++ version has no
1280// overload that takes a string
1281
1282 if (2 <= PyTuple_GET_SIZE(args) && CPyCppyy_PyText_Check(PyTuple_GET_ITEM(args, 0))) {
1283 PyObject* pystr = CPyCppyy_PyText_FromStringAndSize(obj->data(), obj->size());
1284 PyObject* meth = PyObject_GetAttrString(pystr, (char*)"replace");
1287 Py_DECREF(meth);
1288 return result;
1289 }
1290
1291 PyObject* cppreplace = PyObject_GetAttrString((PyObject*)self, (char*)"__cpp_replace");
1292 if (cppreplace) {
1293 PyObject* result = PyObject_Call(cppreplace, args, nullptr);
1295 return result;
1296 }
1297
1298 PyErr_SetString(PyExc_AttributeError, "\'std::string\' object has no attribute \'replace\'");
1299 return nullptr;
1300}
1301
1302#define CPYCPPYY_STRING_FINDMETHOD(name, cppname, pyname) \
1303PyObject* STLString##name(CPPInstance* self, PyObject* args, PyObject* /*kwds*/) \
1304{ \
1305 std::string* obj = GetSTLString(self); \
1306 if (!obj) \
1307 return nullptr; \
1308 \
1309 PyObject* cppmeth = PyObject_GetAttrString((PyObject*)self, (char*)#cppname);\
1310 if (cppmeth) { \
1311 PyObject* result = PyObject_Call(cppmeth, args, nullptr); \
1312 Py_DECREF(cppmeth); \
1313 if (result) { \
1314 if (PyLongOrInt_AsULong64(result) == (PY_ULONG_LONG)std::string::npos) {\
1315 Py_DECREF(result); \
1316 return PyInt_FromLong(-1); \
1317 } \
1318 return result; \
1319 } \
1320 PyErr_Clear(); \
1321 } \
1322 \
1323 PyObject* pystr = CPyCppyy_PyText_FromStringAndSize(obj->data(), obj->size());\
1324 PyObject* pymeth = PyObject_GetAttrString(pystr, (char*)#pyname); \
1325 Py_DECREF(pystr); \
1326 PyObject* result = PyObject_CallObject(pymeth, args); \
1327 Py_DECREF(pymeth); \
1328 return result; \
1329}
1330
1331// both str and std::string have method "find" and "rfin"; try the C++ version first
1332// and fall back on the Python one in case of failure
1335
1337{
1338 std::string* obj = GetSTLString(self);
1339 if (!obj)
1340 return nullptr;
1341
1342 PyObject* pystr = CPyCppyy_PyText_FromStringAndSize(obj->data(), obj->size());
1345 return attr;
1346}
1347
1348
1349#if 0
1351{
1352// force C++ string types conversion to Python str per Python __repr__ requirements
1354 if (!res || CPyCppyy_PyText_Check(res))
1355 return res;
1357 Py_DECREF(res);
1358 return str_res;
1359}
1360
1362{
1363// force C++ string types conversion to Python str per Python __str__ requirements
1365 if (!res || CPyCppyy_PyText_Check(res))
1366 return res;
1368 Py_DECREF(res);
1369 return str_res;
1370}
1371#endif
1372
1374{
1375// std::string objects hash to the same values as Python strings to allow
1376// matches in dictionaries etc.
1379 Py_DECREF(data);
1380 return h;
1381}
1382
1383
1384//- string_view behavior as primitive ----------------------------------------
1386{
1387// if constructed from a Python unicode object, the constructor will convert it
1388// to a temporary byte string, which is likely to go out of scope too soon; so
1389// buffer it as needed
1391 if (realInit) {
1392 PyObject *strbuf = nullptr, *newArgs = nullptr;
1393 if (PyTuple_GET_SIZE(args) == 1) {
1394 PyObject* arg0 = PyTuple_GET_ITEM(args, 0);
1395 if (PyUnicode_Check(arg0)) {
1396 // convert to the expected bytes array to control the temporary
1397 strbuf = PyUnicode_AsEncodedString(arg0, "UTF-8", "strict");
1398 newArgs = PyTuple_New(1);
1401 } else if (PyBytes_Check(arg0)) {
1402 // tie the life time of the provided string to the string_view
1403 Py_INCREF(arg0);
1404 strbuf = arg0;
1405 }
1406 }
1407
1408 PyObject* result = PyObject_Call(realInit, newArgs ? newArgs : args, nullptr);
1409
1412
1413 // if construction was successful and a string buffer was used, add a
1414 // life line to it from the string_view bound object
1415 if (result && self && strbuf)
1418
1419 return result;
1420 }
1421 return nullptr;
1422}
1423
1424
1425//- STL iterator behavior ----------------------------------------------------
1427{
1428// Python iterator protocol __next__ for STL forward iterators.
1429 bool mustIncrement = true;
1430 PyObject* last = nullptr;
1431 if (CPPInstance_Check(self)) {
1432 auto& dmc = ((CPPInstance*)self)->GetDatamemberCache();
1433 for (auto& p: dmc) {
1434 if (p.first == PS_END_ADDR) {
1435 last = p.second;
1436 Py_INCREF(last);
1437 } else if (p.first == PS_FLAG_ADDR) {
1438 mustIncrement = p.second == Py_True;
1439 if (!mustIncrement) {
1440 Py_DECREF(p.second);
1442 p.second = Py_True;
1443 }
1444 }
1445 }
1446 }
1447
1448 PyObject* next = nullptr;
1449 if (last) {
1450 // handle special case of empty container (i.e. self is end)
1451 if (!PyObject_RichCompareBool(last, self, Py_EQ)) {
1452 bool iter_valid = true;
1453 if (mustIncrement) {
1454 // prefer preinc, but allow post-inc; in both cases, it is "self" that has
1455 // the updated state to dereference
1457 if (!iter) {
1458 PyErr_Clear();
1459 static PyObject* dummy = PyInt_FromLong(1l);
1461 }
1463 Py_XDECREF(iter);
1464 }
1465
1466 if (iter_valid) {
1468 if (!next) PyErr_Clear();
1469 }
1470 }
1471 Py_DECREF(last);
1472 }
1473
1474 if (!next) PyErr_SetString(PyExc_StopIteration, "");
1475 return next;
1476}
1477
1478
1479//- STL complex<T> behavior --------------------------------------------------
1480#define COMPLEX_METH_GETSET(name, cppname) \
1481static PyObject* name##ComplexGet(PyObject* self, void*) { \
1482 return PyObject_CallMethodNoArgs(self, cppname); \
1483} \
1484static int name##ComplexSet(PyObject* self, PyObject* value, void*) { \
1485 PyObject* result = PyObject_CallMethodOneArg(self, cppname, value); \
1486 if (result) { \
1487 Py_DECREF(result); \
1488 return 0; \
1489 } \
1490 return -1; \
1491} \
1492PyGetSetDef name##Complex{(char*)#name, (getter)name##ComplexGet, (setter)name##ComplexSet, nullptr, nullptr};
1493
1496
1499 if (!real) return nullptr;
1500 double r = PyFloat_AsDouble(real);
1501 Py_DECREF(real);
1502 if (r == -1. && PyErr_Occurred())
1503 return nullptr;
1504
1506 if (!imag) return nullptr;
1507 double i = PyFloat_AsDouble(imag);
1508 Py_DECREF(imag);
1509 if (i == -1. && PyErr_Occurred())
1510 return nullptr;
1511
1512 return PyComplex_FromDoubles(r, i);
1513}
1514
1517 if (!real) return nullptr;
1518 double r = PyFloat_AsDouble(real);
1519 Py_DECREF(real);
1520 if (r == -1. && PyErr_Occurred())
1521 return nullptr;
1522
1524 if (!imag) return nullptr;
1525 double i = PyFloat_AsDouble(imag);
1526 Py_DECREF(imag);
1527 if (i == -1. && PyErr_Occurred())
1528 return nullptr;
1529
1530 std::ostringstream s;
1531 s << '(' << r << '+' << i << "j)";
1532 return CPyCppyy_PyText_FromString(s.str().c_str());
1533}
1534
1536{
1537 return PyFloat_FromDouble(((std::complex<double>*)self->GetObject())->real());
1538}
1539
1540static int ComplexDRealSet(CPPInstance* self, PyObject* value, void*)
1541{
1542 double d = PyFloat_AsDouble(value);
1543 if (d == -1.0 && PyErr_Occurred())
1544 return -1;
1545 ((std::complex<double>*)self->GetObject())->real(d);
1546 return 0;
1547}
1548
1549PyGetSetDef ComplexDReal{(char*)"real", (getter)ComplexDRealGet, (setter)ComplexDRealSet, nullptr, nullptr};
1550
1551
1553{
1554 return PyFloat_FromDouble(((std::complex<double>*)self->GetObject())->imag());
1555}
1556
1557static int ComplexDImagSet(CPPInstance* self, PyObject* value, void*)
1558{
1559 double d = PyFloat_AsDouble(value);
1560 if (d == -1.0 && PyErr_Occurred())
1561 return -1;
1562 ((std::complex<double>*)self->GetObject())->imag(d);
1563 return 0;
1564}
1565
1566PyGetSetDef ComplexDImag{(char*)"imag", (getter)ComplexDImagGet, (setter)ComplexDImagSet, nullptr, nullptr};
1567
1569{
1570 double r = ((std::complex<double>*)self->GetObject())->real();
1571 double i = ((std::complex<double>*)self->GetObject())->imag();
1572 return PyComplex_FromDoubles(r, i);
1573}
1574
1575
1576} // unnamed namespace
1577
1578
1579//- public functions ---------------------------------------------------------
1580namespace CPyCppyy {
1581 std::set<std::string> gIteratorTypes;
1582}
1583
1584static inline
1585bool run_pythonizors(PyObject* pyclass, PyObject* pyname, const std::vector<PyObject*>& v)
1586{
1587 PyObject* args = PyTuple_New(2);
1590
1591 bool pstatus = true;
1592 for (auto pythonizor : v) {
1594 if (!result) {
1595 pstatus = false; // TODO: detail the error handling
1596 break;
1597 }
1599 }
1600 Py_DECREF(args);
1601
1602 return pstatus;
1603}
1604
1605bool CPyCppyy::Pythonize(PyObject* pyclass, const std::string& name)
1606{
1607// Add pre-defined pythonizations (for STL and ROOT) to classes based on their
1608// signature and/or class name.
1609 if (!pyclass)
1610 return false;
1611
1613
1614//- method name based pythonization ------------------------------------------
1615
1616// for smart pointer style classes that are otherwise not known as such; would
1617// prefer operator-> as that returns a pointer (which is simpler since it never
1618// has to deal with ref-assignment), but operator* plays better with STL iters
1619// and algorithms
1624
1625// for pre-check of nullptr for boolean types
1627#if PY_VERSION_HEX >= 0x03000000
1628 const char* pybool_name = "__bool__";
1629#else
1630 const char* pybool_name = "__nonzero__";
1631#endif
1633 }
1634
1635// for STL containers, and user classes modeled after them
1637 Utility::AddToClass(pyclass, "__len__", "size");
1638
1639 if (!IsTemplatedSTLClass(name, "vector") && // vector is dealt with below
1642 // obtain the name of the return type
1643 const auto& v = Cppyy::GetMethodIndicesFromName(klass->fCppType, "begin");
1644 if (!v.empty()) {
1645 // check return type; if not explicitly an iterator, add it to the "known" return
1646 // types to add the "next" method on use
1648 const std::string& resname = Cppyy::GetMethodResultType(meth);
1649 bool isIterator = gIteratorTypes.find(resname) != gIteratorTypes.end();
1651 if (resname.find("iterator") == std::string::npos)
1652 gIteratorTypes.insert(resname);
1653 isIterator = true;
1654 }
1655
1656 if (isIterator) {
1657 // install iterator protocol a la STL
1660 } else {
1661 // still okay if this is some pointer type of builtin persuasion (general class
1662 // won't work: the return type needs to understand the iterator protocol)
1663 std::string resolved = Cppyy::ResolveName(resname);
1664 if (resolved.back() == '*' && Cppyy::IsBuiltin(resolved.substr(0, resolved.size()-1))) {
1667 }
1668 }
1669 }
1670 }
1671 if (!((PyTypeObject*)pyclass)->tp_iter && // no iterator resolved
1673 // Python will iterate over __getitem__ using integers, but C++ operator[] will never raise
1674 // a StopIteration. A checked getitem (raising IndexError if beyond size()) works in some
1675 // cases but would mess up if operator[] is meant to implement an associative container. So,
1676 // this has to be implemented as an iterator protocol.
1679 }
1680 }
1681
1682// operator==/!= are used in op_richcompare of CPPInstance, which subsequently allows
1683// comparisons to None; if no operator is available, a hook is installed for lazy
1684// lookups in the global and/or class namespace
1685 if (HasAttrDirect(pyclass, PyStrings::gEq, true) && \
1686 Cppyy::GetMethodIndicesFromName(klass->fCppType, "__eq__").empty()) {
1688 if (!klass->fOperators) klass->fOperators = new Utility::PyOperators();
1689 klass->fOperators->fEq = cppol;
1690 // re-insert the forwarding __eq__ from the CPPInstance in case there was a Python-side
1691 // override in the base class
1692 static PyObject* top_eq = nullptr;
1693 if (!top_eq) {
1696 Py_DECREF(top_eq); // make it borrowed
1698 }
1700 }
1701
1702 if (HasAttrDirect(pyclass, PyStrings::gNe, true) && \
1703 Cppyy::GetMethodIndicesFromName(klass->fCppType, "__ne__").empty()) {
1705 if (!klass->fOperators) klass->fOperators = new Utility::PyOperators();
1706 klass->fOperators->fNe = cppol;
1707 // re-insert the forwarding __ne__ (same reason as above for __eq__)
1708 static PyObject* top_ne = nullptr;
1709 if (!top_ne) {
1712 Py_DECREF(top_ne); // make it borrowed
1714 }
1716 }
1717
1718#if 0
1720 // guarantee that the result of __repr__ is a Python string
1721 Utility::AddToClass(pyclass, "__cpp_repr", "__repr__");
1723 }
1724
1726 // guarantee that the result of __str__ is a Python string
1727 Utility::AddToClass(pyclass, "__cpp_str", "__str__");
1729 }
1730#endif
1731
1732 if (Cppyy::IsAggregate(((CPPClass*)pyclass)->fCppType) && name.compare(0, 5, "std::", 5) != 0 &&
1733 name.compare(0, 6, "tuple<", 6) != 0) {
1734 // create a pseudo-constructor to allow initializer-style object creation
1735 Cppyy::TCppType_t kls = ((CPPClass*)pyclass)->fCppType;
1737 if (ndata) {
1738 std::string rname = name;
1740
1741 std::ostringstream initdef;
1742 initdef << "namespace __cppyy_internal {\n"
1743 << "void init_" << rname << "(" << name << "*& self";
1744 bool codegen_ok = true;
1745 std::vector<std::string> arg_types, arg_names, arg_defaults;
1746 arg_types.reserve(ndata); arg_names.reserve(ndata); arg_defaults.reserve(ndata);
1747 for (Cppyy::TCppIndex_t i = 0; i < ndata; ++i) {
1749 continue;
1750
1751 const std::string& txt = Cppyy::GetDatamemberType(kls, i);
1752 const std::string& res = Cppyy::IsEnum(txt) ? txt : Cppyy::ResolveName(txt);
1753 const std::string& cpd = TypeManip::compound(res);
1754 std::string res_clean = TypeManip::clean_type(res, false, true);
1755
1756 if (res_clean == "internal_enum_type_t")
1757 res_clean = txt; // restore (properly scoped name)
1758
1759 if (res.rfind(']') == std::string::npos && res.rfind(')') == std::string::npos) {
1760 if (!cpd.empty()) arg_types.push_back(res_clean+cpd);
1761 else arg_types.push_back("const "+res_clean+"&");
1762 arg_names.push_back(Cppyy::GetDatamemberName(kls, i));
1763 if ((!cpd.empty() && cpd.back() == '*') || Cppyy::IsBuiltin(res_clean))
1764 arg_defaults.push_back("0");
1765 else {
1768 }
1769 } else {
1770 codegen_ok = false; // TODO: how to support arrays, anonymous enums, etc?
1771 break;
1772 }
1773 }
1774
1775 if (codegen_ok && !arg_types.empty()) {
1776 bool defaults_ok = arg_defaults.size() == arg_types.size();
1777 for (std::vector<std::string>::size_type i = 0; i < arg_types.size(); ++i) {
1778 initdef << ", " << arg_types[i] << " " << arg_names[i];
1779 if (defaults_ok) initdef << " = " << arg_defaults[i];
1780 }
1781 initdef << ") {\n self = new " << name << "{";
1782 for (std::vector<std::string>::size_type i = 0; i < arg_names.size(); ++i) {
1783 if (i != 0) initdef << ", ";
1784 initdef << arg_names[i];
1785 }
1786 initdef << "};\n} }";
1787
1788 if (Cppyy::Compile(initdef.str(), true /* silent */)) {
1789 Cppyy::TCppScope_t cis = Cppyy::GetScope("__cppyy_internal");
1790 const auto& mix = Cppyy::GetMethodIndicesFromName(cis, "init_"+rname);
1791 if (mix.size()) {
1792 if (!Utility::AddToClass(pyclass, "__init__",
1793 new CPPFunction(cis, Cppyy::GetMethod(cis, mix[0]))))
1794 PyErr_Clear();
1795 }
1796 }
1797 }
1798 }
1799 }
1800
1801
1802//- class name based pythonization -------------------------------------------
1803
1804 if (IsTemplatedSTLClass(name, "vector")) {
1805
1806 // std::vector<bool> is a special case in C++
1808 if (klass->fCppType == sVectorBoolTypeID) {
1811 } else {
1812 // constructor that takes python collections
1813 Utility::AddToClass(pyclass, "__real_init", "__init__");
1815
1816 // data with size
1817 Utility::AddToClass(pyclass, "__real_data", "data");
1819
1820 // The addition of the __array__ utility to std::vector Python proxies causes a
1821 // bug where the resulting array is a single dimension, causing loss of data when
1822 // converting to numpy arrays, for >1dim vectors. Since this C++ pythonization
1823 // was added with the upgrade in 6.32, and is only defined and used recursively,
1824 // the safe option is to disable this function and no longer add it.
1825#if 0
1826 // numpy array conversion
1828#endif
1829
1830 // checked getitem
1832 Utility::AddToClass(pyclass, "_getitem__unchecked", "__getitem__");
1834 }
1835
1836 // vector-optimized iterator protocol
1838
1839 // optimized __iadd__
1841
1842 // helpers for iteration
1843 const std::string& vtype = Cppyy::ResolveName(name+"::value_type");
1844 if (vtype.rfind("value_type") == std::string::npos) { // actually resolved?
1848 }
1849
1850 size_t typesz = Cppyy::SizeOf(name+"::value_type");
1851 if (typesz) {
1855 }
1856 }
1857 }
1858
1859 else if (IsTemplatedSTLClass(name, "array")) {
1860 // constructor that takes python associative collections
1861 Utility::AddToClass(pyclass, "__real_init", "__init__");
1863 }
1864
1865 else if (IsTemplatedSTLClass(name, "map") || IsTemplatedSTLClass(name, "unordered_map")) {
1866 // constructor that takes python associative collections
1867 Utility::AddToClass(pyclass, "__real_init", "__init__");
1869
1871 }
1872
1873 else if (IsTemplatedSTLClass(name, "set")) {
1874 // constructor that takes python associative collections
1875 Utility::AddToClass(pyclass, "__real_init", "__init__");
1877
1879 }
1880
1881 else if (IsTemplatedSTLClass(name, "pair")) {
1884 }
1885
1886 if (IsTemplatedSTLClass(name, "shared_ptr") || IsTemplatedSTLClass(name, "unique_ptr")) {
1887 Utility::AddToClass(pyclass, "__real_init", "__init__");
1889 }
1890
1891 else if (!((PyTypeObject*)pyclass)->tp_iter && \
1892 (name.find("iterator") != std::string::npos || gIteratorTypes.find(name) != gIteratorTypes.end())) {
1893 ((PyTypeObject*)pyclass)->tp_iternext = (iternextfunc)STLIterNext;
1897 }
1898
1899 else if (name == "string" || name == "std::string") { // TODO: ask backend as well
1908 Utility::AddToClass(pyclass, "__cpp_find", "find");
1910 Utility::AddToClass(pyclass, "__cpp_rfind", "rfind");
1912 Utility::AddToClass(pyclass, "__cpp_replace", "replace");
1915
1916 // to allow use of std::string in dictionaries and findable with str
1918 }
1919
1920 else if (name == "basic_string_view<char>" || name == "std::basic_string_view<char>") {
1921 Utility::AddToClass(pyclass, "__real_init", "__init__");
1923 }
1924
1925 else if (name == "basic_string<wchar_t,char_traits<wchar_t>,allocator<wchar_t> >" || name == "std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> >") {
1932 }
1933
1934 else if (name == "complex<double>" || name == "std::complex<double>") {
1935 Utility::AddToClass(pyclass, "__cpp_real", "real");
1937 Utility::AddToClass(pyclass, "__cpp_imag", "imag");
1941 }
1942
1943 else if (IsTemplatedSTLClass(name, "complex")) {
1944 Utility::AddToClass(pyclass, "__cpp_real", "real");
1946 Utility::AddToClass(pyclass, "__cpp_imag", "imag");
1950 }
1951
1952// direct user access; there are two calls here:
1953// - explicit pythonization: won't fall through to the base classes and is preferred if present
1954// - normal pythonization: only called if explicit isn't present, falls through to base classes
1955 bool bUserOk = true; PyObject* res = nullptr;
1959 bUserOk = (bool)res;
1960 } else {
1962 if (func) {
1963 res = PyObject_CallFunctionObjArgs(func, pyclass, pyname, nullptr);
1964 Py_DECREF(func);
1965 bUserOk = (bool)res;
1966 } else
1967 PyErr_Clear();
1968 }
1969 if (!bUserOk) {
1971 return false;
1972 } else {
1973 Py_XDECREF(res);
1974 // pyname handed to args tuple below
1975 }
1976
1977// call registered pythonizors, if any: first run the namespace-specific pythonizors, then
1978// the global ones (the idea is to allow writing a pythonizor that see all classes)
1979 bool pstatus = true;
1981 auto &pyzMap = pythonizations();
1982 if (!outer_scope.empty()) {
1983 auto p = pyzMap.find(outer_scope);
1984 if (p != pyzMap.end()) {
1986 name.substr(outer_scope.size()+2, std::string::npos).c_str());
1989 }
1990 }
1991
1992 if (pstatus) {
1993 auto p = pyzMap.find("");
1994 if (p != pyzMap.end())
1995 pstatus = run_pythonizors(pyclass, pyname, p->second);
1996 }
1997
1999
2000// phew! all done ...
2001 return pstatus;
2002}
#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:385
#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:381
#define CPPYY__next__
Definition CPyCppyy.h:112
#define CPyCppyy_PyText_FromString
Definition CPyCppyy.h:81
#define CPyCppyy_PyText_Check
Definition CPyCppyy.h:74
_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
std::ios_base::fmtflags fFlags
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:110
const_iterator end() const
PyObject * gCTypesType
Definition PyStrings.cxx:39
PyObject * gRealInit
Definition PyStrings.cxx:42
PyObject * gExPythonize
Definition PyStrings.cxx:72
PyObject * gLifeLine
Definition PyStrings.cxx:29
PyObject * gGetItem
Definition PyStrings.cxx:23
PyObject * gCppBool
Definition PyStrings.cxx:11
PyObject * gCppReal
Definition PyStrings.cxx:64
PyObject * gPythonize
Definition PyStrings.cxx:73
PyObject * gTypeCode
Definition PyStrings.cxx:38
PyObject * gPostInc
Definition PyStrings.cxx:18
PyObject * gCppImag
Definition PyStrings.cxx:65
PyObject * gValueSize
Definition PyStrings.cxx:62
PyObject * gSetItem
Definition PyStrings.cxx:25
PyObject * gGetNoCheck
Definition PyStrings.cxx:24
PyObject * gCppRepr
Definition PyStrings.cxx:35
PyObject * gValueType
Definition PyStrings.cxx:61
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:813
bool AddToClass(PyObject *pyclass, const char *label, PyCFunction cfunc, int flags=METH_VARARGS)
Definition Utility.cxx:186
PyTypeObject VectorIter_Type
static PyObject * GetAttrDirect(PyObject *pyclass, PyObject *pyname)
bool Pythonize(PyObject *pyclass, const std::string &name)
bool CPPOverload_Check(T *object)
Definition CPPOverload.h:90
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
size_t TCppIndex_t
Definition cpp_cppyy.h:24
RPY_EXPORTED size_t SizeOf(TCppType_t klass)
intptr_t TCppMethod_t
Definition cpp_cppyy.h:22
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 std::string ResolveName(const std::string &cppitem_name)
TCppScope_t TCppType_t
Definition cpp_cppyy.h:19
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:18
RPY_EXPORTED std::string GetMethodResultType(TCppMethod_t)
RPY_EXPORTED std::string GetDatamemberName(TCppScope_t scope, TCppIndex_t idata)