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//-----------------------------------------------------------------------------
38bool HasAttrDirect(PyObject* pyclass, PyObject* pyname, bool mustBeCPyCppyy = false) {
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
41 PyObject* dct = PyObject_GetAttr(pyclass, PyStrings::gDict);
42 if (dct) {
43 PyObject* attr = PyObject_GetItem(dct, pyname);
44 Py_DECREF(dct);
45 if (attr) {
46 bool ret = !mustBeCPyCppyy || CPPOverload_Check(attr);
47 Py_DECREF(attr);
48 return ret;
49 }
50 }
51 PyErr_Clear();
52 return false;
53}
54
55PyObject* GetAttrDirect(PyObject* pyclass, PyObject* pyname) {
56// get an attribute without causing getattr lookups
57 PyObject* dct = PyObject_GetAttr(pyclass, PyStrings::gDict);
58 if (dct) {
59 PyObject* attr = PyObject_GetItem(dct, pyname);
60 Py_DECREF(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);
88 PyObject* result = PyObject_CallMethod(
89 obj, const_cast<char*>(meth), const_cast<char*>("O"), arg1);
90 Py_DECREF(obj);
91 return result;
92}
93
94//-----------------------------------------------------------------------------
95PyObject* PyStyleIndex(PyObject* self, PyObject* index)
96{
97// Helper; converts python index into straight C index.
99 if (idx == (Py_ssize_t)-1 && PyErr_Occurred())
100 return nullptr;
101
102 Py_ssize_t size = PySequence_Size(self);
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) {
110 Py_INCREF(index);
111 pyindex = index;
112 } else
113 pyindex = PyLong_FromSsize_t(size+idx);
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) 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//-----------------------------------------------------------------------------
134inline PyObject* CallSelfIndex(CPPInstance* self, PyObject* idx, PyObject* pymeth)
135{
136// Helper; call method with signature: meth(pyindex).
137 Py_INCREF((PyObject*)self);
138 PyObject* pyindex = PyStyleIndex((PyObject*)self, idx);
139 if (!pyindex) {
140 Py_DECREF((PyObject*)self);
141 return nullptr;
142 }
143
144 PyObject* result = PyObject_CallMethodOneArg((PyObject*)self, pymeth, pyindex);
145 Py_DECREF(pyindex);
146 Py_DECREF((PyObject*)self);
147 return result;
148}
149
150//- "smart pointer" behavior ---------------------------------------------------
151PyObject* DeRefGetAttr(PyObject* self, PyObject* name)
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.
159 PyErr_SetString(PyExc_AttributeError, CPyCppyy_PyText_AsString(name));
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)) {
172 PyObject* val1 = PyObject_Str(self);
173 PyObject* val2 = PyObject_Str(name);
174 PyErr_Format(PyExc_AttributeError, "%s has no attribute \'%s\'",
176 Py_DECREF(val2);
177 Py_DECREF(val1);
178
179 Py_DECREF(pyptr);
180 return nullptr;
181 }
182
183 PyObject* result = PyObject_GetAttr(pyptr, name);
184 Py_DECREF(pyptr);
185 return result;
186}
187
188//-----------------------------------------------------------------------------
189PyObject* FollowGetAttr(PyObject* self, PyObject* name)
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
200 PyObject* result = PyObject_GetAttr(pyptr, name);
201 Py_DECREF(pyptr);
202 return result;
203}
204
205//- pointer checking bool converter -------------------------------------------
206PyObject* NullCheckBool(PyObject* self)
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++);
244 Py_INCREF(item);
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++);
258 Py_INCREF(item);
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)
298 if (PyObject_CheckBuffer(fi))
299 return nullptr;
300
301 if (PyTuple_CheckExact(fi))
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 {
308 PyObject* iter = PyObject_GetIter(fi);
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
337 PyObject* fi = PySequence_GetItem(PyTuple_GET_ITEM(args, 0), 0);
338 if (!fi) PyErr_Clear();
339 if (fi && (PyTuple_CheckExact(fi) || PyList_CheckExact(fi))) {
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;
344 if (vtype && CPyCppyy_PyText_Check(vtype)) {
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();
350 Py_XDECREF(vtype);
351
352 if (eb_call) {
353 PyObject* eb_args;
354 for (int i = 0; /* until break */; ++i) {
355 PyObject* item = getter->get();
356 if (item) {
357 if (value_is_vector && PySequence_Check(item)) {
358 eb_args = PyTuple_New(1);
359 PyTuple_SET_ITEM(eb_args, 0, item);
360 } else if (PyTuple_CheckExact(item)) {
361 eb_args = item;
362 } else if (PyList_CheckExact(item)) {
363 Py_ssize_t isz = PyList_GET_SIZE(item);
364 eb_args = PyTuple_New(isz);
365 for (Py_ssize_t j = 0; j < isz; ++j) {
366 PyObject* iarg = PyList_GET_ITEM(item, j);
367 Py_INCREF(iarg);
368 PyTuple_SET_ITEM(eb_args, j, iarg);
369 }
370 Py_DECREF(item);
371 } else {
372 Py_DECREF(item);
373 PyErr_Format(PyExc_TypeError, "argument %d is not a tuple or list", i);
374 fill_ok = false;
375 break;
376 }
377 PyObject* ebres = PyObject_CallObject(eb_call, eb_args);
378 Py_DECREF(eb_args);
379 if (!ebres) {
380 fill_ok = false;
381 break;
382 }
383 Py_DECREF(ebres);
384 } else {
385 if (PyErr_Occurred()) {
386 if (!(PyErr_ExceptionMatches(PyExc_IndexError) ||
387 PyErr_ExceptionMatches(PyExc_StopIteration)))
388 fill_ok = false;
389 else { PyErr_Clear(); }
390 }
391 break;
392 }
393 }
394 Py_DECREF(eb_call);
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) {
403 PyObject* pbres = PyObject_CallFunctionObjArgs(pb_call, item, nullptr);
404 Py_DECREF(item);
405 if (!pbres) {
406 fill_ok = false;
407 break;
408 }
409 Py_DECREF(pbres);
410 } else {
411 if (PyErr_Occurred()) {
412 if (!(PyErr_ExceptionMatches(PyExc_IndexError) ||
413 PyErr_ExceptionMatches(PyExc_StopIteration)))
414 fill_ok = false;
415 else { PyErr_Clear(); }
416 }
417 break;
418 }
419 }
420 Py_DECREF(pb_call);
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
440 Py_INCREF(self);
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);
448 if (PyObject_CheckBuffer(fi) && !(CPyCppyy_PyText_Check(fi) || PyBytes_Check(fi))) {
450 if (vend) {
451 PyObject* result = PyObject_CallMethodObjArgs(self, PyStrings::gInsert, vend, fi, nullptr);
452 Py_DECREF(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) {
486 Py_DECREF(result);
487 return nullptr;
488 }
489
490 return result;
491 }
492
493// The given argument wasn't iterable: simply forward to regular constructor
494 PyObject* realInit = PyObject_GetAttr(self, PyStrings::gRealInit);
495 if (realInit) {
496 PyObject* result = PyObject_Call(realInit, args, nullptr);
497 Py_DECREF(realInit);
498 return result;
499 }
500
501 return nullptr;
502}
503
504//---------------------------------------------------------------------------
505PyObject* VectorData(PyObject* self, PyObject*)
506{
507 PyObject* pydata = CallPyObjMethod(self, "__real_data");
508 if (!LowLevelView_Check(pydata) && !CPPInstance_Check(pydata))
509 return pydata;
510
512 if (!pylen) {
513 PyErr_Clear();
514 return pydata;
515 }
516
517 long clen = PyInt_AsLong(pylen);
518 Py_DECREF(pylen);
519
520 if (CPPInstance_Check(pydata)) {
521 ((CPPInstance*)pydata)->CastToArray(clen);
522 return pydata;
523 }
524
525 ((LowLevelView*)pydata)->resize((size_t)clen);
526 return pydata;
527}
528
529
530//---------------------------------------------------------------------------
531PyObject* VectorArray(PyObject* self, PyObject* args, PyObject* kwargs)
532{
533 PyObject* pydata = VectorData(self, nullptr);
534 PyObject* arrcall = PyObject_GetAttr(pydata, PyStrings::gArray);
535 PyObject* newarr = PyObject_Call(arrcall, args, kwargs);
536 Py_DECREF(arrcall);
537 Py_DECREF(pydata);
538 return newarr;
539}
540
541
542//-----------------------------------------------------------------------------
543static PyObject* vector_iter(PyObject* v) {
544 vectoriterobject* vi = PyObject_GC_New(vectoriterobject, &VectorIter_Type);
545 if (!vi) return nullptr;
546
547 Py_INCREF(v);
548 vi->ii_container = v;
549
550// tell the iterator code to set a life line if this container is a temporary
552 if (v->ob_refcnt <= 2 || (((CPPInstance*)v)->fFlags & CPPInstance::kIsValue))
554
555 PyObject* pyvalue_type = PyObject_GetAttr((PyObject*)Py_TYPE(v), PyStrings::gValueType);
556 if (pyvalue_type) {
558 if (pyvalue_size) {
559 vi->vi_stride = PyLong_AsLong(pyvalue_size);
560 Py_DECREF(pyvalue_size);
561 } else {
562 PyErr_Clear();
563 vi->vi_stride = 0;
564 }
565
566 if (CPyCppyy_PyText_Check(pyvalue_type)) {
567 std::string value_type = CPyCppyy_PyText_AsString(pyvalue_type);
568 value_type = Cppyy::ResolveName(value_type);
569 vi->vi_klass = Cppyy::GetScope(value_type);
570 if (!vi->vi_klass) {
571 // look for a special case of pointer to a class type (which is a builtin, but it
572 // is more useful to treat it polymorphically by allowing auto-downcasts)
573 const std::string& clean_type = TypeManip::clean_type(value_type, false, false);
575 if (c && TypeManip::compound(value_type) == "*") {
576 vi->vi_klass = c;
578 }
579 }
580 if (vi->vi_klass) {
581 vi->vi_converter = nullptr;
582 if (!vi->vi_flags) {
583 if (value_type.back() != '*') // meaning, object stored by-value
585 }
586 } else
587 vi->vi_converter = CPyCppyy::CreateConverter(value_type);
588 if (!vi->vi_stride) vi->vi_stride = Cppyy::SizeOf(value_type);
589
590 } else if (CPPScope_Check(pyvalue_type)) {
591 vi->vi_klass = ((CPPClass*)pyvalue_type)->fCppType;
592 vi->vi_converter = nullptr;
593 if (!vi->vi_stride) vi->vi_stride = Cppyy::SizeOf(vi->vi_klass);
595 }
596
597 PyObject* pydata = CallPyObjMethod(v, "__real_data");
598 if (!pydata || Utility::GetBuffer(pydata, '*', 1, vi->vi_data, false) == 0)
599 vi->vi_data = CPPInstance_Check(pydata) ? ((CPPInstance*)pydata)->GetObjectRaw() : nullptr;
600 Py_XDECREF(pydata);
601
602 } else {
603 PyErr_Clear();
604 vi->vi_data = nullptr;
605 vi->vi_stride = 0;
606 vi->vi_converter = nullptr;
607 vi->vi_klass = 0;
608 vi->vi_flags = 0;
609 }
610
611 Py_XDECREF(pyvalue_type);
612
613 vi->ii_pos = 0;
614 vi->ii_len = PySequence_Size(v);
615
616 PyObject_GC_Track(vi);
617 return (PyObject*)vi;
618}
619
620PyObject* VectorGetItem(CPPInstance* self, PySliceObject* index)
621{
622// Implement python's __getitem__ for std::vector<>s.
623 if (PySlice_Check(index)) {
624 if (!self->GetObject()) {
625 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
626 return nullptr;
627 }
628
629 PyObject* pyclass = (PyObject*)Py_TYPE((PyObject*)self);
630 PyObject* nseq = PyObject_CallObject(pyclass, nullptr);
631
632 Py_ssize_t start, stop, step;
633 PySlice_GetIndices((CPyCppyy_PySliceCast)index, PyObject_Length((PyObject*)self), &start, &stop, &step);
634
635 const Py_ssize_t nlen = PySequence_Size((PyObject*)self);
636 if (!AdjustSlice(nlen, start, stop, step))
637 return nseq;
638
639 const Py_ssize_t sign = step < 0 ? -1 : 1;
640 for (Py_ssize_t i = start; i*sign < stop*sign; i += step) {
641 PyObject* pyidx = PyInt_FromSsize_t(i);
643 CallPyObjMethod(nseq, "push_back", item);
644 Py_DECREF(item);
645 Py_DECREF(pyidx);
646 }
647
648 return nseq;
649 }
650
651 return CallSelfIndex(self, (PyObject*)index, PyStrings::gGetNoCheck);
652}
653
654
655static Cppyy::TCppType_t sVectorBoolTypeID = (Cppyy::TCppType_t)0;
656
657PyObject* VectorBoolGetItem(CPPInstance* self, PyObject* idx)
658{
659// std::vector<bool> is a special-case in C++, and its return type depends on
660// the compiler: treat it special here as well
661 if (!CPPInstance_Check(self) || self->ObjectIsA() != sVectorBoolTypeID) {
662 PyErr_Format(PyExc_TypeError,
663 "require object of type std::vector<bool>, but %s given",
664 Cppyy::GetScopedFinalName(self->ObjectIsA()).c_str());
665 return nullptr;
666 }
667
668 if (!self->GetObject()) {
669 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
670 return nullptr;
671 }
672
673 if (PySlice_Check(idx)) {
674 PyObject* pyclass = (PyObject*)Py_TYPE((PyObject*)self);
675 PyObject* nseq = PyObject_CallObject(pyclass, nullptr);
676
677 Py_ssize_t start, stop, step;
678 PySlice_GetIndices((CPyCppyy_PySliceCast)idx, PyObject_Length((PyObject*)self), &start, &stop, &step);
679 const Py_ssize_t nlen = PySequence_Size((PyObject*)self);
680 if (!AdjustSlice(nlen, start, stop, step))
681 return nseq;
682
683 const Py_ssize_t sign = step < 0 ? -1 : 1;
684 for (Py_ssize_t i = start; i*sign < stop*sign; i += step) {
685 PyObject* pyidx = PyInt_FromSsize_t(i);
687 CallPyObjMethod(nseq, "push_back", item);
688 Py_DECREF(item);
689 Py_DECREF(pyidx);
690 }
691
692 return nseq;
693 }
694
695 PyObject* pyindex = PyStyleIndex((PyObject*)self, idx);
696 if (!pyindex)
697 return nullptr;
698
699 int index = (int)PyLong_AsLong(pyindex);
700 Py_DECREF(pyindex);
701
702// get hold of the actual std::vector<bool> (no cast, as vector is never a base)
703 std::vector<bool>* vb = (std::vector<bool>*)self->GetObject();
704
705// finally, return the value
706 if (bool((*vb)[index]))
709}
710
711PyObject* VectorBoolSetItem(CPPInstance* self, PyObject* args)
712{
713// std::vector<bool> is a special-case in C++, and its return type depends on
714// the compiler: treat it special here as well
715 if (!CPPInstance_Check(self) || self->ObjectIsA() != sVectorBoolTypeID) {
716 PyErr_Format(PyExc_TypeError,
717 "require object of type std::vector<bool>, but %s given",
718 Cppyy::GetScopedFinalName(self->ObjectIsA()).c_str());
719 return nullptr;
720 }
721
722 if (!self->GetObject()) {
723 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
724 return nullptr;
725 }
726
727 int bval = 0; PyObject* idx = nullptr;
728 if (!PyArg_ParseTuple(args, const_cast<char*>("Oi:__setitem__"), &idx, &bval))
729 return nullptr;
730
731 PyObject* pyindex = PyStyleIndex((PyObject*)self, idx);
732 if (!pyindex)
733 return nullptr;
734
735 int index = (int)PyLong_AsLong(pyindex);
736 Py_DECREF(pyindex);
737
738// get hold of the actual std::vector<bool> (no cast, as vector is never a base)
739 std::vector<bool>* vb = (std::vector<bool>*)self->GetObject();
740
741// finally, set the value
742 (*vb)[index] = (bool)bval;
743
745}
746
747
748//- array behavior as primitives ----------------------------------------------
749PyObject* ArrayInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
750{
751// std::array is normally only constructed using aggregate initialization, which
752// is a concept that does not exist in python, so use this custom constructor to
753// to fill the array using setitem
754
755 if (args && PyTuple_GET_SIZE(args) == 1 && PySequence_Check(PyTuple_GET_ITEM(args, 0))) {
756 // construct the empty array, then fill it
758 if (!result)
759 return nullptr;
760
761 PyObject* items = PyTuple_GET_ITEM(args, 0);
762 Py_ssize_t fillsz = PySequence_Size(items);
763 if (PySequence_Size(self) != fillsz) {
764 PyErr_Format(PyExc_ValueError, "received sequence of size %zd where %zd expected",
765 fillsz, PySequence_Size(self));
766 Py_DECREF(result);
767 return nullptr;
768 }
769
770 PyObject* si_call = PyObject_GetAttr(self, PyStrings::gSetItem);
771 for (Py_ssize_t i = 0; i < fillsz; ++i) {
772 PyObject* item = PySequence_GetItem(items, i);
774 PyObject* sires = PyObject_CallFunctionObjArgs(si_call, index, item, nullptr);
775 Py_DECREF(index);
776 Py_DECREF(item);
777 if (!sires) {
778 Py_DECREF(si_call);
779 Py_DECREF(result);
780 return nullptr;
781 } else
782 Py_DECREF(sires);
783 }
784 Py_DECREF(si_call);
785
786 return result;
787 } else
788 PyErr_Clear();
789
790// The given argument wasn't iterable: simply forward to regular constructor
791 PyObject* realInit = PyObject_GetAttr(self, PyStrings::gRealInit);
792 if (realInit) {
793 PyObject* result = PyObject_Call(realInit, args, nullptr);
794 Py_DECREF(realInit);
795 return result;
796 }
797
798 return nullptr;
799}
800
801
802//- map behavior as primitives ------------------------------------------------
803static PyObject* MapFromPairs(PyObject* self, PyObject* pairs)
804{
805// construct an empty map, then fill it with the key, value pairs
807 if (!result)
808 return nullptr;
809
810 PyObject* si_call = PyObject_GetAttr(self, PyStrings::gSetItem);
811 for (Py_ssize_t i = 0; i < PySequence_Size(pairs); ++i) {
812 PyObject* pair = PySequence_GetItem(pairs, i);
813 PyObject* sires = nullptr;
814 if (pair && PySequence_Check(pair) && PySequence_Size(pair) == 2) {
815 PyObject* key = PySequence_GetItem(pair, 0);
816 PyObject* value = PySequence_GetItem(pair, 1);
817 sires = PyObject_CallFunctionObjArgs(si_call, key, value, nullptr);
818 Py_DECREF(value);
819 Py_DECREF(key);
820 }
821 Py_DECREF(pair);
822 if (!sires) {
823 Py_DECREF(si_call);
824 Py_DECREF(result);
825 if (!PyErr_Occurred())
826 PyErr_SetString(PyExc_TypeError, "Failed to fill map (argument not a dict or sequence of pairs)");
827 return nullptr;
828 } else
829 Py_DECREF(sires);
830 }
831 Py_DECREF(si_call);
832
833 return result;
834}
835
836PyObject* MapInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
837{
838// Specialized map constructor to allow construction from mapping containers and
839// from tuples of pairs ("initializer_list style").
840
841// PyMapping_Check is not very discriminatory, as it basically only checks for the
842// existence of __getitem__, hence the most common cases of tuple and list are
843// dropped straight-of-the-bat (the PyMapping_Items call will fail on them).
844 if (PyTuple_GET_SIZE(args) == 1 && PyMapping_Check(PyTuple_GET_ITEM(args, 0)) && \
845 !(PyTuple_Check(PyTuple_GET_ITEM(args, 0)) || PyList_Check(PyTuple_GET_ITEM(args, 0)))) {
846 PyObject* assoc = PyTuple_GET_ITEM(args, 0);
847#if PY_VERSION_HEX < 0x03000000
848 // to prevent warning about literal string, expand macro
849 PyObject* items = PyObject_CallMethod(assoc, (char*)"items", nullptr);
850#else
851 // in p3, PyMapping_Items isn't a macro, but a function that short-circuits dict
852 PyObject* items = PyMapping_Items(assoc);
853#endif
854 if (items && PySequence_Check(items)) {
855 PyObject* result = MapFromPairs(self, items);
856 Py_DECREF(items);
857 return result;
858 }
859
860 Py_XDECREF(items);
861 PyErr_Clear();
862
863 // okay to fall through as long as 'self' has not been created (is done in MapFromPairs)
864 }
865
866// tuple of pairs case (some mapping types are sequences)
867 if (PyTuple_GET_SIZE(args) == 1 && PySequence_Check(PyTuple_GET_ITEM(args, 0)))
868 return MapFromPairs(self, PyTuple_GET_ITEM(args, 0));
869
870// The given argument wasn't a mapping or tuple of pairs: forward to regular constructor
871 PyObject* realInit = PyObject_GetAttr(self, PyStrings::gRealInit);
872 if (realInit) {
873 PyObject* result = PyObject_Call(realInit, args, nullptr);
874 Py_DECREF(realInit);
875 return result;
876 }
877
878 return nullptr;
879}
880
881PyObject* STLContainsWithFind(PyObject* self, PyObject* obj)
882{
883// Implement python's __contains__ for std::map/std::set
884 PyObject* result = nullptr;
885
886 PyObject* iter = CallPyObjMethod(self, "find", obj);
887 if (CPPInstance_Check(iter)) {
889 if (CPPInstance_Check(end)) {
890 if (!PyObject_RichCompareBool(iter, end, Py_EQ)) {
891 Py_INCREF(Py_True);
892 result = Py_True;
893 }
894 }
895 Py_XDECREF(end);
896 }
897 Py_XDECREF(iter);
898
899 if (!result) {
900 PyErr_Clear(); // e.g. wrong argument type, which should always lead to False
901 Py_INCREF(Py_False);
902 result = Py_False;
903 }
904
905 return result;
906}
907
908
909//- set behavior as primitives ------------------------------------------------
910PyObject* SetInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
911{
912// Specialized set constructor to allow construction from Python sets.
913 if (PyTuple_GET_SIZE(args) == 1 && PySet_Check(PyTuple_GET_ITEM(args, 0))) {
914 PyObject* pyset = PyTuple_GET_ITEM(args, 0);
915
916 // construct an empty set, then fill it
918 if (!result)
919 return nullptr;
920
921 PyObject* iter = PyObject_GetIter(pyset);
922 if (iter) {
923 PyObject* ins_call = PyObject_GetAttrString(self, (char*)"insert");
924
925 IterItemGetter getter{iter};
926 Py_DECREF(iter);
927
928 PyObject* item = getter.get();
929 while (item) {
930 PyObject* insres = PyObject_CallFunctionObjArgs(ins_call, item, nullptr);
931 Py_DECREF(item);
932 if (!insres) {
933 Py_DECREF(ins_call);
934 Py_DECREF(result);
935 return nullptr;
936 } else
937 Py_DECREF(insres);
938 item = getter.get();
939 }
940 Py_DECREF(ins_call);
941 }
942
943 return result;
944 }
945
946// The given argument wasn't iterable: simply forward to regular constructor
947 PyObject* realInit = PyObject_GetAttr(self, PyStrings::gRealInit);
948 if (realInit) {
949 PyObject* result = PyObject_Call(realInit, args, nullptr);
950 Py_DECREF(realInit);
951 return result;
952 }
953
954 return nullptr;
955}
956
957
958//- STL container iterator support --------------------------------------------
959static const ptrdiff_t PS_END_ADDR = 7; // non-aligned address, so no clash
960static const ptrdiff_t PS_FLAG_ADDR = 11; // id.
961static const ptrdiff_t PS_COLL_ADDR = 13; // id.
962
963PyObject* LLSequenceIter(PyObject* self)
964{
965// Implement python's __iter__ for low level views used through STL-type begin()/end()
967
968 if (LowLevelView_Check(iter)) {
969 // builtin pointer iteration: can only succeed if a size is available
970 Py_ssize_t sz = PySequence_Size(self);
971 if (sz == -1) {
972 Py_DECREF(iter);
973 return nullptr;
974 }
975 PyObject* lliter = Py_TYPE(iter)->tp_iter(iter);
976 ((indexiterobject*)lliter)->ii_len = sz;
977 Py_DECREF(iter);
978 return lliter;
979 }
980
981 if (iter) {
982 Py_DECREF(iter);
983 PyErr_SetString(PyExc_TypeError, "unrecognized iterator type for low level views");
984 }
985
986 return nullptr;
987}
988
989PyObject* STLSequenceIter(PyObject* self)
990{
991// Implement python's __iter__ for std::iterator<>s
993 if (iter) {
995 if (end) {
996 if (CPPInstance_Check(iter)) {
997 // use the data member cache to store extra state on the iterator object,
998 // without it being visible on the Python side
999 auto& dmc = ((CPPInstance*)iter)->GetDatamemberCache();
1000 dmc.push_back(std::make_pair(PS_END_ADDR, end));
1001
1002 // set a flag, indicating first iteration (reset in __next__)
1003 Py_INCREF(Py_False);
1004 dmc.push_back(std::make_pair(PS_FLAG_ADDR, Py_False));
1005
1006 // make sure the iterated over collection remains alive for the duration
1007 Py_INCREF(self);
1008 dmc.push_back(std::make_pair(PS_COLL_ADDR, self));
1009 } else {
1010 // could store "end" on the object's dictionary anyway, but if end() returns
1011 // a user-customized object, then its __next__ is probably custom, too
1012 Py_DECREF(end);
1013 }
1014 }
1015 }
1016 return iter;
1017}
1018
1019//- generic iterator support over a sequence with operator[] and size ---------
1020//-----------------------------------------------------------------------------
1021static PyObject* index_iter(PyObject* c) {
1022 indexiterobject* ii = PyObject_GC_New(indexiterobject, &IndexIter_Type);
1023 if (!ii) return nullptr;
1024
1025 Py_INCREF(c);
1026 ii->ii_container = c;
1027 ii->ii_pos = 0;
1028 ii->ii_len = PySequence_Size(c);
1029
1030 PyObject_GC_Track(ii);
1031 return (PyObject*)ii;
1032}
1033
1034
1035//- safe indexing for STL-like vector w/o iterator dictionaries ---------------
1036/* replaced by indexiterobject iteration, but may still have some future use ...
1037PyObject* CheckedGetItem(PyObject* self, PyObject* obj)
1038{
1039// Implement a generic python __getitem__ for STL-like classes that are missing the
1040// reflection info for their iterators. This is then used for iteration by means of
1041// consecutive indices, it such index is of integer type.
1042 Py_ssize_t size = PySequence_Size(self);
1043 Py_ssize_t idx = PyInt_AsSsize_t(obj);
1044 if ((size == (Py_ssize_t)-1 || idx == (Py_ssize_t)-1) && PyErr_Occurred()) {
1045 // argument conversion problem: let method itself resolve anew and report
1046 PyErr_Clear();
1047 return PyObject_CallMethodOneArg(self, PyStrings::gGetNoCheck, obj);
1048 }
1049
1050 bool inbounds = false;
1051 if (idx < 0) idx += size;
1052 if (0 <= idx && 0 <= size && idx < size)
1053 inbounds = true;
1054
1055 if (inbounds)
1056 return PyObject_CallMethodOneArg(self, PyStrings::gGetNoCheck, obj);
1057 else
1058 PyErr_SetString( PyExc_IndexError, "index out of range" );
1059
1060 return nullptr;
1061}*/
1062
1063
1064//- pair as sequence to allow tuple unpacking --------------------------------
1065PyObject* PairUnpack(PyObject* self, PyObject* pyindex)
1066{
1067// For std::map<> iteration, unpack std::pair<>s into tuples for the loop.
1068 long idx = PyLong_AsLong(pyindex);
1069 if (idx == -1 && PyErr_Occurred())
1070 return nullptr;
1071
1072 if (!CPPInstance_Check(self) || !((CPPInstance*)self)->GetObject()) {
1073 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
1074 return nullptr;
1075 }
1076
1077 if ((int)idx == 0)
1078 return PyObject_GetAttr(self, PyStrings::gFirst);
1079 else if ((int)idx == 1)
1080 return PyObject_GetAttr(self, PyStrings::gSecond);
1081
1082// still here? Trigger stop iteration
1083 PyErr_SetString(PyExc_IndexError, "out of bounds");
1084 return nullptr;
1085}
1086
1087//- simplistic len() functions -----------------------------------------------
1088PyObject* ReturnTwo(CPPInstance*, PyObject*) {
1089 return PyInt_FromLong(2);
1090}
1091
1092
1093//- shared/unique_ptr behavior -----------------------------------------------
1094PyObject* SmartPtrInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
1095{
1096// since the shared/unique pointer will take ownership, we need to relinquish it
1097 PyObject* realInit = PyObject_GetAttr(self, PyStrings::gRealInit);
1098 if (realInit) {
1099 PyObject* result = PyObject_Call(realInit, args, nullptr);
1100 Py_DECREF(realInit);
1101 if (result && PyTuple_GET_SIZE(args) == 1 && CPPInstance_Check(PyTuple_GET_ITEM(args, 0))) {
1102 CPPInstance* cppinst = (CPPInstance*)PyTuple_GET_ITEM(args, 0);
1103 if (!(cppinst->fFlags & CPPInstance::kIsSmartPtr)) cppinst->CppOwns();
1104 }
1105 return result;
1106 }
1107 return nullptr;
1108}
1109
1110
1111//- string behavior as primitives --------------------------------------------
1112#if PY_VERSION_HEX >= 0x03000000
1113// TODO: this is wrong, b/c it doesn't order
1114static int PyObject_Compare(PyObject* one, PyObject* other) {
1115 return !PyObject_RichCompareBool(one, other, Py_EQ);
1116}
1117#endif
1118static inline
1119PyObject* CPyCppyy_PyString_FromCppString(std::string* s, bool native=true) {
1120 if (native)
1121 return PyBytes_FromStringAndSize(s->data(), s->size());
1122 return CPyCppyy_PyText_FromStringAndSize(s->data(), s->size());
1123}
1124
1125static inline
1126PyObject* CPyCppyy_PyString_FromCppString(std::wstring* s, bool native=true) {
1127 PyObject* pyobj = PyUnicode_FromWideChar(s->data(), s->size());
1128 if (pyobj && native) {
1129 PyObject* pybytes = PyUnicode_AsEncodedString(pyobj, "UTF-8", "strict");
1130 Py_DECREF(pyobj);
1131 pyobj = pybytes;
1132 }
1133 return pyobj;
1134}
1135
1136#define CPPYY_IMPL_STRING_PYTHONIZATION(type, name) \
1137static inline \
1138PyObject* name##StringGetData(PyObject* self, bool native=true) \
1139{ \
1140 if (CPyCppyy::CPPInstance_Check(self)) { \
1141 type* obj = ((type*)((CPPInstance*)self)->GetObject()); \
1142 if (obj) return CPyCppyy_PyString_FromCppString(obj, native); \
1143 } \
1144 PyErr_Format(PyExc_TypeError, "object mismatch (%s expected)", #type); \
1145 return nullptr; \
1146} \
1147 \
1148PyObject* name##StringStr(PyObject* self) \
1149{ \
1150 PyObject* pyobj = name##StringGetData(self, false); \
1151 if (!pyobj) { \
1152 /* do a native conversion to make printing possible (debatable) */ \
1153 PyErr_Clear(); \
1154 PyObject* pybytes = name##StringGetData(self, true); \
1155 if (pybytes) { /* should not fail */ \
1156 pyobj = PyObject_Str(pybytes); \
1157 Py_DECREF(pybytes); \
1158 } \
1159 } \
1160 return pyobj; \
1161} \
1162 \
1163PyObject* name##StringBytes(PyObject* self) \
1164{ \
1165 return name##StringGetData(self, true); \
1166} \
1167 \
1168PyObject* name##StringRepr(PyObject* self) \
1169{ \
1170 PyObject* data = name##StringGetData(self, true); \
1171 if (data) { \
1172 PyObject* repr = PyObject_Repr(data); \
1173 Py_DECREF(data); \
1174 return repr; \
1175 } \
1176 return nullptr; \
1177} \
1178 \
1179PyObject* name##StringIsEqual(PyObject* self, PyObject* obj) \
1180{ \
1181 PyObject* data = name##StringGetData(self, PyBytes_Check(obj)); \
1182 if (data) { \
1183 PyObject* result = PyObject_RichCompare(data, obj, Py_EQ); \
1184 Py_DECREF(data); \
1185 return result; \
1186 } \
1187 return nullptr; \
1188} \
1189 \
1190PyObject* name##StringIsNotEqual(PyObject* self, PyObject* obj) \
1191{ \
1192 PyObject* data = name##StringGetData(self, PyBytes_Check(obj)); \
1193 if (data) { \
1194 PyObject* result = PyObject_RichCompare(data, obj, Py_NE); \
1195 Py_DECREF(data); \
1196 return result; \
1197 } \
1198 return nullptr; \
1199}
1200
1201// Only define STLStringCompare:
1202#define CPPYY_IMPL_STRING_PYTHONIZATION_CMP(type, name) \
1203CPPYY_IMPL_STRING_PYTHONIZATION(type, name) \
1204PyObject* name##StringCompare(PyObject* self, PyObject* obj) \
1205{ \
1206 PyObject* data = name##StringGetData(self, PyBytes_Check(obj)); \
1207 int result = 0; \
1208 if (data) { \
1209 result = PyObject_Compare(data, obj); \
1210 Py_DECREF(data); \
1211 } \
1212 if (PyErr_Occurred()) \
1213 return nullptr; \
1214 return PyInt_FromLong(result); \
1215}
1216
1218CPPYY_IMPL_STRING_PYTHONIZATION_CMP(std::wstring, STLW)
1219
1220static inline std::string* GetSTLString(CPPInstance* self) {
1221 if (!CPPInstance_Check(self)) {
1222 PyErr_SetString(PyExc_TypeError, "std::string object expected");
1223 return nullptr;
1224 }
1225
1226 std::string* obj = (std::string*)self->GetObject();
1227 if (!obj)
1228 PyErr_SetString(PyExc_ReferenceError, "attempt to access a null-pointer");
1229
1230 return obj;
1231}
1232
1233PyObject* STLStringDecode(CPPInstance* self, PyObject* args, PyObject* kwds)
1234{
1235 std::string* obj = GetSTLString(self);
1236 if (!obj)
1237 return nullptr;
1238
1239 char* keywords[] = {(char*)"encoding", (char*)"errors", (char*)nullptr};
1240 const char* encoding; const char* errors;
1241 if (!PyArg_ParseTupleAndKeywords(args, kwds,
1242 const_cast<char*>("s|s"), keywords, &encoding, &errors))
1243 return nullptr;
1244
1245 return PyUnicode_Decode(obj->data(), obj->size(), encoding, errors);
1246}
1247
1248PyObject* STLStringContains(CPPInstance* self, PyObject* pyobj)
1249{
1250 std::string* obj = GetSTLString(self);
1251 if (!obj)
1252 return nullptr;
1253
1254 const char* needle = CPyCppyy_PyText_AsString(pyobj);
1255 if (!needle)
1256 return nullptr;
1257
1258 if (obj->find(needle) != std::string::npos) {
1260 }
1261
1263}
1264
1265PyObject* STLStringReplace(CPPInstance* self, PyObject* args, PyObject* /*kwds*/)
1266{
1267 std::string* obj = GetSTLString(self);
1268 if (!obj)
1269 return nullptr;
1270
1271// both str and std::string have a method "replace", but the Python version only
1272// accepts strings and takes no keyword arguments, whereas the C++ version has no
1273// overload that takes a string
1274
1275 if (2 <= PyTuple_GET_SIZE(args) && CPyCppyy_PyText_Check(PyTuple_GET_ITEM(args, 0))) {
1276 PyObject* pystr = CPyCppyy_PyText_FromStringAndSize(obj->data(), obj->size());
1277 PyObject* meth = PyObject_GetAttrString(pystr, (char*)"replace");
1278 Py_DECREF(pystr);
1279 PyObject* result = PyObject_CallObject(meth, args);
1280 Py_DECREF(meth);
1281 return result;
1282 }
1283
1284 PyObject* cppreplace = PyObject_GetAttrString((PyObject*)self, (char*)"__cpp_replace");
1285 if (cppreplace) {
1286 PyObject* result = PyObject_Call(cppreplace, args, nullptr);
1287 Py_DECREF(cppreplace);
1288 return result;
1289 }
1290
1291 PyErr_SetString(PyExc_AttributeError, "\'std::string\' object has no attribute \'replace\'");
1292 return nullptr;
1293}
1294
1295#define CPYCPPYY_STRING_FINDMETHOD(name, cppname, pyname) \
1296PyObject* STLString##name(CPPInstance* self, PyObject* args, PyObject* /*kwds*/) \
1297{ \
1298 std::string* obj = GetSTLString(self); \
1299 if (!obj) \
1300 return nullptr; \
1301 \
1302 PyObject* cppmeth = PyObject_GetAttrString((PyObject*)self, (char*)#cppname);\
1303 if (cppmeth) { \
1304 PyObject* result = PyObject_Call(cppmeth, args, nullptr); \
1305 Py_DECREF(cppmeth); \
1306 if (result) { \
1307 if (PyLongOrInt_AsULong64(result) == (PY_ULONG_LONG)std::string::npos) {\
1308 Py_DECREF(result); \
1309 return PyInt_FromLong(-1); \
1310 } \
1311 return result; \
1312 } \
1313 PyErr_Clear(); \
1314 } \
1315 \
1316 PyObject* pystr = CPyCppyy_PyText_FromStringAndSize(obj->data(), obj->size());\
1317 PyObject* pymeth = PyObject_GetAttrString(pystr, (char*)#pyname); \
1318 Py_DECREF(pystr); \
1319 PyObject* result = PyObject_CallObject(pymeth, args); \
1320 Py_DECREF(pymeth); \
1321 return result; \
1322}
1323
1324// both str and std::string have method "find" and "rfin"; try the C++ version first
1325// and fall back on the Python one in case of failure
1326CPYCPPYY_STRING_FINDMETHOD( Find, __cpp_find, find)
1327CPYCPPYY_STRING_FINDMETHOD(RFind, __cpp_rfind, rfind)
1328
1329PyObject* STLStringGetAttr(CPPInstance* self, PyObject* attr_name)
1330{
1331 std::string* obj = GetSTLString(self);
1332 if (!obj)
1333 return nullptr;
1334
1335 PyObject* pystr = CPyCppyy_PyText_FromStringAndSize(obj->data(), obj->size());
1336 PyObject* attr = PyObject_GetAttr(pystr, attr_name);
1337 Py_DECREF(pystr);
1338 return attr;
1339}
1340
1341
1342#if 0
1343PyObject* UTF8Repr(PyObject* self)
1344{
1345// force C++ string types conversion to Python str per Python __repr__ requirements
1347 if (!res || CPyCppyy_PyText_Check(res))
1348 return res;
1349 PyObject* str_res = PyObject_Str(res);
1350 Py_DECREF(res);
1351 return str_res;
1352}
1353
1354PyObject* UTF8Str(PyObject* self)
1355{
1356// force C++ string types conversion to Python str per Python __str__ requirements
1358 if (!res || CPyCppyy_PyText_Check(res))
1359 return res;
1360 PyObject* str_res = PyObject_Str(res);
1361 Py_DECREF(res);
1362 return str_res;
1363}
1364#endif
1365
1366Py_hash_t STLStringHash(PyObject* self)
1367{
1368// std::string objects hash to the same values as Python strings to allow
1369// matches in dictionaries etc.
1370 PyObject* data = STLStringGetData(self, false);
1372 Py_DECREF(data);
1373 return h;
1374}
1375
1376
1377//- string_view behavior as primitive ----------------------------------------
1378PyObject* StringViewInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
1379{
1380// if constructed from a Python unicode object, the constructor will convert it
1381// to a temporary byte string, which is likely to go out of scope too soon; so
1382// buffer it as needed
1383 PyObject* realInit = PyObject_GetAttr(self, PyStrings::gRealInit);
1384 if (realInit) {
1385 PyObject *strbuf = nullptr, *newArgs = nullptr;
1386 if (PyTuple_GET_SIZE(args) == 1) {
1387 PyObject* arg0 = PyTuple_GET_ITEM(args, 0);
1388 if (PyUnicode_Check(arg0)) {
1389 // convert to the expected bytes array to control the temporary
1390 strbuf = PyUnicode_AsEncodedString(arg0, "UTF-8", "strict");
1391 newArgs = PyTuple_New(1);
1392 Py_INCREF(strbuf);
1393 PyTuple_SET_ITEM(newArgs, 0, strbuf);
1394 } else if (PyBytes_Check(arg0)) {
1395 // tie the life time of the provided string to the string_view
1396 Py_INCREF(arg0);
1397 strbuf = arg0;
1398 }
1399 }
1400
1401 PyObject* result = PyObject_Call(realInit, newArgs ? newArgs : args, nullptr);
1402
1403 Py_XDECREF(newArgs);
1404 Py_DECREF(realInit);
1405
1406 // if construction was successful and a string buffer was used, add a
1407 // life line to it from the string_view bound object
1408 if (result && self && strbuf)
1409 PyObject_SetAttr(self, PyStrings::gLifeLine, strbuf);
1410 Py_XDECREF(strbuf);
1411
1412 return result;
1413 }
1414 return nullptr;
1415}
1416
1417
1418//- STL iterator behavior ----------------------------------------------------
1419PyObject* STLIterNext(PyObject* self)
1420{
1421// Python iterator protocol __next__ for STL forward iterators.
1422 bool mustIncrement = true;
1423 PyObject* last = nullptr;
1424 if (CPPInstance_Check(self)) {
1425 auto& dmc = ((CPPInstance*)self)->GetDatamemberCache();
1426 for (auto& p: dmc) {
1427 if (p.first == PS_END_ADDR) {
1428 last = p.second;
1429 Py_INCREF(last);
1430 } else if (p.first == PS_FLAG_ADDR) {
1431 mustIncrement = p.second == Py_True;
1432 if (!mustIncrement) {
1433 Py_DECREF(p.second);
1434 Py_INCREF(Py_True);
1435 p.second = Py_True;
1436 }
1437 }
1438 }
1439 }
1440
1441 PyObject* next = nullptr;
1442 if (last) {
1443 // handle special case of empty container (i.e. self is end)
1444 if (!PyObject_RichCompareBool(last, self, Py_EQ)) {
1445 bool iter_valid = true;
1446 if (mustIncrement) {
1447 // prefer preinc, but allow post-inc; in both cases, it is "self" that has
1448 // the updated state to dereference
1450 if (!iter) {
1451 PyErr_Clear();
1452 static PyObject* dummy = PyInt_FromLong(1l);
1454 }
1455 iter_valid = iter && PyObject_RichCompareBool(last, self, Py_NE);
1456 Py_XDECREF(iter);
1457 }
1458
1459 if (iter_valid) {
1461 if (!next) PyErr_Clear();
1462 }
1463 }
1464 Py_DECREF(last);
1465 }
1466
1467 if (!next) PyErr_SetString(PyExc_StopIteration, "");
1468 return next;
1469}
1470
1471
1472//- STL complex<T> behavior --------------------------------------------------
1473#define COMPLEX_METH_GETSET(name, cppname) \
1474static PyObject* name##ComplexGet(PyObject* self, void*) { \
1475 return PyObject_CallMethodNoArgs(self, cppname); \
1476} \
1477static int name##ComplexSet(PyObject* self, PyObject* value, void*) { \
1478 PyObject* result = PyObject_CallMethodOneArg(self, cppname, value); \
1479 if (result) { \
1480 Py_DECREF(result); \
1481 return 0; \
1482 } \
1483 return -1; \
1484} \
1485PyGetSetDef name##Complex{(char*)#name, (getter)name##ComplexGet, (setter)name##ComplexSet, nullptr, nullptr};
1486
1489
1490static PyObject* ComplexComplex(PyObject* self) {
1492 if (!real) return nullptr;
1493 double r = PyFloat_AsDouble(real);
1494 Py_DECREF(real);
1495 if (r == -1. && PyErr_Occurred())
1496 return nullptr;
1497
1499 if (!imag) return nullptr;
1500 double i = PyFloat_AsDouble(imag);
1501 Py_DECREF(imag);
1502 if (i == -1. && PyErr_Occurred())
1503 return nullptr;
1504
1505 return PyComplex_FromDoubles(r, i);
1506}
1507
1508static PyObject* ComplexRepr(PyObject* self) {
1510 if (!real) return nullptr;
1511 double r = PyFloat_AsDouble(real);
1512 Py_DECREF(real);
1513 if (r == -1. && PyErr_Occurred())
1514 return nullptr;
1515
1517 if (!imag) return nullptr;
1518 double i = PyFloat_AsDouble(imag);
1519 Py_DECREF(imag);
1520 if (i == -1. && PyErr_Occurred())
1521 return nullptr;
1522
1523 std::ostringstream s;
1524 s << '(' << r << '+' << i << "j)";
1525 return CPyCppyy_PyText_FromString(s.str().c_str());
1526}
1527
1528static PyObject* ComplexDRealGet(CPPInstance* self, void*)
1529{
1530 return PyFloat_FromDouble(((std::complex<double>*)self->GetObject())->real());
1531}
1532
1533static int ComplexDRealSet(CPPInstance* self, PyObject* value, void*)
1534{
1535 double d = PyFloat_AsDouble(value);
1536 if (d == -1.0 && PyErr_Occurred())
1537 return -1;
1538 ((std::complex<double>*)self->GetObject())->real(d);
1539 return 0;
1540}
1541
1542PyGetSetDef ComplexDReal{(char*)"real", (getter)ComplexDRealGet, (setter)ComplexDRealSet, nullptr, nullptr};
1543
1544
1545static PyObject* ComplexDImagGet(CPPInstance* self, void*)
1546{
1547 return PyFloat_FromDouble(((std::complex<double>*)self->GetObject())->imag());
1548}
1549
1550static int ComplexDImagSet(CPPInstance* self, PyObject* value, void*)
1551{
1552 double d = PyFloat_AsDouble(value);
1553 if (d == -1.0 && PyErr_Occurred())
1554 return -1;
1555 ((std::complex<double>*)self->GetObject())->imag(d);
1556 return 0;
1557}
1558
1559PyGetSetDef ComplexDImag{(char*)"imag", (getter)ComplexDImagGet, (setter)ComplexDImagSet, nullptr, nullptr};
1560
1561static PyObject* ComplexDComplex(CPPInstance* self)
1562{
1563 double r = ((std::complex<double>*)self->GetObject())->real();
1564 double i = ((std::complex<double>*)self->GetObject())->imag();
1565 return PyComplex_FromDoubles(r, i);
1566}
1567
1568
1569} // unnamed namespace
1570
1571
1572//- public functions ---------------------------------------------------------
1573namespace CPyCppyy {
1574 std::set<std::string> gIteratorTypes;
1575}
1576
1577static inline
1578bool run_pythonizors(PyObject* pyclass, PyObject* pyname, const std::vector<PyObject*>& v)
1579{
1580 PyObject* args = PyTuple_New(2);
1581 Py_INCREF(pyclass); PyTuple_SET_ITEM(args, 0, pyclass);
1582 Py_INCREF(pyname); PyTuple_SET_ITEM(args, 1, pyname);
1583
1584 bool pstatus = true;
1585 for (auto pythonizor : v) {
1586 PyObject* result = PyObject_CallObject(pythonizor, args);
1587 if (!result) {
1588 pstatus = false; // TODO: detail the error handling
1589 break;
1590 }
1591 Py_DECREF(result);
1592 }
1593 Py_DECREF(args);
1594
1595 return pstatus;
1596}
1597
1598bool CPyCppyy::Pythonize(PyObject* pyclass, const std::string& name)
1599{
1600// Add pre-defined pythonizations (for STL and ROOT) to classes based on their
1601// signature and/or class name.
1602 if (!pyclass)
1603 return false;
1604
1605 CPPScope* klass = (CPPScope*)pyclass;
1606
1607//- method name based pythonization ------------------------------------------
1608
1609// for smart pointer style classes that are otherwise not known as such; would
1610// prefer operator-> as that returns a pointer (which is simpler since it never
1611// has to deal with ref-assignment), but operator* plays better with STL iters
1612// and algorithms
1613 if (HasAttrDirect(pyclass, PyStrings::gDeref) && !Cppyy::IsSmartPtr(klass->fCppType))
1614 Utility::AddToClass(pyclass, "__getattr__", (PyCFunction)DeRefGetAttr, METH_O);
1615 else if (HasAttrDirect(pyclass, PyStrings::gFollow) && !Cppyy::IsSmartPtr(klass->fCppType))
1616 Utility::AddToClass(pyclass, "__getattr__", (PyCFunction)FollowGetAttr, METH_O);
1617
1618// for pre-check of nullptr for boolean types
1619 if (HasAttrDirect(pyclass, PyStrings::gCppBool)) {
1620#if PY_VERSION_HEX >= 0x03000000
1621 const char* pybool_name = "__bool__";
1622#else
1623 const char* pybool_name = "__nonzero__";
1624#endif
1625 Utility::AddToClass(pyclass, pybool_name, (PyCFunction)NullCheckBool, METH_NOARGS);
1626 }
1627
1628// for STL containers, and user classes modeled after them
1629 if (HasAttrDirect(pyclass, PyStrings::gSize))
1630 Utility::AddToClass(pyclass, "__len__", "size");
1631
1632 if (!IsTemplatedSTLClass(name, "vector") && // vector is dealt with below
1633 !((PyTypeObject*)pyclass)->tp_iter) {
1634 if (HasAttrDirect(pyclass, PyStrings::gBegin) && HasAttrDirect(pyclass, PyStrings::gEnd)) {
1635 // obtain the name of the return type
1636 const auto& v = Cppyy::GetMethodIndicesFromName(klass->fCppType, "begin");
1637 if (!v.empty()) {
1638 // check return type; if not explicitly an iterator, add it to the "known" return
1639 // types to add the "next" method on use
1641 const std::string& resname = Cppyy::GetMethodResultType(meth);
1642 bool isIterator = gIteratorTypes.find(resname) != gIteratorTypes.end();
1643 if (!isIterator && Cppyy::GetScope(resname)) {
1644 if (resname.find("iterator") == std::string::npos)
1645 gIteratorTypes.insert(resname);
1646 isIterator = true;
1647 }
1648
1649 if (isIterator) {
1650 // install iterator protocol a la STL
1651 ((PyTypeObject*)pyclass)->tp_iter = (getiterfunc)STLSequenceIter;
1652 Utility::AddToClass(pyclass, "__iter__", (PyCFunction)STLSequenceIter, METH_NOARGS);
1653 } else {
1654 // still okay if this is some pointer type of builtin persuasion (general class
1655 // won't work: the return type needs to understand the iterator protocol)
1656 std::string resolved = Cppyy::ResolveName(resname);
1657 if (resolved.back() == '*' && Cppyy::IsBuiltin(resolved.substr(0, resolved.size()-1))) {
1658 ((PyTypeObject*)pyclass)->tp_iter = (getiterfunc)LLSequenceIter;
1659 Utility::AddToClass(pyclass, "__iter__", (PyCFunction)LLSequenceIter, METH_NOARGS);
1660 }
1661 }
1662 }
1663 }
1664 if (!((PyTypeObject*)pyclass)->tp_iter && // no iterator resolved
1665 HasAttrDirect(pyclass, PyStrings::gGetItem) && PyObject_HasAttr(pyclass, PyStrings::gLen)) {
1666 // Python will iterate over __getitem__ using integers, but C++ operator[] will never raise
1667 // a StopIteration. A checked getitem (raising IndexError if beyond size()) works in some
1668 // cases but would mess up if operator[] is meant to implement an associative container. So,
1669 // this has to be implemented as an iterator protocol.
1670 ((PyTypeObject*)pyclass)->tp_iter = (getiterfunc)index_iter;
1671 Utility::AddToClass(pyclass, "__iter__", (PyCFunction)index_iter, METH_NOARGS);
1672 }
1673 }
1674
1675// operator==/!= are used in op_richcompare of CPPInstance, which subsequently allows
1676// comparisons to None; if no operator is available, a hook is installed for lazy
1677// lookups in the global and/or class namespace
1678 if (HasAttrDirect(pyclass, PyStrings::gEq, true) && \
1679 Cppyy::GetMethodIndicesFromName(klass->fCppType, "__eq__").empty()) {
1680 PyObject* cppol = PyObject_GetAttr(pyclass, PyStrings::gEq);
1681 if (!klass->fOperators) klass->fOperators = new Utility::PyOperators();
1682 klass->fOperators->fEq = cppol;
1683 // re-insert the forwarding __eq__ from the CPPInstance in case there was a Python-side
1684 // override in the base class
1685 static PyObject* top_eq = nullptr;
1686 if (!top_eq) {
1687 PyObject* top_cls = PyObject_GetAttrString(gThisModule, "CPPInstance");
1688 top_eq = PyObject_GetAttr(top_cls, PyStrings::gEq);
1689 Py_DECREF(top_eq); // make it borrowed
1690 Py_DECREF(top_cls);
1691 }
1692 PyObject_SetAttr(pyclass, PyStrings::gEq, top_eq);
1693 }
1694
1695 if (HasAttrDirect(pyclass, PyStrings::gNe, true) && \
1696 Cppyy::GetMethodIndicesFromName(klass->fCppType, "__ne__").empty()) {
1697 PyObject* cppol = PyObject_GetAttr(pyclass, PyStrings::gNe);
1698 if (!klass->fOperators) klass->fOperators = new Utility::PyOperators();
1699 klass->fOperators->fNe = cppol;
1700 // re-insert the forwarding __ne__ (same reason as above for __eq__)
1701 static PyObject* top_ne = nullptr;
1702 if (!top_ne) {
1703 PyObject* top_cls = PyObject_GetAttrString(gThisModule, "CPPInstance");
1704 top_ne = PyObject_GetAttr(top_cls, PyStrings::gNe);
1705 Py_DECREF(top_ne); // make it borrowed
1706 Py_DECREF(top_cls);
1707 }
1708 PyObject_SetAttr(pyclass, PyStrings::gNe, top_ne);
1709 }
1710
1711#if 0
1712 if (HasAttrDirect(pyclass, PyStrings::gRepr, true)) {
1713 // guarantee that the result of __repr__ is a Python string
1714 Utility::AddToClass(pyclass, "__cpp_repr", "__repr__");
1715 Utility::AddToClass(pyclass, "__repr__", (PyCFunction)UTF8Repr, METH_NOARGS);
1716 }
1717
1718 if (HasAttrDirect(pyclass, PyStrings::gStr, true)) {
1719 // guarantee that the result of __str__ is a Python string
1720 Utility::AddToClass(pyclass, "__cpp_str", "__str__");
1721 Utility::AddToClass(pyclass, "__str__", (PyCFunction)UTF8Str, METH_NOARGS);
1722 }
1723#endif
1724
1725 if (Cppyy::IsAggregate(((CPPClass*)pyclass)->fCppType) && name.compare(0, 5, "std::", 5) != 0 &&
1726 name.compare(0, 6, "tuple<", 6) != 0) {
1727 // create a pseudo-constructor to allow initializer-style object creation
1728 Cppyy::TCppType_t kls = ((CPPClass*)pyclass)->fCppType;
1730 if (ndata) {
1731 std::string rname = name;
1733
1734 std::ostringstream initdef;
1735 initdef << "namespace __cppyy_internal {\n"
1736 << "void init_" << rname << "(" << name << "*& self";
1737 bool codegen_ok = true;
1738 std::vector<std::string> arg_types, arg_names, arg_defaults;
1739 arg_types.reserve(ndata); arg_names.reserve(ndata); arg_defaults.reserve(ndata);
1740 for (Cppyy::TCppIndex_t i = 0; i < ndata; ++i) {
1741 if (Cppyy::IsStaticData(kls, i) || !Cppyy::IsPublicData(kls, i))
1742 continue;
1743
1744 const std::string& txt = Cppyy::GetDatamemberType(kls, i);
1745 const std::string& res = Cppyy::IsEnum(txt) ? txt : Cppyy::ResolveName(txt);
1746 const std::string& cpd = TypeManip::compound(res);
1747 std::string res_clean = TypeManip::clean_type(res, false, true);
1748
1749 if (res_clean == "internal_enum_type_t")
1750 res_clean = txt; // restore (properly scoped name)
1751
1752 if (res.rfind(']') == std::string::npos && res.rfind(')') == std::string::npos) {
1753 if (!cpd.empty()) arg_types.push_back(res_clean+cpd);
1754 else arg_types.push_back("const "+res_clean+"&");
1755 arg_names.push_back(Cppyy::GetDatamemberName(kls, i));
1756 if ((!cpd.empty() && cpd.back() == '*') || Cppyy::IsBuiltin(res_clean))
1757 arg_defaults.push_back("0");
1758 else {
1759 Cppyy::TCppScope_t klsid = Cppyy::GetScope(res_clean);
1760 if (Cppyy::IsDefaultConstructable(klsid)) arg_defaults.push_back(res_clean+"{}");
1761 }
1762 } else {
1763 codegen_ok = false; // TODO: how to support arrays, anonymous enums, etc?
1764 break;
1765 }
1766 }
1767
1768 if (codegen_ok && !arg_types.empty()) {
1769 bool defaults_ok = arg_defaults.size() == arg_types.size();
1770 for (std::vector<std::string>::size_type i = 0; i < arg_types.size(); ++i) {
1771 initdef << ", " << arg_types[i] << " " << arg_names[i];
1772 if (defaults_ok) initdef << " = " << arg_defaults[i];
1773 }
1774 initdef << ") {\n self = new " << name << "{";
1775 for (std::vector<std::string>::size_type i = 0; i < arg_names.size(); ++i) {
1776 if (i != 0) initdef << ", ";
1777 initdef << arg_names[i];
1778 }
1779 initdef << "};\n} }";
1780
1781 if (Cppyy::Compile(initdef.str(), true /* silent */)) {
1782 Cppyy::TCppScope_t cis = Cppyy::GetScope("__cppyy_internal");
1783 const auto& mix = Cppyy::GetMethodIndicesFromName(cis, "init_"+rname);
1784 if (mix.size()) {
1785 if (!Utility::AddToClass(pyclass, "__init__",
1786 new CPPFunction(cis, Cppyy::GetMethod(cis, mix[0]))))
1787 PyErr_Clear();
1788 }
1789 }
1790 }
1791 }
1792 }
1793
1794
1795//- class name based pythonization -------------------------------------------
1796
1797 if (IsTemplatedSTLClass(name, "vector")) {
1798
1799 // std::vector<bool> is a special case in C++
1800 if (!sVectorBoolTypeID) sVectorBoolTypeID = (Cppyy::TCppType_t)Cppyy::GetScope("std::vector<bool>");
1801 if (klass->fCppType == sVectorBoolTypeID) {
1802 Utility::AddToClass(pyclass, "__getitem__", (PyCFunction)VectorBoolGetItem, METH_O);
1803 Utility::AddToClass(pyclass, "__setitem__", (PyCFunction)VectorBoolSetItem);
1804 } else {
1805 // constructor that takes python collections
1806 Utility::AddToClass(pyclass, "__real_init", "__init__");
1807 Utility::AddToClass(pyclass, "__init__", (PyCFunction)VectorInit, METH_VARARGS | METH_KEYWORDS);
1808
1809 // data with size
1810 Utility::AddToClass(pyclass, "__real_data", "data");
1811 Utility::AddToClass(pyclass, "data", (PyCFunction)VectorData);
1812
1813 // numpy array conversion
1814 Utility::AddToClass(pyclass, "__array__", (PyCFunction)VectorArray, METH_VARARGS | METH_KEYWORDS /* unused */);
1815
1816 // checked getitem
1817 if (HasAttrDirect(pyclass, PyStrings::gLen)) {
1818 Utility::AddToClass(pyclass, "_getitem__unchecked", "__getitem__");
1819 Utility::AddToClass(pyclass, "__getitem__", (PyCFunction)VectorGetItem, METH_O);
1820 }
1821
1822 // vector-optimized iterator protocol
1823 ((PyTypeObject*)pyclass)->tp_iter = (getiterfunc)vector_iter;
1824
1825 // optimized __iadd__
1826 Utility::AddToClass(pyclass, "__iadd__", (PyCFunction)VectorIAdd, METH_VARARGS | METH_KEYWORDS);
1827
1828 // helpers for iteration
1829 const std::string& vtype = Cppyy::ResolveName(name+"::value_type");
1830 if (vtype.rfind("value_type") == std::string::npos) { // actually resolved?
1831 PyObject* pyvalue_type = CPyCppyy_PyText_FromString(vtype.c_str());
1832 PyObject_SetAttr(pyclass, PyStrings::gValueType, pyvalue_type);
1833 Py_DECREF(pyvalue_type);
1834 }
1835
1836 size_t typesz = Cppyy::SizeOf(name+"::value_type");
1837 if (typesz) {
1838 PyObject* pyvalue_size = PyLong_FromSsize_t(typesz);
1839 PyObject_SetAttr(pyclass, PyStrings::gValueSize, pyvalue_size);
1840 Py_DECREF(pyvalue_size);
1841 }
1842 }
1843 }
1844
1845 else if (IsTemplatedSTLClass(name, "array")) {
1846 // constructor that takes python associative collections
1847 Utility::AddToClass(pyclass, "__real_init", "__init__");
1848 Utility::AddToClass(pyclass, "__init__", (PyCFunction)ArrayInit, METH_VARARGS | METH_KEYWORDS);
1849 }
1850
1851 else if (IsTemplatedSTLClass(name, "map") || IsTemplatedSTLClass(name, "unordered_map")) {
1852 // constructor that takes python associative collections
1853 Utility::AddToClass(pyclass, "__real_init", "__init__");
1854 Utility::AddToClass(pyclass, "__init__", (PyCFunction)MapInit, METH_VARARGS | METH_KEYWORDS);
1855
1856 Utility::AddToClass(pyclass, "__contains__", (PyCFunction)STLContainsWithFind, METH_O);
1857 }
1858
1859 else if (IsTemplatedSTLClass(name, "set")) {
1860 // constructor that takes python associative collections
1861 Utility::AddToClass(pyclass, "__real_init", "__init__");
1862 Utility::AddToClass(pyclass, "__init__", (PyCFunction)SetInit, METH_VARARGS | METH_KEYWORDS);
1863
1864 Utility::AddToClass(pyclass, "__contains__", (PyCFunction)STLContainsWithFind, METH_O);
1865 }
1866
1867 else if (IsTemplatedSTLClass(name, "pair")) {
1868 Utility::AddToClass(pyclass, "__getitem__", (PyCFunction)PairUnpack, METH_O);
1869 Utility::AddToClass(pyclass, "__len__", (PyCFunction)ReturnTwo, METH_NOARGS);
1870 }
1871
1872 if (IsTemplatedSTLClass(name, "shared_ptr") || IsTemplatedSTLClass(name, "unique_ptr")) {
1873 Utility::AddToClass(pyclass, "__real_init", "__init__");
1874 Utility::AddToClass(pyclass, "__init__", (PyCFunction)SmartPtrInit, METH_VARARGS | METH_KEYWORDS);
1875 }
1876
1877 else if (!((PyTypeObject*)pyclass)->tp_iter && \
1878 (name.find("iterator") != std::string::npos || gIteratorTypes.find(name) != gIteratorTypes.end())) {
1879 ((PyTypeObject*)pyclass)->tp_iternext = (iternextfunc)STLIterNext;
1880 Utility::AddToClass(pyclass, CPPYY__next__, (PyCFunction)STLIterNext, METH_NOARGS);
1881 ((PyTypeObject*)pyclass)->tp_iter = (getiterfunc)PyObject_SelfIter;
1882 Utility::AddToClass(pyclass, "__iter__", (PyCFunction)PyObject_SelfIter, METH_NOARGS);
1883 }
1884
1885 else if (name == "string" || name == "std::string") { // TODO: ask backend as well
1886 Utility::AddToClass(pyclass, "__repr__", (PyCFunction)STLStringRepr, METH_NOARGS);
1887 Utility::AddToClass(pyclass, "__str__", (PyCFunction)STLStringStr, METH_NOARGS);
1888 Utility::AddToClass(pyclass, "__bytes__", (PyCFunction)STLStringBytes, METH_NOARGS);
1889 Utility::AddToClass(pyclass, "__cmp__", (PyCFunction)STLStringCompare, METH_O);
1890 Utility::AddToClass(pyclass, "__eq__", (PyCFunction)STLStringIsEqual, METH_O);
1891 Utility::AddToClass(pyclass, "__ne__", (PyCFunction)STLStringIsNotEqual, METH_O);
1892 Utility::AddToClass(pyclass, "__contains__", (PyCFunction)STLStringContains, METH_O);
1893 Utility::AddToClass(pyclass, "decode", (PyCFunction)STLStringDecode, METH_VARARGS | METH_KEYWORDS);
1894 Utility::AddToClass(pyclass, "__cpp_find", "find");
1895 Utility::AddToClass(pyclass, "find", (PyCFunction)STLStringFind, METH_VARARGS | METH_KEYWORDS);
1896 Utility::AddToClass(pyclass, "__cpp_rfind", "rfind");
1897 Utility::AddToClass(pyclass, "rfind", (PyCFunction)STLStringRFind, METH_VARARGS | METH_KEYWORDS);
1898 Utility::AddToClass(pyclass, "__cpp_replace", "replace");
1899 Utility::AddToClass(pyclass, "replace", (PyCFunction)STLStringReplace, METH_VARARGS | METH_KEYWORDS);
1900 Utility::AddToClass(pyclass, "__getattr__", (PyCFunction)STLStringGetAttr, METH_O);
1901
1902 // to allow use of std::string in dictionaries and findable with str
1903 ((PyTypeObject*)pyclass)->tp_hash = (hashfunc)STLStringHash;
1904 }
1905
1906 else if (name == "basic_string_view<char>" || name == "std::basic_string_view<char>") {
1907 Utility::AddToClass(pyclass, "__real_init", "__init__");
1908 Utility::AddToClass(pyclass, "__init__", (PyCFunction)StringViewInit, METH_VARARGS | METH_KEYWORDS);
1909 }
1910
1911 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> >") {
1912 Utility::AddToClass(pyclass, "__repr__", (PyCFunction)STLWStringRepr, METH_NOARGS);
1913 Utility::AddToClass(pyclass, "__str__", (PyCFunction)STLWStringStr, METH_NOARGS);
1914 Utility::AddToClass(pyclass, "__bytes__", (PyCFunction)STLWStringBytes, METH_NOARGS);
1915 Utility::AddToClass(pyclass, "__cmp__", (PyCFunction)STLWStringCompare, METH_O);
1916 Utility::AddToClass(pyclass, "__eq__", (PyCFunction)STLWStringIsEqual, METH_O);
1917 Utility::AddToClass(pyclass, "__ne__", (PyCFunction)STLWStringIsNotEqual, METH_O);
1918 }
1919
1920 else if (name == "complex<double>" || name == "std::complex<double>") {
1921 Utility::AddToClass(pyclass, "__cpp_real", "real");
1922 PyObject_SetAttrString(pyclass, "real", PyDescr_NewGetSet((PyTypeObject*)pyclass, &ComplexDReal));
1923 Utility::AddToClass(pyclass, "__cpp_imag", "imag");
1924 PyObject_SetAttrString(pyclass, "imag", PyDescr_NewGetSet((PyTypeObject*)pyclass, &ComplexDImag));
1925 Utility::AddToClass(pyclass, "__complex__", (PyCFunction)ComplexDComplex, METH_NOARGS);
1926 Utility::AddToClass(pyclass, "__repr__", (PyCFunction)ComplexRepr, METH_NOARGS);
1927 }
1928
1929 else if (IsTemplatedSTLClass(name, "complex")) {
1930 Utility::AddToClass(pyclass, "__cpp_real", "real");
1931 PyObject_SetAttrString(pyclass, "real", PyDescr_NewGetSet((PyTypeObject*)pyclass, &realComplex));
1932 Utility::AddToClass(pyclass, "__cpp_imag", "imag");
1933 PyObject_SetAttrString(pyclass, "imag", PyDescr_NewGetSet((PyTypeObject*)pyclass, &imagComplex));
1934 Utility::AddToClass(pyclass, "__complex__", (PyCFunction)ComplexComplex, METH_NOARGS);
1935 Utility::AddToClass(pyclass, "__repr__", (PyCFunction)ComplexRepr, METH_NOARGS);
1936 }
1937
1938// direct user access; there are two calls here:
1939// - explicit pythonization: won't fall through to the base classes and is preferred if present
1940// - normal pythonization: only called if explicit isn't present, falls through to base classes
1941 bool bUserOk = true; PyObject* res = nullptr;
1942 PyObject* pyname = CPyCppyy_PyText_FromString(name.c_str());
1943 if (HasAttrDirect(pyclass, PyStrings::gExPythonize)) {
1944 res = PyObject_CallMethodObjArgs(pyclass, PyStrings::gExPythonize, pyclass, pyname, nullptr);
1945 bUserOk = (bool)res;
1946 } else {
1947 PyObject* func = PyObject_GetAttr(pyclass, PyStrings::gPythonize);
1948 if (func) {
1949 res = PyObject_CallFunctionObjArgs(func, pyclass, pyname, nullptr);
1950 Py_DECREF(func);
1951 bUserOk = (bool)res;
1952 } else
1953 PyErr_Clear();
1954 }
1955 if (!bUserOk) {
1956 Py_DECREF(pyname);
1957 return false;
1958 } else {
1959 Py_XDECREF(res);
1960 // pyname handed to args tuple below
1961 }
1962
1963// call registered pythonizors, if any: first run the namespace-specific pythonizors, then
1964// the global ones (the idea is to allow writing a pythonizor that see all classes)
1965 bool pstatus = true;
1966 std::string outer_scope = TypeManip::extract_namespace(name);
1967 auto &pyzMap = pythonizations();
1968 if (!outer_scope.empty()) {
1969 auto p = pyzMap.find(outer_scope);
1970 if (p != pyzMap.end()) {
1972 name.substr(outer_scope.size()+2, std::string::npos).c_str());
1973 pstatus = run_pythonizors(pyclass, subname, p->second);
1974 Py_DECREF(subname);
1975 }
1976 }
1977
1978 if (pstatus) {
1979 auto p = pyzMap.find("");
1980 if (p != pyzMap.end())
1981 pstatus = run_pythonizors(pyclass, pyname, p->second);
1982 }
1983
1984 Py_DECREF(pyname);
1985
1986// phew! all done ...
1987 return pstatus;
1988}
#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
void FillVector(std::vector< double > &v, int size, T *a)
#define d(i)
Definition RSha256.hxx:102
#define c(i)
Definition RSha256.hxx:101
#define h(i)
Definition RSha256.hxx:106
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t attr
char name[80]
Definition TGX11.cxx:110
Cppyy::TCppType_t ObjectIsA(bool check_smart=true) const
Utility::PyOperators * fOperators
Definition CPPScope.h:61
Cppyy::TCppType_t fCppType
Definition CPPScope.h:55
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:808
bool AddToClass(PyObject *pyclass, const char *label, PyCFunction cfunc, int flags=METH_VARARGS)
Definition Utility.cxx:182
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)
PyObject_HEAD PyObject * ii_container
Cppyy::TCppType_t vi_klass
CPyCppyy::Converter * vi_converter