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 "CPPOverload.h"
7#include "CustomPyTypes.h"
8#include "LowLevelViews.h"
9#include "ProxyWrappers.h"
10#include "PyCallable.h"
11#include "PyStrings.h"
12#include "TypeManip.h"
13#include "Utility.h"
14
15// Standard
16#include <algorithm>
17#include <complex>
18#include <set>
19#include <stdexcept>
20#include <sstream>
21#include <string>
22#include <utility>
23
24
25//- data and local helpers ---------------------------------------------------
26namespace CPyCppyy {
27 extern PyObject* gThisModule;
28 extern std::map<std::string, std::vector<PyObject*>> gPythonizations;
29}
30
31namespace {
32
33// for convenience
34using namespace CPyCppyy;
35
36//-----------------------------------------------------------------------------
37bool HasAttrDirect(PyObject* pyclass, PyObject* pyname, bool mustBeCPyCppyy = false) {
38// prevents calls to Py_TYPE(pyclass)->tp_getattr, which is unnecessary for our
39// purposes here and could tickle problems w/ spurious lookups into ROOT meta
40 PyObject* dct = PyObject_GetAttr(pyclass, PyStrings::gDict);
41 if (dct) {
42 PyObject* attr = PyObject_GetItem(dct, pyname);
43 Py_DECREF(dct);
44 if (attr) {
45 bool ret = !mustBeCPyCppyy || CPPOverload_Check(attr);
46 Py_DECREF(attr);
47 return ret;
48 }
49 }
50 PyErr_Clear();
51 return false;
52}
53
54//-----------------------------------------------------------------------------
55inline bool IsTemplatedSTLClass(const std::string& name, const std::string& klass) {
56// Scan the name of the class and determine whether it is a template instantiation.
57 auto pos = name.find(klass);
58 return (pos == 0 || pos == 5) && name.find("::", name.rfind(">")) == std::string::npos;
59}
60
61// to prevent compiler warnings about const char* -> char*
62inline PyObject* CallPyObjMethod(PyObject* obj, const char* meth)
63{
64// Helper; call method with signature: obj->meth().
65 Py_INCREF(obj);
66 PyObject* result = PyObject_CallMethod(obj, const_cast<char*>(meth), const_cast<char*>(""));
67 Py_DECREF(obj);
68 return result;
69}
70
71//-----------------------------------------------------------------------------
72inline PyObject* CallPyObjMethod(PyObject* obj, const char* meth, PyObject* arg1)
73{
74// Helper; call method with signature: obj->meth(arg1).
75 Py_INCREF(obj);
76 PyObject* result = PyObject_CallMethod(
77 obj, const_cast<char*>(meth), const_cast<char*>("O"), arg1);
78 Py_DECREF(obj);
79 return result;
80}
81
82//-----------------------------------------------------------------------------
84{
85// Helper; converts python index into straight C index.
87 if (idx == (Py_ssize_t)-1 && PyErr_Occurred())
88 return nullptr;
89
90 Py_ssize_t size = PySequence_Size(self);
91 if (idx >= size || (idx < 0 && idx < -size)) {
92 PyErr_SetString(PyExc_IndexError, "index out of range");
93 return nullptr;
94 }
95
96 PyObject* pyindex = nullptr;
97 if (idx >= 0) {
98 Py_INCREF(index);
99 pyindex = index;
100 } else
101 pyindex = PyLong_FromSsize_t(size+idx);
102
103 return pyindex;
104}
105
106//-----------------------------------------------------------------------------
107inline bool AdjustSlice(const Py_ssize_t nlen, Py_ssize_t& start, Py_ssize_t& stop, Py_ssize_t& step)
108{
109// Helper; modify slice range to match the container.
110 if ((step > 0 && stop <= start) || (step < 0 && start <= stop))
111 return false;
112
113 if (start < 0) start = 0;
114 if (start >= nlen) start = nlen-1;
115 if (step >= nlen) step = nlen;
116
117 stop = step > 0 ? std::min(nlen, stop) : (stop >= 0 ? stop : -1);
118 return true;
119}
120
121//-----------------------------------------------------------------------------
122inline PyObject* CallSelfIndex(CPPInstance* self, PyObject* idx, PyObject* pymeth)
123{
124// Helper; call method with signature: meth(pyindex).
125 Py_INCREF((PyObject*)self);
126 PyObject* pyindex = PyStyleIndex((PyObject*)self, idx);
127 if (!pyindex) {
128 Py_DECREF((PyObject*)self);
129 return nullptr;
130 }
131
132 PyObject* result = PyObject_CallMethodObjArgs((PyObject*)self, pymeth, pyindex, nullptr);
133 Py_DECREF(pyindex);
134 Py_DECREF((PyObject*)self);
135 return result;
136}
137
138//- "smart pointer" behavior ---------------------------------------------------
139PyObject* DeRefGetAttr(PyObject* self, PyObject* name)
140{
141// Follow operator*() if present (available in python as __deref__), so that
142// smart pointers behave as expected.
144 // TODO: these calls come from TemplateProxy and are unlikely to be needed in practice,
145 // whereas as-is, they can accidentally dereference the result of end() on some STL
146 // containers. Obviously, this is a dumb hack that should be resolved more fundamentally.
147 PyErr_SetString(PyExc_AttributeError, CPyCppyy_PyText_AsString(name));
148 return nullptr;
149 }
150
152 PyErr_SetString(PyExc_TypeError, "getattr(): attribute name must be string");
153
154 PyObject* pyptr = PyObject_CallMethodObjArgs(self, PyStrings::gDeref, nullptr);
155 if (!pyptr)
156 return nullptr;
157
158// prevent a potential infinite loop
159 if (Py_TYPE(pyptr) == Py_TYPE(self)) {
160 PyObject* val1 = PyObject_Str(self);
161 PyObject* val2 = PyObject_Str(name);
162 PyErr_Format(PyExc_AttributeError, "%s has no attribute \'%s\'",
164 Py_DECREF(val2);
165 Py_DECREF(val1);
166
167 Py_DECREF(pyptr);
168 return nullptr;
169 }
170
171 PyObject* result = PyObject_GetAttr(pyptr, name);
172 Py_DECREF(pyptr);
173 return result;
174}
175
176//-----------------------------------------------------------------------------
177PyObject* FollowGetAttr(PyObject* self, PyObject* name)
178{
179// Follow operator->() if present (available in python as __follow__), so that
180// smart pointers behave as expected.
182 PyErr_SetString(PyExc_TypeError, "getattr(): attribute name must be string");
183
184 PyObject* pyptr = PyObject_CallMethodObjArgs(self, PyStrings::gFollow, nullptr);
185 if (!pyptr)
186 return nullptr;
187
188 PyObject* result = PyObject_GetAttr(pyptr, name);
189 Py_DECREF(pyptr);
190 return result;
191}
192
193
194//- vector behavior as primitives ----------------------------------------------
195#if PY_VERSION_HEX < 0x03040000
196#define PyObject_LengthHint _PyObject_LengthHint
197#endif
198
199// TODO: can probably use the below getters in the InitializerListConverter
200struct ItemGetter {
201 ItemGetter(PyObject* pyobj) : fPyObject(pyobj) { Py_INCREF(fPyObject); }
202 virtual ~ItemGetter() { Py_DECREF(fPyObject); }
203 virtual Py_ssize_t size() = 0;
204 virtual PyObject* get() = 0;
205 PyObject* fPyObject;
206};
207
208struct CountedItemGetter : public ItemGetter {
209 CountedItemGetter(PyObject* pyobj) : ItemGetter(pyobj), fCur(0) {}
210 Py_ssize_t fCur;
211};
212
213struct TupleItemGetter : public CountedItemGetter {
214 using CountedItemGetter::CountedItemGetter;
215 virtual Py_ssize_t size() { return PyTuple_GET_SIZE(fPyObject); }
216 virtual PyObject* get() {
217 if (fCur < PyTuple_GET_SIZE(fPyObject)) {
218 PyObject* item = PyTuple_GET_ITEM(fPyObject, fCur++);
219 Py_INCREF(item);
220 return item;
221 }
222 PyErr_SetString(PyExc_StopIteration, "end of tuple");
223 return nullptr;
224 }
225};
226
227struct ListItemGetter : public CountedItemGetter {
228 using CountedItemGetter::CountedItemGetter;
229 virtual Py_ssize_t size() { return PyList_GET_SIZE(fPyObject); }
230 virtual PyObject* get() {
231 if (fCur < PyList_GET_SIZE(fPyObject)) {
232 PyObject* item = PyList_GET_ITEM(fPyObject, fCur++);
233 Py_INCREF(item);
234 return item;
235 }
236 PyErr_SetString(PyExc_StopIteration, "end of list");
237 return nullptr;
238 }
239};
240
241struct SequenceItemGetter : public CountedItemGetter {
242 using CountedItemGetter::CountedItemGetter;
243 virtual Py_ssize_t size() {
244 Py_ssize_t sz = PySequence_Size(fPyObject);
245 if (sz < 0) {
246 PyErr_Clear();
247 return PyObject_LengthHint(fPyObject, 8);
248 }
249 return sz;
250 }
251 virtual PyObject* get() { return PySequence_GetItem(fPyObject, fCur++); }
252};
253
254struct IterItemGetter : public ItemGetter {
255 using ItemGetter::ItemGetter;
256 virtual Py_ssize_t size() { return PyObject_LengthHint(fPyObject, 8); }
257 virtual PyObject* get() { return (*(Py_TYPE(fPyObject)->tp_iternext))(fPyObject); }
258};
259
260PyObject* VectorInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
261{
262// Specialized vector constructor to allow construction from containers; allowing
263// such construction from initializer_list instead would possible, but can be
264// error-prone. This use case is common enough for std::vector to implement it
265// directly, except for arrays (which can be passed wholesale) and strings (which
266// won't convert properly as they'll be seen as buffers)
267
268 ItemGetter* getter = nullptr;
269 if (PyTuple_GET_SIZE(args) == 1) {
270 PyObject* fi = PyTuple_GET_ITEM(args, 0);
271 if (CPyCppyy_PyText_Check(fi) || PyBytes_Check(fi)) {
272 PyErr_SetString(PyExc_TypeError, "can not convert string to vector");
273 return nullptr;
274 }
275 // TODO: this only tests for new-style buffers, which is too strict, but a
276 // generic check for Py_TYPE(fi)->tp_as_buffer is too loose (note that the
277 // main use case is numpy, which offers the new interface)
278 if (!PyObject_CheckBuffer(fi)) {
279 if (PyTuple_CheckExact(fi))
280 getter = new TupleItemGetter(fi);
281 else if (PyList_CheckExact(fi))
282 getter = new ListItemGetter(fi);
283 else if (PySequence_Check(fi))
284 getter = new SequenceItemGetter(fi);
285 else {
286 PyObject* iter = PyObject_GetIter(fi);
287 if (iter) {
288 getter = new IterItemGetter{iter};
289 Py_DECREF(iter);
290 }
291 else PyErr_Clear();
292 }
293 }
294 }
295
296 if (getter) {
297 // construct an empty vector, then back-fill it
298 PyObject* mname = CPyCppyy_PyText_FromString("__real_init");
299 PyObject* result = PyObject_CallMethodObjArgs(self, mname, nullptr);
300 Py_DECREF(mname);
301 if (!result) {
302 delete getter;
303 return result;
304 }
305
306 Py_ssize_t sz = getter->size();
307 if (sz < 0) {
308 delete getter;
309 return nullptr;
310 }
311
312 // reserve memory as appliable
313 if (0 < sz) {
314 PyObject* res = PyObject_CallMethod(self, (char*)"reserve", (char*)"n", sz);
315 Py_DECREF(res);
316 } else { // empty container
317 delete getter;
318 return result;
319 }
320
321 bool fill_ok = true;
322
323 // two main options: a list of lists (or tuples), or a list of objects; the former
324 // are emplace_back'ed, the latter push_back'ed
325 PyObject* fi = PySequence_GetItem(PyTuple_GET_ITEM(args, 0), 0);
326 if (!fi) PyErr_Clear();
327 if (fi && (PyTuple_CheckExact(fi) || PyList_CheckExact(fi))) {
328 // use emplace_back to construct the vector entries one by one
329 PyObject* eb_call = PyObject_GetAttrString(self, (char*)"emplace_back");
330 PyObject* vtype = PyObject_GetAttrString((PyObject*)Py_TYPE(self), "value_type");
331 bool value_is_vector = false;
332 if (vtype && CPyCppyy_PyText_Check(vtype)) {
333 // if the value_type is a vector, then allow for initialization from sequences
334 if (std::string(CPyCppyy_PyText_AsString(vtype)).rfind("std::vector", 0) != std::string::npos)
335 value_is_vector = true;
336 } else
337 PyErr_Clear();
338 Py_XDECREF(vtype);
339
340 if (eb_call) {
341 PyObject* eb_args;
342 for (int i = 0; /* until break */; ++i) {
343 PyObject* item = getter->get();
344 if (item) {
345 if (value_is_vector && PySequence_Check(item)) {
346 eb_args = PyTuple_New(1);
347 PyTuple_SET_ITEM(eb_args, 0, item);
348 } else if (PyTuple_CheckExact(item)) {
349 eb_args = item;
350 } else if (PyList_CheckExact(item)) {
351 Py_ssize_t isz = PyList_GET_SIZE(item);
352 eb_args = PyTuple_New(isz);
353 for (Py_ssize_t j = 0; j < isz; ++j) {
354 PyObject* iarg = PyList_GET_ITEM(item, j);
355 Py_INCREF(iarg);
356 PyTuple_SET_ITEM(eb_args, j, iarg);
357 }
358 Py_DECREF(item);
359 } else {
360 Py_DECREF(item);
361 PyErr_Format(PyExc_TypeError, "argument %d is not a tuple or list", i);
362 fill_ok = false;
363 break;
364 }
365 PyObject* ebres = PyObject_CallObject(eb_call, eb_args);
366 Py_DECREF(eb_args);
367 if (!ebres) {
368 fill_ok = false;
369 break;
370 }
371 Py_DECREF(ebres);
372 } else {
373 if (PyErr_Occurred()) {
374 if (!(PyErr_ExceptionMatches(PyExc_IndexError) ||
375 PyErr_ExceptionMatches(PyExc_StopIteration)))
376 fill_ok = false;
377 else { PyErr_Clear(); }
378 }
379 break;
380 }
381 }
382 Py_DECREF(eb_call);
383 }
384 } else {
385 // use push_back to add the vector entries one by one
386 PyObject* pb_call = PyObject_GetAttrString(self, (char*)"push_back");
387 if (pb_call) {
388 for (;;) {
389 PyObject* item = getter->get();
390 if (item) {
391 PyObject* pbres = PyObject_CallFunctionObjArgs(pb_call, item, nullptr);
392 Py_DECREF(item);
393 if (!pbres) {
394 fill_ok = false;
395 break;
396 }
397 Py_DECREF(pbres);
398 } else {
399 if (PyErr_Occurred()) {
400 if (!(PyErr_ExceptionMatches(PyExc_IndexError) ||
401 PyErr_ExceptionMatches(PyExc_StopIteration)))
402 fill_ok = false;
403 else { PyErr_Clear(); }
404 }
405 break;
406 }
407 }
408 Py_DECREF(pb_call);
409 }
410 }
411 Py_XDECREF(fi);
412 delete getter;
413
414 if (!fill_ok) {
415 Py_DECREF(result);
416 return nullptr;
417 }
418
419 return result;
420 }
421
422// The given argument wasn't iterable: simply forward to regular constructor
423 PyObject* realInit = PyObject_GetAttrString(self, "__real_init");
424 if (realInit) {
425 PyObject* result = PyObject_Call(realInit, args, nullptr);
426 Py_DECREF(realInit);
427 return result;
428 }
429
430 return nullptr;
431}
432
433//---------------------------------------------------------------------------
434PyObject* VectorData(PyObject* self, PyObject*)
435{
436 PyObject* pydata = CallPyObjMethod(self, "__real_data");
437 if (!LowLevelView_Check(pydata)) return pydata;
438
439 PyObject* pylen = PyObject_CallMethodObjArgs(self, PyStrings::gSize, nullptr);
440 if (!pylen) {
441 PyErr_Clear();
442 return pydata;
443 }
444
445 long clen = PyInt_AsLong(pylen);
446 Py_DECREF(pylen);
447
448// TODO: should be a LowLevelView helper
449 Py_buffer& bi = ((LowLevelView*)pydata)->fBufInfo;
450 bi.len = clen * bi.itemsize;
451 if (bi.ndim == 1 && bi.shape)
452 bi.shape[0] = clen;
453
454 return pydata;
455}
456
457
458//-----------------------------------------------------------------------------
459static PyObject* vector_iter(PyObject* v) {
460 vectoriterobject* vi = PyObject_GC_New(vectoriterobject, &VectorIter_Type);
461 if (!vi) return nullptr;
462
463 Py_INCREF(v);
464 vi->ii_container = v;
465 vi->vi_flags = v->ob_refcnt <= 2 ? 1 : 0; // 2, b/c of preceding INCREF
466
467 PyObject* pyvalue_type = PyObject_GetAttrString((PyObject*)Py_TYPE(v), "value_type");
468 PyObject* pyvalue_size = PyObject_GetAttrString((PyObject*)Py_TYPE(v), "value_size");
469
470 vi->vi_klass = 0;
471 if (pyvalue_type && pyvalue_size) {
472 PyObject* pydata = CallPyObjMethod(v, "data");
473 if (!pydata || Utility::GetBuffer(pydata, '*', 1, vi->vi_data, false) == 0) {
474 if (CPPInstance_Check(pydata)) {
475 vi->vi_data = ((CPPInstance*)pydata)->GetObjectRaw();
476 vi->vi_klass = ((CPPInstance*)pydata)->ObjectIsA(false);
477 } else
478 vi->vi_data = nullptr;
479 }
480 Py_XDECREF(pydata);
481
482 vi->vi_converter = vi->vi_klass ? nullptr : CPyCppyy::CreateConverter(CPyCppyy_PyText_AsString(pyvalue_type));
483 vi->vi_stride = PyLong_AsLong(pyvalue_size);
484 } else {
485 PyErr_Clear();
486 vi->vi_data = nullptr;
487 vi->vi_converter = nullptr;
488 vi->vi_stride = 0;
489 }
490
491 Py_XDECREF(pyvalue_size);
492 Py_XDECREF(pyvalue_type);
493
494 vi->ii_pos = 0;
495 vi->ii_len = PySequence_Size(v);
496
497 PyObject_GC_Track(vi);
498 return (PyObject*)vi;
499}
500
501PyObject* VectorGetItem(CPPInstance* self, PySliceObject* index)
502{
503// Implement python's __getitem__ for std::vector<>s.
504 if (PySlice_Check(index)) {
505 if (!self->GetObject()) {
506 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
507 return nullptr;
508 }
509
510 PyObject* pyclass = (PyObject*)Py_TYPE((PyObject*)self);
511 PyObject* nseq = PyObject_CallObject(pyclass, nullptr);
512
513 Py_ssize_t start, stop, step;
514 PySlice_GetIndices((CPyCppyy_PySliceCast)index, PyObject_Length((PyObject*)self), &start, &stop, &step);
515
516 const Py_ssize_t nlen = PySequence_Size((PyObject*)self);
517 if (!AdjustSlice(nlen, start, stop, step))
518 return nseq;
519
520 const Py_ssize_t sign = step < 0 ? -1 : 1;
521 for (Py_ssize_t i = start; i*sign < stop*sign; i += step) {
522 PyObject* pyidx = PyInt_FromSsize_t(i);
523 PyObject* item = PyObject_CallMethodObjArgs((PyObject*)self, PyStrings::gGetNoCheck, pyidx, nullptr);
524 CallPyObjMethod(nseq, "push_back", item);
525 Py_DECREF(item);
526 Py_DECREF(pyidx);
527 }
528
529 return nseq;
530 }
531
532 return CallSelfIndex(self, (PyObject*)index, PyStrings::gGetNoCheck);
533}
534
535
536static Cppyy::TCppType_t sVectorBoolTypeID = (Cppyy::TCppType_t)0;
537
538PyObject* VectorBoolGetItem(CPPInstance* self, PyObject* idx)
539{
540// std::vector<bool> is a special-case in C++, and its return type depends on
541// the compiler: treat it special here as well
542 if (!CPPInstance_Check(self) || self->ObjectIsA() != sVectorBoolTypeID) {
543 PyErr_Format(PyExc_TypeError,
544 "require object of type std::vector<bool>, but %s given",
545 Cppyy::GetScopedFinalName(self->ObjectIsA()).c_str());
546 return nullptr;
547 }
548
549 if (!self->GetObject()) {
550 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
551 return nullptr;
552 }
553
554 if (PySlice_Check(idx)) {
555 PyObject* pyclass = (PyObject*)Py_TYPE((PyObject*)self);
556 PyObject* nseq = PyObject_CallObject(pyclass, nullptr);
557
558 Py_ssize_t start, stop, step;
559 PySlice_GetIndices((CPyCppyy_PySliceCast)idx, PyObject_Length((PyObject*)self), &start, &stop, &step);
560 const Py_ssize_t nlen = PySequence_Size((PyObject*)self);
561 if (!AdjustSlice(nlen, start, stop, step))
562 return nseq;
563
564 const Py_ssize_t sign = step < 0 ? -1 : 1;
565 for (Py_ssize_t i = start; i*sign < stop*sign; i += step) {
566 PyObject* pyidx = PyInt_FromSsize_t(i);
567 PyObject* item = PyObject_CallMethodObjArgs((PyObject*)self, PyStrings::gGetItem, pyidx, nullptr);
568 CallPyObjMethod(nseq, "push_back", item);
569 Py_DECREF(item);
570 Py_DECREF(pyidx);
571 }
572
573 return nseq;
574 }
575
576 PyObject* pyindex = PyStyleIndex((PyObject*)self, idx);
577 if (!pyindex)
578 return nullptr;
579
580 int index = (int)PyLong_AsLong(pyindex);
581 Py_DECREF(pyindex);
582
583// get hold of the actual std::vector<bool> (no cast, as vector is never a base)
584 std::vector<bool>* vb = (std::vector<bool>*)self->GetObject();
585
586// finally, return the value
587 if (bool((*vb)[index]))
590}
591
592PyObject* VectorBoolSetItem(CPPInstance* self, PyObject* args)
593{
594// std::vector<bool> is a special-case in C++, and its return type depends on
595// the compiler: treat it special here as well
596 if (!CPPInstance_Check(self) || self->ObjectIsA() != sVectorBoolTypeID) {
597 PyErr_Format(PyExc_TypeError,
598 "require object of type std::vector<bool>, but %s given",
599 Cppyy::GetScopedFinalName(self->ObjectIsA()).c_str());
600 return nullptr;
601 }
602
603 if (!self->GetObject()) {
604 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
605 return nullptr;
606 }
607
608 int bval = 0; PyObject* idx = nullptr;
609 if (!PyArg_ParseTuple(args, const_cast<char*>("Oi:__setitem__"), &idx, &bval))
610 return nullptr;
611
612 PyObject* pyindex = PyStyleIndex((PyObject*)self, idx);
613 if (!pyindex)
614 return nullptr;
615
616 int index = (int)PyLong_AsLong(pyindex);
617 Py_DECREF(pyindex);
618
619// get hold of the actual std::vector<bool> (no cast, as vector is never a base)
620 std::vector<bool>* vb = (std::vector<bool>*)self->GetObject();
621
622// finally, set the value
623 (*vb)[index] = (bool)bval;
624
626}
627
628//- map behavior as primitives ------------------------------------------------
629PyObject* MapContains(PyObject* self, PyObject* obj)
630{
631// Implement python's __contains__ for std::map<>s
632 PyObject* result = nullptr;
633
634 PyObject* iter = CallPyObjMethod(self, "find", obj);
635 if (CPPInstance_Check(iter)) {
636 PyObject* end = PyObject_CallMethodObjArgs(self, PyStrings::gEnd, nullptr);
637 if (CPPInstance_Check(end)) {
638 if (!PyObject_RichCompareBool(iter, end, Py_EQ)) {
639 Py_INCREF(Py_True);
640 result = Py_True;
641 }
642 }
643 Py_XDECREF(end);
644 }
645 Py_XDECREF(iter);
646
647 if (!result) {
648 PyErr_Clear(); // e.g. wrong argument type, which should always lead to False
649 Py_INCREF(Py_False);
650 result = Py_False;
651 }
652
653 return result;
654}
655
656
657//- STL container iterator support --------------------------------------------
658static const ptrdiff_t PS_END_ADDR = 7; // non-aligned address, so no clash
659static const ptrdiff_t PS_FLAG_ADDR = 11; // id.
660static const ptrdiff_t PS_COLL_ADDR = 13; // id.
661
662PyObject* StlSequenceIter(PyObject* self)
663{
664// Implement python's __iter__ for std::iterator<>s
665 PyObject* iter = PyObject_CallMethodObjArgs(self, PyStrings::gBegin, nullptr);
666 if (iter) {
667 PyObject* end = PyObject_CallMethodObjArgs(self, PyStrings::gEnd, nullptr);
668 if (end) {
669 if (CPPInstance_Check(iter)) {
670 // use the data member cache to store extra state on the iterator object,
671 // without it being visible on the Python side
672 auto& dmc = ((CPPInstance*)iter)->GetDatamemberCache();
673 dmc.push_back(std::make_pair(PS_END_ADDR, end));
674
675 // set a flag, indicating first iteration (reset in __next__)
676 Py_INCREF(Py_False);
677 dmc.push_back(std::make_pair(PS_FLAG_ADDR, Py_False));
678
679 // make sure the iterated over collection remains alive for the duration
680 Py_INCREF(self);
681 dmc.push_back(std::make_pair(PS_COLL_ADDR, self));
682 } else {
683 // could store "end" on the object's dictionary anyway, but if end() returns
684 // a user-customized object, then its __next__ is probably custom, too
685 Py_DECREF(end);
686 }
687 }
688 }
689 return iter;
690}
691
692//- generic iterator support over a sequence with operator[] and size ---------
693//-----------------------------------------------------------------------------
694static PyObject* index_iter(PyObject* c) {
695 indexiterobject* ii = PyObject_GC_New(indexiterobject, &IndexIter_Type);
696 if (!ii) return nullptr;
697
698 Py_INCREF(c);
699 ii->ii_container = c;
700 ii->ii_pos = 0;
701 ii->ii_len = PySequence_Size(c);
702
703 PyObject_GC_Track(ii);
704 return (PyObject*)ii;
705}
706
707
708//- safe indexing for STL-like vector w/o iterator dictionaries ---------------
709/* replaced by indexiterobject iteration, but may still have some future use ...
710PyObject* CheckedGetItem(PyObject* self, PyObject* obj)
711{
712// Implement a generic python __getitem__ for STL-like classes that are missing the
713// reflection info for their iterators. This is then used for iteration by means of
714// consecutive indeces, it such index is of integer type.
715 Py_ssize_t size = PySequence_Size(self);
716 Py_ssize_t idx = PyInt_AsSsize_t(obj);
717 if ((size == (Py_ssize_t)-1 || idx == (Py_ssize_t)-1) && PyErr_Occurred()) {
718 // argument conversion problem: let method itself resolve anew and report
719 PyErr_Clear();
720 return PyObject_CallMethodObjArgs(self, PyStrings::gGetNoCheck, obj, nullptr);
721 }
722
723 bool inbounds = false;
724 if (idx < 0) idx += size;
725 if (0 <= idx && 0 <= size && idx < size)
726 inbounds = true;
727
728 if (inbounds)
729 return PyObject_CallMethodObjArgs(self, PyStrings::gGetNoCheck, obj, nullptr);
730 else
731 PyErr_SetString( PyExc_IndexError, "index out of range" );
732
733 return nullptr;
734}*/
735
736//- pair as sequence to allow tuple unpacking --------------------------------
737PyObject* PairUnpack(PyObject* self, PyObject* pyindex)
738{
739// For std::map<> iteration, unpack std::pair<>s into tuples for the loop.
740 long idx = PyLong_AsLong(pyindex);
741 if (idx == -1 && PyErr_Occurred())
742 return nullptr;
743
744 if (!CPPInstance_Check(self) || !((CPPInstance*)self)->GetObject()) {
745 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
746 return nullptr;
747 }
748
749 if ((int)idx == 0)
750 return PyObject_GetAttr(self, PyStrings::gFirst);
751 else if ((int)idx == 1)
752 return PyObject_GetAttr(self, PyStrings::gSecond);
753
754// still here? Trigger stop iteration
755 PyErr_SetString(PyExc_IndexError, "out of bounds");
756 return nullptr;
757}
758
759//- simplistic len() functions -----------------------------------------------
760PyObject* ReturnTwo(CPPInstance*, PyObject*) {
761 return PyInt_FromLong(2);
762}
763
764
765//- shared_ptr behavior --------------------------------------------------------
766PyObject* SharedPtrInit(PyObject* self, PyObject* args, PyObject* /* kwds */)
767{
768// since the shared pointer will take ownership, we need to relinquish it
769 PyObject* realInit = PyObject_GetAttrString(self, "__real_init");
770 if (realInit) {
771 PyObject* result = PyObject_Call(realInit, args, nullptr);
772 Py_DECREF(realInit);
773 if (result && PyTuple_GET_SIZE(args) == 1 && CPPInstance_Check(PyTuple_GET_ITEM(args, 0)))
774 PyObject_SetAttrString(PyTuple_GET_ITEM(args, 0), "__python_owns__", Py_False);
775 return result;
776 }
777 return nullptr;
778}
779
780
781//- string behavior as primitives --------------------------------------------
782#if PY_VERSION_HEX >= 0x03000000
783// TODO: this is wrong, b/c it doesn't order
784static int PyObject_Compare(PyObject* one, PyObject* other) {
785 return !PyObject_RichCompareBool(one, other, Py_EQ);
786}
787#endif
788static inline PyObject* CPyCppyy_PyString_FromCppString(std::string* s) {
789 return CPyCppyy_PyText_FromStringAndSize(s->c_str(), s->size());
790}
791
792static inline PyObject* CPyCppyy_PyString_FromCppString(std::wstring* s) {
793 return PyUnicode_FromWideChar(s->c_str(), s->size());
794}
795
796#define CPPYY_IMPL_STRING_PYTHONIZATION(type, name) \
797static PyObject* name##StringGetData(PyObject* self) \
798{ \
799 if (CPyCppyy::CPPInstance_Check(self)) { \
800 type* obj = ((type*)((CPPInstance*)self)->GetObject()); \
801 if (obj) { \
802 return CPyCppyy_PyString_FromCppString(obj); \
803 } else { \
804 return CPPInstance_Type.tp_str(self); \
805 } \
806 } \
807 PyErr_Format(PyExc_TypeError, "object mismatch (%s expected)", #type); \
808 return nullptr; \
809} \
810 \
811PyObject* name##StringRepr(PyObject* self) \
812{ \
813 PyObject* data = name##StringGetData(self); \
814 if (data) { \
815 PyObject* repr = PyObject_Repr(data); \
816 Py_DECREF(data); \
817 return repr; \
818 } \
819 return nullptr; \
820} \
821 \
822PyObject* name##StringIsEqual(PyObject* self, PyObject* obj) \
823{ \
824 PyObject* data = name##StringGetData(self); \
825 if (data) { \
826 PyObject* result = PyObject_RichCompare(data, obj, Py_EQ); \
827 Py_DECREF(data); \
828 return result; \
829 } \
830 return nullptr; \
831} \
832 \
833PyObject* name##StringIsNotEqual(PyObject* self, PyObject* obj) \
834{ \
835 PyObject* data = name##StringGetData(self); \
836 if (data) { \
837 PyObject* result = PyObject_RichCompare(data, obj, Py_NE); \
838 Py_DECREF(data); \
839 return result; \
840 } \
841 return nullptr; \
842}
843
844// Only define StlStringCompare:
845#define CPPYY_IMPL_STRING_PYTHONIZATION_CMP(type, name) \
846CPPYY_IMPL_STRING_PYTHONIZATION(type, name) \
847PyObject* name##StringCompare(PyObject* self, PyObject* obj) \
848{ \
849 PyObject* data = name##StringGetData(self); \
850 int result = 0; \
851 if (data) { \
852 result = PyObject_Compare(data, obj); \
853 Py_DECREF(data); \
854 } \
855 if (PyErr_Occurred()) \
856 return nullptr; \
857 return PyInt_FromLong(result); \
858}
859
861CPPYY_IMPL_STRING_PYTHONIZATION_CMP(std::wstring, StlW)
862
863Py_hash_t StlStringHash(PyObject* self)
864{
865// std::string objects hash to the same values as Python strings to allow
866// matches in dictionaries etc.
867 PyObject* data = StlStringGetData(self);
869 Py_DECREF(data);
870 return h;
871}
872
873
874//- STL iterator behavior ----------------------------------------------------
875PyObject* StlIterNext(PyObject* self)
876{
877// Python iterator protocol __next__ for STL forward iterators.
878 bool mustIncrement = true;
879 PyObject* last = nullptr;
880 if (CPPInstance_Check(self)) {
881 auto& dmc = ((CPPInstance*)self)->GetDatamemberCache();
882 for (auto& p: dmc) {
883 if (p.first == PS_END_ADDR) {
884 last = p.second;
885 Py_INCREF(last);
886 } else if (p.first == PS_FLAG_ADDR) {
887 mustIncrement = p.second == Py_True;
888 if (!mustIncrement) {
889 Py_DECREF(p.second);
890 Py_INCREF(Py_True);
891 p.second = Py_True;
892 }
893 }
894 }
895 }
896
897 PyObject* next = nullptr;
898 if (last) {
899 // handle special case of empty container (i.e. self is end)
900 if (!PyObject_RichCompareBool(last, self, Py_EQ)) {
901 bool iter_valid = true;
902 if (mustIncrement) {
903 // prefer preinc, but allow post-inc; in both cases, it is "self" that has
904 // the updated state to dereference
905 PyObject* iter = PyObject_CallMethodObjArgs(self, PyStrings::gPreInc, nullptr);
906 if (!iter) {
907 PyErr_Clear();
908 static PyObject* dummy = PyInt_FromLong(1l);
909 iter = PyObject_CallMethodObjArgs(self, PyStrings::gPostInc, dummy, nullptr);
910 }
911 iter_valid = iter && PyObject_RichCompareBool(last, self, Py_NE);
912 Py_XDECREF(iter);
913 }
914
915 if (iter_valid) {
916 next = PyObject_CallMethodObjArgs(self, PyStrings::gDeref, nullptr);
917 if (!next) PyErr_Clear();
918 }
919 }
920 Py_DECREF(last);
921 }
922
923 if (!next) PyErr_SetString(PyExc_StopIteration, "");
924 return next;
925}
926
927
928//- STL complex<T> behavior --------------------------------------------------
929#define COMPLEX_METH_GETSET(name, cppname) \
930static PyObject* name##ComplexGet(PyObject* self, void*) { \
931 return PyObject_CallMethodObjArgs(self, cppname, nullptr); \
932} \
933static int name##ComplexSet(PyObject* self, PyObject* value, void*) { \
934 PyObject* result = PyObject_CallMethodObjArgs(self, cppname, value, nullptr);\
935 if (result) { \
936 Py_DECREF(result); \
937 return 0; \
938 } \
939 return -1; \
940} \
941PyGetSetDef name##Complex{(char*)#name, (getter)name##ComplexGet, (setter)name##ComplexSet, nullptr, nullptr};
942
945
946static PyObject* ComplexComplex(PyObject* self) {
947 PyObject* real = PyObject_CallMethodObjArgs(self, PyStrings::gCppReal, nullptr);
948 if (!real) return nullptr;
949 double r = PyFloat_AsDouble(real);
950 Py_DECREF(real);
951 if (r == -1. && PyErr_Occurred())
952 return nullptr;
953
954 PyObject* imag = PyObject_CallMethodObjArgs(self, PyStrings::gCppImag, nullptr);
955 if (!imag) return nullptr;
956 double i = PyFloat_AsDouble(imag);
957 Py_DECREF(imag);
958 if (i == -1. && PyErr_Occurred())
959 return nullptr;
960
961 return PyComplex_FromDoubles(r, i);
962}
963
964static PyObject* ComplexRepr(PyObject* self) {
965 PyObject* real = PyObject_CallMethodObjArgs(self, PyStrings::gCppReal, nullptr);
966 if (!real) return nullptr;
967 double r = PyFloat_AsDouble(real);
968 Py_DECREF(real);
969 if (r == -1. && PyErr_Occurred())
970 return nullptr;
971
972 PyObject* imag = PyObject_CallMethodObjArgs(self, PyStrings::gCppImag, nullptr);
973 if (!imag) return nullptr;
974 double i = PyFloat_AsDouble(imag);
975 Py_DECREF(imag);
976 if (i == -1. && PyErr_Occurred())
977 return nullptr;
978
979 std::ostringstream s;
980 s << '(' << r << '+' << i << "j)";
981 return CPyCppyy_PyText_FromString(s.str().c_str());
982}
983
984static PyObject* ComplexDRealGet(CPPInstance* self, void*)
985{
986 return PyFloat_FromDouble(((std::complex<double>*)self->GetObject())->real());
987}
988
989static int ComplexDRealSet(CPPInstance* self, PyObject* value, void*)
990{
991 double d = PyFloat_AsDouble(value);
992 if (d == -1.0 && PyErr_Occurred())
993 return -1;
994 ((std::complex<double>*)self->GetObject())->real(d);
995 return 0;
996}
997
998PyGetSetDef ComplexDReal{(char*)"real", (getter)ComplexDRealGet, (setter)ComplexDRealSet, nullptr, nullptr};
999
1000
1001static PyObject* ComplexDImagGet(CPPInstance* self, void*)
1002{
1003 return PyFloat_FromDouble(((std::complex<double>*)self->GetObject())->imag());
1004}
1005
1006static int ComplexDImagSet(CPPInstance* self, PyObject* value, void*)
1007{
1008 double d = PyFloat_AsDouble(value);
1009 if (d == -1.0 && PyErr_Occurred())
1010 return -1;
1011 ((std::complex<double>*)self->GetObject())->imag(d);
1012 return 0;
1013}
1014
1015PyGetSetDef ComplexDImag{(char*)"imag", (getter)ComplexDImagGet, (setter)ComplexDImagSet, nullptr, nullptr};
1016
1017static PyObject* ComplexDComplex(CPPInstance* self)
1018{
1019 double r = ((std::complex<double>*)self->GetObject())->real();
1020 double i = ((std::complex<double>*)self->GetObject())->imag();
1021 return PyComplex_FromDoubles(r, i);
1022}
1023
1024
1025} // unnamed namespace
1026
1027
1028//- public functions ---------------------------------------------------------
1029namespace CPyCppyy {
1030 std::set<std::string> gIteratorTypes;
1031}
1032
1033bool CPyCppyy::Pythonize(PyObject* pyclass, const std::string& name)
1034{
1035// Add pre-defined pythonizations (for STL and ROOT) to classes based on their
1036// signature and/or class name.
1037 if (!pyclass)
1038 return false;
1039
1040 CPPScope* klass = (CPPScope*)pyclass;
1041
1042//- method name based pythonization ------------------------------------------
1043
1044// for smart pointer style classes that are otherwise not known as such; would
1045// prefer operator-> as that returns a pointer (which is simpler since it never
1046// has to deal with ref-assignment), but operator* plays better with STL iters
1047// and algorithms
1048 if (HasAttrDirect(pyclass, PyStrings::gDeref) && !Cppyy::IsSmartPtr(klass->fCppType))
1049 Utility::AddToClass(pyclass, "__getattr__", (PyCFunction)DeRefGetAttr, METH_O);
1050 else if (HasAttrDirect(pyclass, PyStrings::gFollow) && !Cppyy::IsSmartPtr(klass->fCppType))
1051 Utility::AddToClass(pyclass, "__getattr__", (PyCFunction)FollowGetAttr, METH_O);
1052
1053// for STL containers, and user classes modeled after them
1054 if (HasAttrDirect(pyclass, PyStrings::gSize))
1055 Utility::AddToClass(pyclass, "__len__", "size");
1056
1057 if (!IsTemplatedSTLClass(name, "vector") && // vector is dealt with below
1058 !((PyTypeObject*)pyclass)->tp_iter) {
1059 if (HasAttrDirect(pyclass, PyStrings::gBegin) && HasAttrDirect(pyclass, PyStrings::gEnd)) {
1060 // obtain the name of the return type
1061 const auto& v = Cppyy::GetMethodIndicesFromName(klass->fCppType, "begin");
1062 if (!v.empty()) {
1063 // check return type; if not explicitly an iterator, add it to the "known" return
1064 // types to add the "next" method on use
1066 const std::string& resname = Cppyy::GetMethodResultType(meth);
1067 if (Cppyy::GetScope(resname)) {
1068 if (resname.find("iterator") == std::string::npos)
1069 gIteratorTypes.insert(resname);
1070
1071 // install iterator protocol a la STL
1072 ((PyTypeObject*)pyclass)->tp_iter = (getiterfunc)StlSequenceIter;
1073 Utility::AddToClass(pyclass, "__iter__", (PyCFunction)StlSequenceIter, METH_NOARGS);
1074 }
1075 }
1076 }
1077 if (!((PyTypeObject*)pyclass)->tp_iter && // no iterator resolved
1078 HasAttrDirect(pyclass, PyStrings::gGetItem) && HasAttrDirect(pyclass, PyStrings::gLen)) {
1079 // Python will iterate over __getitem__ using integers, but C++ operator[] will never raise
1080 // a StopIteration. A checked getitem (raising IndexError if beyond size()) works in some
1081 // cases but would mess up if operator[] is meant to implement an associative container. So,
1082 // this has to be implemented as an interator protocol.
1083 ((PyTypeObject*)pyclass)->tp_iter = (getiterfunc)index_iter;
1084 Utility::AddToClass(pyclass, "__iter__", (PyCFunction)index_iter, METH_NOARGS);
1085 }
1086 }
1087
1088// operator==/!= are used in op_richcompare of CPPInstance, which subsequently allows
1089// comparisons to None; if no operator is available, a hook is installed for lazy
1090// lookups in the global and/or class namespace
1091 if (HasAttrDirect(pyclass, PyStrings::gEq, true)) {
1092 PyObject* cppol = PyObject_GetAttr(pyclass, PyStrings::gEq);
1093 if (!klass->fOperators) klass->fOperators = new Utility::PyOperators();
1094 klass->fOperators->fEq = cppol;
1095 // re-insert the forwarding __eq__ from the CPPInstance in case there was a Python-side
1096 // override in the base class
1097 static PyObject* top_eq = nullptr;
1098 if (!top_eq) {
1099 PyObject* top_cls = PyObject_GetAttrString(gThisModule, "CPPInstance");
1100 top_eq = PyObject_GetAttr(top_cls, PyStrings::gEq);
1101 Py_DECREF(top_eq); // make it borrowed
1102 Py_DECREF(top_cls);
1103 }
1104 PyObject_SetAttr(pyclass, PyStrings::gEq, top_eq);
1105 }
1106
1107 if (HasAttrDirect(pyclass, PyStrings::gNe, true)) {
1108 PyObject* cppol = PyObject_GetAttr(pyclass, PyStrings::gNe);
1109 if (!klass->fOperators) klass->fOperators = new Utility::PyOperators();
1110 klass->fOperators->fNe = cppol;
1111 // re-insert the forwarding __ne__ (same reason as above for __eq__)
1112 static PyObject* top_ne = nullptr;
1113 if (!top_ne) {
1114 PyObject* top_cls = PyObject_GetAttrString(gThisModule, "CPPInstance");
1115 top_ne = PyObject_GetAttr(top_cls, PyStrings::gNe);
1116 Py_DECREF(top_ne); // make it borrowed
1117 Py_DECREF(top_cls);
1118 }
1119 PyObject_SetAttr(pyclass, PyStrings::gNe, top_ne);
1120 }
1121
1122
1123//- class name based pythonization -------------------------------------------
1124
1125 if (IsTemplatedSTLClass(name, "vector")) {
1126
1127 // std::vector<bool> is a special case in C++
1128 if (!sVectorBoolTypeID) sVectorBoolTypeID = (Cppyy::TCppType_t)Cppyy::GetScope("std::vector<bool>");
1129 if (klass->fCppType == sVectorBoolTypeID) {
1130 Utility::AddToClass(pyclass, "__getitem__", (PyCFunction)VectorBoolGetItem, METH_O);
1131 Utility::AddToClass(pyclass, "__setitem__", (PyCFunction)VectorBoolSetItem);
1132 } else {
1133 // constructor that takes python collections
1134 Utility::AddToClass(pyclass, "__real_init", "__init__");
1135 Utility::AddToClass(pyclass, "__init__", (PyCFunction)VectorInit, METH_VARARGS | METH_KEYWORDS);
1136
1137 // data with size
1138 Utility::AddToClass(pyclass, "__real_data", "data");
1139 Utility::AddToClass(pyclass, "data", (PyCFunction)VectorData);
1140
1141 // checked getitem
1142 if (HasAttrDirect(pyclass, PyStrings::gLen)) {
1143 Utility::AddToClass(pyclass, "_getitem__unchecked", "__getitem__");
1144 Utility::AddToClass(pyclass, "__getitem__", (PyCFunction)VectorGetItem, METH_O);
1145 }
1146
1147 // vector-optimized iterator protocol
1148 ((PyTypeObject*)pyclass)->tp_iter = (getiterfunc)vector_iter;
1149
1150 // helpers for iteration
1151 const std::string& vtype = Cppyy::ResolveName(name+"::value_type");
1152 size_t typesz = Cppyy::SizeOf(vtype);
1153 if (typesz) {
1154 PyObject* pyvalue_size = PyLong_FromSsize_t(typesz);
1155 PyObject_SetAttrString(pyclass, "value_size", pyvalue_size);
1156 Py_DECREF(pyvalue_size);
1157
1158 PyObject* pyvalue_type = CPyCppyy_PyText_FromString(vtype.c_str());
1159 PyObject_SetAttrString(pyclass, "value_type", pyvalue_type);
1160 Py_DECREF(pyvalue_type);
1161 }
1162 }
1163 }
1164
1165 else if (IsTemplatedSTLClass(name, "map")) {
1166 Utility::AddToClass(pyclass, "__contains__", (PyCFunction)MapContains, METH_O);
1167 }
1168
1169 else if (IsTemplatedSTLClass(name, "pair")) {
1170 Utility::AddToClass(pyclass, "__getitem__", (PyCFunction)PairUnpack, METH_O);
1171 Utility::AddToClass(pyclass, "__len__", (PyCFunction)ReturnTwo, METH_NOARGS);
1172 }
1173
1174 if (IsTemplatedSTLClass(name, "shared_ptr")) {
1175 Utility::AddToClass(pyclass, "__real_init", "__init__");
1176 Utility::AddToClass(pyclass, "__init__", (PyCFunction)SharedPtrInit, METH_VARARGS | METH_KEYWORDS);
1177 }
1178
1179 else if (name.find("iterator") != std::string::npos || gIteratorTypes.find(name) != gIteratorTypes.end()) {
1180 ((PyTypeObject*)pyclass)->tp_iternext = (iternextfunc)StlIterNext;
1181 Utility::AddToClass(pyclass, CPPYY__next__, (PyCFunction)StlIterNext, METH_NOARGS);
1182 ((PyTypeObject*)pyclass)->tp_iter = (getiterfunc)PyObject_SelfIter;
1183 Utility::AddToClass(pyclass, "__iter__", (PyCFunction)PyObject_SelfIter, METH_NOARGS);
1184 }
1185
1186 else if (name == "string" || name == "std::string") { // TODO: ask backend as well
1187 Utility::AddToClass(pyclass, "__repr__", (PyCFunction)StlStringRepr, METH_NOARGS);
1188 Utility::AddToClass(pyclass, "__str__", (PyCFunction)StlStringGetData, METH_NOARGS);
1189 Utility::AddToClass(pyclass, "__cmp__", (PyCFunction)StlStringCompare, METH_O);
1190 Utility::AddToClass(pyclass, "__eq__", (PyCFunction)StlStringIsEqual, METH_O);
1191 Utility::AddToClass(pyclass, "__ne__", (PyCFunction)StlStringIsNotEqual, METH_O);
1192 ((PyTypeObject*)pyclass)->tp_hash = (hashfunc)StlStringHash;
1193 }
1194
1195 else if (name == "basic_string<wchar_t,char_traits<wchar_t>,allocator<wchar_t> >" || \
1196 name == "std::basic_string<wchar_t,char_traits<wchar_t>,allocator<wchar_t> >") {
1197 Utility::AddToClass(pyclass, "__repr__", (PyCFunction)StlWStringRepr, METH_NOARGS);
1198 Utility::AddToClass(pyclass, "__str__", (PyCFunction)StlWStringGetData, METH_NOARGS);
1199 Utility::AddToClass(pyclass, "__cmp__", (PyCFunction)StlWStringCompare, METH_O);
1200 Utility::AddToClass(pyclass, "__eq__", (PyCFunction)StlWStringIsEqual, METH_O);
1201 Utility::AddToClass(pyclass, "__ne__", (PyCFunction)StlWStringIsNotEqual, METH_O);
1202 }
1203
1204 else if (name == "complex<double>" || name == "std::complex<double>") {
1205 Utility::AddToClass(pyclass, "__cpp_real", "real");
1206 PyObject_SetAttrString(pyclass, "real", PyDescr_NewGetSet((PyTypeObject*)pyclass, &ComplexDReal));
1207 Utility::AddToClass(pyclass, "__cpp_imag", "imag");
1208 PyObject_SetAttrString(pyclass, "imag", PyDescr_NewGetSet((PyTypeObject*)pyclass, &ComplexDImag));
1209 Utility::AddToClass(pyclass, "__complex__", (PyCFunction)ComplexDComplex, METH_NOARGS);
1210 Utility::AddToClass(pyclass, "__repr__", (PyCFunction)ComplexRepr, METH_NOARGS);
1211 }
1212
1213 else if (IsTemplatedSTLClass(name, "complex")) {
1214 Utility::AddToClass(pyclass, "__cpp_real", "real");
1215 PyObject_SetAttrString(pyclass, "real", PyDescr_NewGetSet((PyTypeObject*)pyclass, &realComplex));
1216 Utility::AddToClass(pyclass, "__cpp_imag", "imag");
1217 PyObject_SetAttrString(pyclass, "imag", PyDescr_NewGetSet((PyTypeObject*)pyclass, &imagComplex));
1218 Utility::AddToClass(pyclass, "__complex__", (PyCFunction)ComplexComplex, METH_NOARGS);
1219 Utility::AddToClass(pyclass, "__repr__", (PyCFunction)ComplexRepr, METH_NOARGS);
1220 }
1221
1222// direct user access; there are two calls here:
1223// - explicit pythonization: won't fall through to the base classes and is preferred if present
1224// - normal pythonization: only called if explicit isn't present, falls through to base classes
1225 bool bUserOk = true; PyObject* res = nullptr;
1227 if (HasAttrDirect(pyclass, PyStrings::gExPythonize)) {
1228 res = PyObject_CallMethodObjArgs(pyclass, PyStrings::gExPythonize, pyclass, pyname, nullptr);
1229 bUserOk = (bool)res;
1230 } else {
1231 PyObject* func = PyObject_GetAttr(pyclass, PyStrings::gPythonize);
1232 if (func) {
1233 res = PyObject_CallFunctionObjArgs(func, pyclass, pyname, nullptr);
1234 Py_DECREF(func);
1235 bUserOk = (bool)res;
1236 } else
1237 PyErr_Clear();
1238 }
1239 if (!bUserOk) {
1240 Py_DECREF(pyname);
1241 return false;
1242 } else {
1243 Py_XDECREF(res);
1244 // pyname handed to tuple below
1245 }
1246
1247// call registered pythonizors, if any
1248 PyObject* args = PyTuple_New(2);
1249 Py_INCREF(pyclass);
1250 PyTuple_SET_ITEM(args, 0, pyclass);
1251
1252 std::string outer_scope = TypeManip::extract_namespace(name);
1253
1254 bool pstatus = true;
1255 auto p = outer_scope.empty() ? gPythonizations.end() : gPythonizations.find(outer_scope);
1256 if (p == gPythonizations.end()) {
1257 p = gPythonizations.find("");
1258 PyTuple_SET_ITEM(args, 1, pyname);
1259 } else {
1260 PyTuple_SET_ITEM(args, 1, CPyCppyy_PyText_FromString(
1261 name.substr(outer_scope.size()+2, std::string::npos).c_str()));
1262 Py_DECREF(pyname);
1263 }
1264
1265 if (p != gPythonizations.end()) {
1266 for (auto pythonizor : p->second) {
1267 PyObject* result = PyObject_CallObject(pythonizor, args);
1268 if (!result) {
1269 // TODO: detail error handling for the pythonizors
1270 pstatus = false;
1271 break;
1272 }
1273 Py_DECREF(result);
1274 }
1275 }
1276
1277 Py_DECREF(args);
1278
1279// phew! all done ...
1280 return pstatus;
1281}
#define Py_TYPE(ob)
Definition CPyCppyy.h:217
#define Py_RETURN_TRUE
Definition CPyCppyy.h:293
#define Py_RETURN_FALSE
Definition CPyCppyy.h:297
#define PyInt_FromSsize_t
Definition CPyCppyy.h:238
#define CPyCppyy_PyText_FromStringAndSize
Definition CPyCppyy.h:106
#define PyBytes_Check
Definition CPyCppyy.h:83
#define PyInt_AsSsize_t
Definition CPyCppyy.h:237
#define CPyCppyy_PySliceCast
Definition CPyCppyy.h:210
#define CPyCppyy_PyText_AsString
Definition CPyCppyy.h:97
long Py_hash_t
Definition CPyCppyy.h:135
#define Py_RETURN_NONE
Definition CPyCppyy.h:289
#define CPyCppyy_PyText_Type
Definition CPyCppyy.h:115
#define CPPYY__next__
Definition CPyCppyy.h:133
#define CPyCppyy_PyText_FromString
Definition CPyCppyy.h:102
#define CPyCppyy_PyText_Check
Definition CPyCppyy.h:95
_object PyObject
#define CPPYY_IMPL_STRING_PYTHONIZATION_CMP(type, name)
#define COMPLEX_METH_GETSET(name, cppname)
#define PyObject_LengthHint
PyObject * CallPyObjMethod(PyObject *obj, const char *meth)
Set of helper functions that are invoked from the C++ implementation of pythonizations.
#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
static PyObject * PyStyleIndex(PyObject *self, PyObject *index)
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
#define pyname
Cppyy::TCppType_t ObjectIsA(bool check_smart=true) const
Utility::PyOperators * fOperators
Definition CPPScope.h:56
Cppyy::TCppType_t fCppType
Definition CPPScope.h:50
PyObject * gCTypesType
Definition PyStrings.cxx:31
PyObject * gExPythonize
Definition PyStrings.cxx:60
PyObject * gGetItem
Definition PyStrings.cxx:19
PyObject * gCppReal
Definition PyStrings.cxx:52
PyObject * gPythonize
Definition PyStrings.cxx:61
PyObject * gTypeCode
Definition PyStrings.cxx:30
PyObject * gPostInc
Definition PyStrings.cxx:14
PyObject * gCppImag
Definition PyStrings.cxx:53
PyObject * gGetNoCheck
Definition PyStrings.cxx:20
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:679
bool AddToClass(PyObject *pyclass, const char *label, PyCFunction cfunc, int flags=METH_VARARGS)
Definition Utility.cxx:170
Set of helper functions that are invoked from the pythonizors, on the Python side.
PyTypeObject VectorIter_Type
bool Pythonize(PyObject *pyclass, const std::string &name)
bool CPPOverload_Check(T *object)
Definition CPPOverload.h:83
std::map< std::string, std::vector< PyObject * > > gPythonizations
bool LowLevelView_Check(T *object)
bool CPPInstance_Check(T *object)
PyTypeObject IndexIter_Type
R__EXTERN PyObject * gThisModule
Definition TPython.cxx:100
std::set< std::string > gIteratorTypes
CPYCPPYY_EXTERN Converter * CreateConverter(const std::string &name, Py_ssize_t *dims=nullptr)
RPY_EXPORTED size_t SizeOf(TCppType_t klass)
intptr_t TCppMethod_t
Definition cpp_cppyy.h:22
RPY_EXPORTED std::vector< TCppIndex_t > GetMethodIndicesFromName(TCppScope_t scope, const std::string &name)
RPY_EXPORTED std::string ResolveName(const std::string &cppitem_name)
TCppScope_t TCppType_t
Definition cpp_cppyy.h:19
RPY_EXPORTED std::string GetScopedFinalName(TCppType_t type)
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)
RPY_EXPORTED std::string GetMethodResultType(TCppMethod_t)
PyObject_HEAD PyObject * ii_container
Cppyy::TCppType_t vi_klass
CPyCppyy::Converter * vi_converter