Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
Converters.cxx
Go to the documentation of this file.
1// Bindings
2#include "CPyCppyy.h"
3#include "DeclareConverters.h"
4#include "CallContext.h"
5#include "CPPExcInstance.h"
6#include "CPPInstance.h"
7#include "CPPOverload.h"
8#include "CustomPyTypes.h"
9#include "LowLevelViews.h"
10#include "MemoryRegulator.h"
11#include "ProxyWrappers.h"
12#include "PyStrings.h"
13#include "TemplateProxy.h"
14#include "TupleOfInstances.h"
15#include "TypeManip.h"
16#include "Utility.h"
17
18// Standard
19#include <cassert>
20#include <complex>
21#include <limits.h>
22#include <stddef.h> // for ptrdiff_t
23#include <string.h>
24#include <algorithm>
25#include <array>
26#include <locale> // for wstring_convert
27#include <regex>
28#include <utility>
29#include <sstream>
30#include <cstddef>
31#include <string_view>
32#if __cplusplus >= 202002L
33#include <span>
34#endif
35
36// codecvt does not exist for gcc4.8.5 and is in principle deprecated; it is
37// only used in py2 for char -> wchar_t conversion for std::wstring; if not
38// available, the conversion is done through Python (requires an extra copy)
39#if PY_VERSION_HEX < 0x03000000
40#if defined(__GNUC__) && !defined(__APPLE__)
41# if __GNUC__ > 4 && __has_include("codecvt")
42# include <codecvt>
43# define HAS_CODECVT 1
44# endif
45#else
46#include <codecvt>
47#define HAS_CODECVT 1
48#endif
49#endif // py2
50
51
52//- data _____________________________________________________________________
53namespace CPyCppyy {
54
55// factories
56 typedef std::map<std::string, cf_t> ConvFactories_t;
58
59// special objects
62
63// regular expression for matching function pointer
64 static std::regex s_fnptr("\\((\\w*:*)*\\*&*\\)");
65}
66
67// Define our own PyUnstable_Object_IsUniqueReferencedTemporary function if the
68// Python version is lower than 3.14, the version where that function got introduced.
69#if PY_VERSION_HEX < 0x030e0000
70#if PY_VERSION_HEX < 0x03000000
72#elif PY_VERSION_HEX < 0x03080000
73// p3 has at least 2 ref-counts, as contrary to p2, it will create a descriptor
74// copy for the method holding self in the case of __init__; but there can also
75// be a reference held by the frame object, which is indistinguishable from a
76// local variable reference, so the cut-off has to remain 2.
78#else
79// since py3.8, vector calls behave again as expected
81#endif
85#endif
86
87//- pretend-ctypes helpers ---------------------------------------------------
88struct CPyCppyy_tagCDataObject { // non-public (but stable)
90 char* b_ptr;
92};
93
94struct CPyCppyy_tagPyCArgObject { // not public (but stable; note that older
95 PyObject_HEAD // Pythons protect 'D' with HAVE_LONG_LONG)
96 void* pffi_type;
97 char tag;
98 union { // for convenience, kept only relevant vals
99 long long q;
100 long double D;
101 void *p;
104};
105
106// indices of ctypes types into the array caches (note that c_complex and c_fcomplex
107// do not exist as types in ctypes)
108#define ct_c_bool 0
109#define ct_c_char 1
110#define ct_c_shar 1
111#define ct_c_wchar 2
112#define ct_c_byte 3
113#define ct_c_int8 3
114#define ct_c_ubyte 4
115#define ct_c_uchar 4
116#define ct_c_uint8 4
117#define ct_c_short 5
118#define ct_c_ushort 6
119#define ct_c_uint16 7
120#define ct_c_int 8
121#define ct_c_uint 9
122#define ct_c_uint32 10
123#define ct_c_long 11
124#define ct_c_ulong 12
125#define ct_c_longlong 13
126#define ct_c_ulonglong 14
127#define ct_c_float 15
128#define ct_c_double 16
129#define ct_c_longdouble 17
130#define ct_c_char_p 18
131#define ct_c_wchar_p 19
132#define ct_c_void_p 20
133#define ct_c_fcomplex 21
134#define ct_c_complex 22
135#define ct_c_pointer 23
136#define ct_c_funcptr 24
137#define ct_c_int16 25
138#define ct_c_int32 26
139#define NTYPES 27
140
141static std::array<const char*, NTYPES> gCTypesNames = {
142 "c_bool", "c_char", "c_wchar", "c_byte", "c_ubyte", "c_short", "c_ushort", "c_uint16",
143 "c_int", "c_uint", "c_uint32", "c_long", "c_ulong", "c_longlong", "c_ulonglong",
144 "c_float", "c_double", "c_longdouble",
145 "c_char_p", "c_wchar_p", "c_void_p", "c_fcomplex", "c_complex",
146 "_Pointer", "_CFuncPtr" };
147static std::array<PyTypeObject*, NTYPES> gCTypesTypes;
148static std::array<PyTypeObject*, NTYPES> gCTypesPtrTypes;
149
150// Both GetCTypesType and GetCTypesPtrType, rely on the ctypes module itself
151// caching the types (thus also making them unique), so no ref-count is needed.
152// Further, by keeping a ref-count on the module, it won't be off-loaded until
153// the 2nd cleanup cycle.
155{
156 static PyObject* ctmod = PyImport_ImportModule("ctypes"); // ref-count kept
157 if (!ctmod) {
158 PyErr_Clear();
159 return nullptr;
160 }
162 if (!ct_t) {
164 if (!ct_t) PyErr_Clear();
165 else {
168 }
169 }
170 return ct_t;
171}
172
174{
175 static PyObject* ctmod = PyImport_ImportModule("ctypes"); // ref-count kept
176 if (!ctmod) {
177 PyErr_Clear();
178 return nullptr;
179 }
181 if (!cpt_t) {
182 if (strcmp(gCTypesNames[nidx], "c_char") == 0) {
184 } else {
186 if (ct_t) {
190 }
191 }
192 if (cpt_t) {
195 }
196 }
197 return cpt_t;
198}
199
201{
202 static PyTypeObject* pycarg_type = nullptr;
203 if (!pycarg_type) {
205 if (!ctmod) PyErr_Clear();
206 else {
208 PyObject* cobj = ct_t->tp_new(ct_t, nullptr, nullptr);
212 pycarg_type = Py_TYPE(pyptr); // static, no ref-count needed
215 }
216 }
217 return Py_TYPE(pyobject) == pycarg_type;
218}
219
220#if PY_VERSION_HEX < 0x30d0000
222{
223 static PyTypeObject* cstgdict_type = nullptr;
224 if (!cstgdict_type) {
225 // get any pointer type to initialize the extended dictionary type
227 if (ct_int && ct_int->tp_dict) {
228 cstgdict_type = Py_TYPE(ct_int->tp_dict);
229 }
230 }
231
233 if (pytype->tp_dict && Py_TYPE(pytype->tp_dict) == cstgdict_type)
234 return true;
235 return false;
236}
237#else
238// the internals of ctypes have been redone, requiring a more complex checking
239namespace {
240
241typedef struct {
251// ... unused fields omitted ...
253
254} // unnamed namespace
255
257{
258 static _cppyy_ctypes_state* state = nullptr;
259 if (!state) {
260 PyObject* ctmod = PyImport_AddModule("_ctypes"); // the extension module, not the Python one
261 if (ctmod)
263 }
264
265 // verify for object types that have a C payload
266 if (state && (PyObject_IsInstance((PyObject*)Py_TYPE(pyobject), (PyObject*)state->PyCType_Type) ||
267 PyObject_IsInstance((PyObject*)Py_TYPE(pyobject), (PyObject*)state->PyCPointerType_Type))) {
268 return true;
269 }
270
271 return false;
272}
273#endif
274
275
276//- helper to establish life lines -------------------------------------------
277static inline bool SetLifeLine(PyObject* holder, PyObject* target, intptr_t ref)
278{
279// set a lifeline from on the holder to the target, using the ref as label
280 if (!holder) return false;
281
282// 'ref' is expected to be the converter address or data memory location, so
283// that the combination of holder and ref is unique, but also identifiable for
284// reuse when the C++ side is being overwritten
285 std::ostringstream attr_name;
286 attr_name << "__" << ref;
287 auto res = PyObject_SetAttrString(holder, (char*)attr_name.str().c_str(), target);
288 return res != -1;
289}
290
291static bool HasLifeLine(PyObject* holder, intptr_t ref)
292{
293// determine if a lifeline was previously set for the ref on the holder
294 if (!holder) return false;
295
296 std::ostringstream attr_name;
297 attr_name << "__" << ref;
298 PyObject* res = PyObject_GetAttrString(holder, (char*)attr_name.str().c_str());
299
300 if (res) {
301 Py_DECREF(res);
302 return true;
303 }
304
305 PyErr_Clear();
306 return false;
307}
308
309
310//- helper to work with both CPPInstance and CPPExcInstance ------------------
313{
314 using namespace CPyCppyy;
316 return (CPPInstance*)pyobject;
318 return (CPPInstance*)((CPPExcInstance*)pyobject)->fCppInstance;
319
320// this is not a C++ proxy; allow custom cast to C++
322 if (castobj) {
324 return (CPPInstance*)castobj;
325 else if (klass && PyTuple_CheckExact(castobj)) {
326 // allow implicit conversion from a tuple of arguments
328 if (pyclass) {
332 if (accept_rvalue)
333 pytmp->fFlags |= CPPInstance::kIsRValue;
335 return pytmp;
336 }
338 }
339 }
340
342 return nullptr;
343 }
344
345 PyErr_Clear();
346 return nullptr;
347}
348
349
350//- custom helpers to check ranges -------------------------------------------
352{
353 using namespace CPyCppyy;
356 return false;
357 }
358 return true;
359}
360
362{
363 using namespace CPyCppyy;
366 return false;
367 }
368 return true;
369}
370
372{
373// range-checking python integer to C++ bool conversion
374 long l = PyLong_AsLong(pyobject);
375// fail to pass float -> bool; the problem is rounding (0.1 -> 0 -> False)
376 if (!(l == 0|| l == 1) || PyFloat_Check(pyobject)) {
377 PyErr_SetString(PyExc_ValueError, "boolean value should be bool, or integer 1 or 0");
378 return (bool)-1;
379 }
380 return (bool)l;
381}
382
383
384// range-checking python integer to C++ integer conversion (prevents p2.7 silent conversions)
385#define CPPYY_PYLONG_AS_TYPE(name, type, limit_low, limit_high) \
386static inline type CPyCppyy_PyLong_As##name(PyObject* pyobject) \
387{ \
388 if (!(PyLong_Check(pyobject) || PyInt_Check(pyobject))) { \
389 if (pyobject == CPyCppyy::gDefaultObject) \
390 return (type)0; \
391 PyErr_SetString(PyExc_TypeError, #type" conversion expects an integer object");\
392 return (type)-1; \
393 } \
394 long l = PyLong_AsLong(pyobject); \
395 if (l < limit_low || limit_high < l) { \
396 PyErr_Format(PyExc_ValueError, "integer %ld out of range for "#type, l);\
397 return (type)-1; \
398 } \
399 return (type)l; \
400}
401
408CPPYY_PYLONG_AS_TYPE(UShort, unsigned short, 0, USHRT_MAX)
411
413{
414// strict python integer to C++ long integer conversion
415
416// prevent float -> long (see CPyCppyy_PyLong_AsStrictInt)
419 return (long)0;
420 PyErr_SetString(PyExc_TypeError, "int/long conversion expects an integer object");
421 return (long)-1;
422 }
423
424 return (long)PyLong_AsLong(pyobject); // already does long range check
425}
426
428{
429// strict python integer to C++ long long integer conversion
430
431// prevent float -> long (see CPyCppyy_PyLong_AsStrictInt)
434 return (PY_LONG_LONG)0;
435 PyErr_SetString(PyExc_TypeError, "int/long conversion expects an integer object");
436 return (PY_LONG_LONG)-1;
437 }
438
439 return PyLong_AsLongLong(pyobject); // already does long range check
440}
441
442
443//- helper for pointer/array/reference conversions ---------------------------
444static inline bool CArraySetArg(
445 PyObject* pyobject, CPyCppyy::Parameter& para, char tc, int size, bool check=true)
446{
447// Case of LowLevelView. In general, they also implement the buffer protocol,
448// but for views around nullptr or C-style arrays without size info the buffer
449// protocol implementation is incomplete and PyObject_GetBuffer will fail.
452 if (llview->fBufInfo.itemsize != size || !strchr(llview->fBufInfo.format, tc)) {
454 "could not convert argument to buffer or nullptr");
455 return false;
456 }
457
458 para.fValue.fVoidp = llview->get_buf();
459 }
460// general case of loading a C array pointer (void* + type code) as function argument
462 para.fValue.fVoidp = nullptr;
463 else {
465 if (!buflen) {
466 // stuck here as it's the least common
468 para.fValue.fVoidp = nullptr;
469 else {
470 PyErr_Format(PyExc_TypeError, // ValueError?
471 "could not convert argument to buffer or nullptr");
472 return false;
473 }
474 }
475 }
476 para.fTypeCode = 'p';
477 return true;
478}
479
480
481//- helper for implicit conversions ------------------------------------------
484{
485 using namespace CPyCppyy;
486
487// filter out copy and move constructors
488 if (IsConstructor(ctxt->fFlags) && klass == ctxt->fCurScope && ctxt->GetSize() == 1)
489 return nullptr;
490
491// only proceed if implicit conversions are allowed (in "round 2") or if the
492// argument is exactly a tuple or list, as these are the equivalent of
493// initializer lists and thus "syntax" not a conversion
494 if (!AllowImplicit(ctxt)) {
496 if (!(pytype == &PyList_Type || pytype == &PyTuple_Type)) {// || !CPPInstance_Check(pyobject))) {
498 return nullptr;
499 }
500 }
501
502// exercise implicit conversion
504 if (!CPPScope_Check(pyscope)) {
506 return nullptr;
507 }
508
509// call constructor of argument type to attempt implicit conversion (disallow any
510// implicit conversions by the scope's constructor itself)
511 PyObject* args = PyTuple_New(1);
513
517 // special case: allow implicit conversion from given set of arguments in tuple
518 PyErr_Clear();
520 }
521 ((CPPScope*)pyscope)->fFlags &= ~CPPScope::kNoImplicit;
522
523 Py_DECREF(args);
525
526 if (pytmp) {
527 // implicit conversion succeeded!
528 if (manage) ctxt->AddTemporary((PyObject*)pytmp);
529 para.fValue.fVoidp = pytmp->GetObjectRaw();
530 para.fTypeCode = 'V';
531 return pytmp;
532 }
533
534 PyErr_Clear();
535 return nullptr;
536}
537
538
539//- base converter implementation --------------------------------------------
541{
542 /* empty */
543}
544
545//----------------------------------------------------------------------------
547{
548// could happen if no derived class override
549 PyErr_SetString(PyExc_TypeError, "C++ type cannot be converted from memory");
550 return nullptr;
551}
552
553//----------------------------------------------------------------------------
555{
556// could happen if no derived class override
557 PyErr_SetString(PyExc_TypeError, "C++ type cannot be converted to memory");
558 return false;
559}
560
561
562//- helper macro's -----------------------------------------------------------
563#define CPPYY_IMPL_BASIC_CONVERTER_BODY(name, type, stype, ctype, F1, F2, tc)\
564/* convert <pyobject> to C++ 'type', set arg for call */ \
565 type val = (type)F2(pyobject); \
566 if (val == (type)-1 && PyErr_Occurred()) { \
567 static PyTypeObject* ctypes_type = nullptr; \
568 if (!ctypes_type) { \
569 auto error = CPyCppyy::Utility::FetchPyError(); \
570 ctypes_type = GetCTypesType(ct_##ctype); \
571 CPyCppyy::Utility::RestorePyError(error); \
572 } \
573 if (Py_TYPE(pyobject) == ctypes_type) { \
574 PyErr_Clear(); \
575 val = *((type*)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr); \
576 } else if (pyobject == CPyCppyy::gDefaultObject) { \
577 PyErr_Clear(); \
578 val = (type)0; \
579 } else \
580 return false; \
581 } \
582 para.fValue.f##name = val; \
583 para.fTypeCode = tc; \
584 return true;
585
586#define CPPYY_IMPL_BASIC_CONVERTER_METHODS(name, type, stype, ctype, F1, F2) \
587PyObject* CPyCppyy::name##Converter::FromMemory(void* address) \
588{ \
589 return F1((stype)*((type*)address)); \
590} \
591 \
592bool CPyCppyy::name##Converter::ToMemory( \
593 PyObject* value, void* address, PyObject* /* ctxt */) \
594{ \
595 type s = (type)F2(value); \
596 if (s == (type)-1 && PyErr_Occurred()) { \
597 if (value == CPyCppyy::gDefaultObject) { \
598 PyErr_Clear(); \
599 s = (type)0; \
600 } else \
601 return false; \
602 } \
603 *((type*)address) = (type)s; \
604 return true; \
605}
606
607#define CPPYY_IMPL_BASIC_CONVERTER_NI(name, type, stype, ctype, F1, F2, tc) \
608bool CPyCppyy::name##Converter::SetArg( \
609 PyObject* pyobject, Parameter& para, CallContext* ctxt) \
610{ \
611 if (!StrictBool(pyobject, ctxt)) \
612 return false; \
613 CPPYY_IMPL_BASIC_CONVERTER_BODY(name, type, stype, ctype, F1, F2, tc) \
614} \
615CPPYY_IMPL_BASIC_CONVERTER_METHODS(name, type, stype, ctype, F1, F2)
616
617#define CPPYY_IMPL_BASIC_CONVERTER_IB(name, type, stype, ctype, F1, F2, tc) \
618bool CPyCppyy::name##Converter::SetArg( \
619 PyObject* pyobject, Parameter& para, CallContext* ctxt) \
620{ \
621 if (!ImplicitBool(pyobject, ctxt)) \
622 return false; \
623 CPPYY_IMPL_BASIC_CONVERTER_BODY(name, type, stype, ctype, F1, F2, tc) \
624} \
625CPPYY_IMPL_BASIC_CONVERTER_METHODS(name, type, stype, ctype, F1, F2)
626
627#define CPPYY_IMPL_BASIC_CONVERTER_NB(name, type, stype, ctype, F1, F2, tc) \
628bool CPyCppyy::name##Converter::SetArg( \
629 PyObject* pyobject, Parameter& para, CallContext* /*ctxt*/) \
630{ \
631 if (PyBool_Check(pyobject)) \
632 return false; \
633 CPPYY_IMPL_BASIC_CONVERTER_BODY(name, type, stype, ctype, F1, F2, tc) \
634} \
635CPPYY_IMPL_BASIC_CONVERTER_METHODS(name, type, stype, ctype, F1, F2)
636
637//----------------------------------------------------------------------------
638static inline int ExtractChar(PyObject* pyobject, const char* tname, int low, int high)
639{
640 int lchar = -1;
641 if (PyBytes_Check(pyobject)) {
642 if (PyBytes_GET_SIZE(pyobject) == 1)
644 else
645 PyErr_Format(PyExc_ValueError, "%s expected, got bytes of size " PY_SSIZE_T_FORMAT,
647 } else if (CPyCppyy_PyText_Check(pyobject)) {
650 else
651 PyErr_Format(PyExc_ValueError, "%s expected, got str of size " PY_SSIZE_T_FORMAT,
653 } else if (pyobject == CPyCppyy::gDefaultObject) {
654 lchar = (int)'\0';
655 } else if (!PyFloat_Check(pyobject)) { // don't allow truncating conversion
657 if (lchar == -1 && PyErr_Occurred())
658 ; // empty, as error already set
659 else if (!(low <= lchar && lchar <= high)) {
661 "integer to character: value %d not in range [%d,%d]", lchar, low, high);
662 lchar = -1;
663 }
664 } else
665 PyErr_SetString(PyExc_TypeError, "char or small int type expected");
666
667 return lchar;
668}
669
670//----------------------------------------------------------------------------
671#define CPPYY_IMPL_REFCONVERTER_FROM_MEMORY(name, ctype) \
672PyObject* CPyCppyy::name##RefConverter::FromMemory(void* ptr) \
673{ \
674/* convert a reference to int to Python through ctypes pointer object */ \
675 PyTypeObject* ctypes_type = GetCTypesType(ct_##ctype); \
676 if (!ctypes_type) { \
677 PyErr_SetString(PyExc_RuntimeError, "no ctypes available"); \
678 return nullptr; \
679 } \
680 PyObject* ref = ctypes_type->tp_new(ctypes_type, nullptr, nullptr); \
681 ((CPyCppyy_tagCDataObject*)ref)->b_ptr = (char*)ptr; \
682 ((CPyCppyy_tagCDataObject*)ref)->b_needsfree = 0; \
683 return ref; \
684}
685
686//----------------------------------------------------------------------------
687#define CPPYY_IMPL_BASIC_CONST_REFCONVERTER(name, type, ctype, F1) \
688bool CPyCppyy::Const##name##RefConverter::SetArg( \
689 PyObject* pyobject, Parameter& para, CallContext* /* ctxt */) \
690{ \
691 type val = (type)F1(pyobject); \
692 if (val == (type)-1 && PyErr_Occurred()) { \
693 if (pyobject == CPyCppyy::gDefaultObject) { \
694 PyErr_Clear(); \
695 val = (type)0; \
696 } else \
697 return false; \
698 } \
699 para.fValue.f##name = val; \
700 para.fRef = &para.fValue.f##name; \
701 para.fTypeCode = 'r'; \
702 return true; \
703} \
704CPPYY_IMPL_REFCONVERTER_FROM_MEMORY(Const##name, ctype)
705
706//----------------------------------------------------------------------------
707#define CPPYY_IMPL_BASIC_CONST_CHAR_REFCONVERTER(name, type, ctype, low, high)\
708bool CPyCppyy::Const##name##RefConverter::SetArg( \
709 PyObject* pyobject, Parameter& para, CallContext* /* ctxt */) \
710{ \
711/* convert <pyobject> to C++ <<type>>, set arg for call, allow int -> char */\
712 type val = (type)ExtractChar(pyobject, #type, low, high); \
713 if (val == (type)-1 && PyErr_Occurred()) \
714 return false; \
715 para.fValue.fLong = val; \
716 para.fTypeCode = 'l'; \
717 return true; \
718} \
719CPPYY_IMPL_REFCONVERTER_FROM_MEMORY(Const##name, ctype)
720
721
722//----------------------------------------------------------------------------
723#define CPPYY_IMPL_BASIC_CHAR_CONVERTER(name, type, low, high) \
724bool CPyCppyy::name##Converter::SetArg( \
725 PyObject* pyobject, Parameter& para, CallContext* /* ctxt */) \
726{ \
727/* convert <pyobject> to C++ <<type>>, set arg for call, allow int -> char */\
728 long val = ExtractChar(pyobject, #type, low, high); \
729 if (val == -1 && PyErr_Occurred()) \
730 return false; \
731 para.fValue.fLong = val; \
732 para.fTypeCode = 'l'; \
733 return true; \
734} \
735 \
736PyObject* CPyCppyy::name##Converter::FromMemory(void* address) \
737{ \
738 /* return char in "native" str type as that's more natural in use */ \
739 return CPyCppyy_PyText_FromFormat("%c", *((type*)address)); \
740} \
741 \
742bool CPyCppyy::name##Converter::ToMemory( \
743 PyObject* value, void* address, PyObject* /* ctxt */) \
744{ \
745 Py_ssize_t len; \
746 const char* cstr = nullptr; \
747 if (PyBytes_Check(value)) \
748 PyBytes_AsStringAndSize(value, (char**)&cstr, &len); \
749 else \
750 cstr = CPyCppyy_PyText_AsStringAndSize(value, &len); \
751 if (cstr) { \
752 if (len != 1) { \
753 PyErr_Format(PyExc_TypeError, #type" expected, got string of size %zd", len);\
754 return false; \
755 } \
756 *((type*)address) = (type)cstr[0]; \
757 } else { \
758 PyErr_Clear(); \
759 long l = PyLong_AsLong(value); \
760 if (l == -1 && PyErr_Occurred()) { \
761 if (value == CPyCppyy::gDefaultObject) { \
762 PyErr_Clear(); \
763 l = (long)0; \
764 } else \
765 return false; \
766 } \
767 if (!(low <= l && l <= high)) { \
768 PyErr_Format(PyExc_ValueError, \
769 "integer to character: value %ld not in range [%d,%d]", l, low, high);\
770 return false; \
771 } \
772 *((type*)address) = (type)l; \
773 } \
774 return true; \
775}
776
777
778//- converters for built-ins -------------------------------------------------
780
781//----------------------------------------------------------------------------
782bool CPyCppyy::LongRefConverter::SetArg(
784{
785// convert <pyobject> to C++ long&, set arg for call
786#if PY_VERSION_HEX < 0x03000000
788 para.fValue.fVoidp = (void*)&((PyIntObject*)pyobject)->ob_ival;
789 para.fTypeCode = 'V';
790 return true;
791 }
792#endif
793
795 para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr;
796 para.fTypeCode = 'V';
797 return true;
798 }
799
800 if (CArraySetArg(pyobject, para, 'l', sizeof(long))) {
801 para.fTypeCode = 'V';
802 return true;
803 }
804
805 PyErr_SetString(PyExc_TypeError, "use ctypes.c_long for pass-by-ref of longs");
806 return false;
807}
808
809//----------------------------------------------------------------------------
812
828
829//----------------------------------------------------------------------------
830bool CPyCppyy::IntRefConverter::SetArg(
832{
833// convert <pyobject> to C++ (pseudo)int&, set arg for call
834#if PY_VERSION_HEX < 0x03000000
836 para.fValue.fVoidp = (void*)&((PyIntObject*)pyobject)->ob_ival;
837 para.fTypeCode = 'V';
838 return true;
839 }
840#endif
841
842#if PY_VERSION_HEX >= 0x02050000
844 para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr;
845 para.fTypeCode = 'V';
846 return true;
847 }
848#endif
849
850// alternate, pass pointer from buffer
851 Py_ssize_t buflen = Utility::GetBuffer(pyobject, 'i', sizeof(int), para.fValue.fVoidp);
852 if (para.fValue.fVoidp && buflen) {
853 para.fTypeCode = 'V';
854 return true;
855 };
856
857#if PY_VERSION_HEX < 0x02050000
858 PyErr_SetString(PyExc_TypeError, "use cppyy.Long for pass-by-ref of ints");
859#else
860 PyErr_SetString(PyExc_TypeError, "use ctypes.c_int for pass-by-ref of ints");
861#endif
862 return false;
863}
864
865//----------------------------------------------------------------------------
866#define CPPYY_IMPL_REFCONVERTER(name, ctype, type, code) \
867bool CPyCppyy::name##RefConverter::SetArg( \
868 PyObject* pyobject, Parameter& para, CallContext* /* ctxt */) \
869{ \
870/* convert a reference to int to Python through ctypes pointer object */ \
871 if (Py_TYPE(pyobject) == GetCTypesType(ct_##ctype)) { \
872 para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr;\
873 para.fTypeCode = 'V'; \
874 return true; \
875 } \
876 bool res = CArraySetArg(pyobject, para, code, sizeof(type)); \
877 if (!res) { \
878 PyErr_SetString(PyExc_TypeError, "use ctypes."#ctype" for pass-by-ref of "#type);\
879 return false; \
880 } \
881 para.fTypeCode = 'V'; \
882 return res; \
883} \
884CPPYY_IMPL_REFCONVERTER_FROM_MEMORY(name, ctype)
885
892CPPYY_IMPL_REFCONVERTER(UChar, c_ubyte, unsigned char, 'B');
900CPPYY_IMPL_REFCONVERTER(UShort, c_ushort, unsigned short, 'H');
902CPPYY_IMPL_REFCONVERTER(UInt, c_uint, unsigned int, 'I');
904CPPYY_IMPL_REFCONVERTER(ULong, c_ulong, unsigned long, 'L');
906CPPYY_IMPL_REFCONVERTER(ULLong, c_ulonglong, unsigned long long, 'Q');
910
911
912//----------------------------------------------------------------------------
913// convert <pyobject> to C++ bool, allow int/long -> bool, set arg for call
915 Bool, bool, long, c_bool, PyBool_FromLong, CPyCppyy_PyLong_AsBool, 'l')
916
917//----------------------------------------------------------------------------
919CPPYY_IMPL_BASIC_CHAR_CONVERTER(UChar, unsigned char, 0, UCHAR_MAX)
920
921PyObject* CPyCppyy::SCharAsIntConverter::FromMemory(void* address)
922{
923// special case to be used with arrays: return a Python int instead of str
924// (following the same convention as module array.array)
925 return PyInt_FromLong((long)*((signed char*)address));
926}
927
928PyObject* CPyCppyy::UCharAsIntConverter::FromMemory(void* address)
929{
930// special case to be used with arrays: return a Python int instead of str
931// (following the same convention as module array.array)
932 return PyInt_FromLong((long)*((unsigned char*)address));
933}
934
935//----------------------------------------------------------------------------
936bool CPyCppyy::WCharConverter::SetArg(
938{
939// convert <pyobject> to C++ <wchar_t>, set arg for call
941 PyErr_SetString(PyExc_ValueError, "single wchar_t character expected");
942 return false;
943 }
944 wchar_t val;
946 if (res == -1)
947 return false;
948 para.fValue.fLong = (long)val;
949 para.fTypeCode = 'U';
950 return true;
951}
952
953PyObject* CPyCppyy::WCharConverter::FromMemory(void* address)
954{
955 return PyUnicode_FromWideChar((const wchar_t*)address, 1);
956}
957
958bool CPyCppyy::WCharConverter::ToMemory(PyObject* value, void* address, PyObject* /* ctxt */)
959{
961 PyErr_SetString(PyExc_ValueError, "single wchar_t character expected");
962 return false;
963 }
964 wchar_t val;
966 if (res == -1)
967 return false;
968 *((wchar_t*)address) = val;
969 return true;
970}
971
972//----------------------------------------------------------------------------
973bool CPyCppyy::Char16Converter::SetArg(
975{
976// convert <pyobject> to C++ <char16_t>, set arg for call
978 PyErr_SetString(PyExc_ValueError, "single char16_t character expected");
979 return false;
980 }
981
983 if (!bstr) return false;
984
985 char16_t val = *(char16_t*)(PyBytes_AS_STRING(bstr) + sizeof(char16_t) /*BOM*/);
987 para.fValue.fLong = (long)val;
988 para.fTypeCode = 'U';
989 return true;
990}
991
992PyObject* CPyCppyy::Char16Converter::FromMemory(void* address)
993{
994 return PyUnicode_DecodeUTF16((const char*)address, sizeof(char16_t), nullptr, nullptr);
995}
996
997bool CPyCppyy::Char16Converter::ToMemory(PyObject* value, void* address, PyObject* /* ctxt */)
998{
1000 PyErr_SetString(PyExc_ValueError, "single char16_t character expected");
1001 return false;
1002 }
1003
1005 if (!bstr) return false;
1006
1007 *((char16_t*)address) = *(char16_t*)(PyBytes_AS_STRING(bstr) + sizeof(char16_t) /*BOM*/);
1008 Py_DECREF(bstr);
1009 return true;
1010}
1011
1012//----------------------------------------------------------------------------
1013bool CPyCppyy::Char32Converter::SetArg(
1014 PyObject* pyobject, Parameter& para, CallContext* /* ctxt */)
1015{
1016// convert <pyobject> to C++ <char32_t>, set arg for call
1018 PyErr_SetString(PyExc_ValueError, "single char32_t character expected");
1019 return false;
1020 }
1021
1023 if (!bstr) return false;
1024
1025 char32_t val = *(char32_t*)(PyBytes_AS_STRING(bstr) + sizeof(char32_t) /*BOM*/);
1026 Py_DECREF(bstr);
1027 para.fValue.fLong = (long)val;
1028 para.fTypeCode = 'U';
1029 return true;
1030}
1031
1032PyObject* CPyCppyy::Char32Converter::FromMemory(void* address)
1033{
1034 return PyUnicode_DecodeUTF32((const char*)address, sizeof(char32_t), nullptr, nullptr);
1035}
1036
1037bool CPyCppyy::Char32Converter::ToMemory(PyObject* value, void* address, PyObject* /* ctxt */)
1038{
1040 PyErr_SetString(PyExc_ValueError, "single char32_t character expected");
1041 return false;
1042 }
1043
1045 if (!bstr) return false;
1046
1047 *((char32_t*)address) = *(char32_t*)(PyBytes_AS_STRING(bstr) + sizeof(char32_t) /*BOM*/);
1048 Py_DECREF(bstr);
1049 return true;
1050}
1051
1052//----------------------------------------------------------------------------
1054 Int8, int8_t, long, c_int8, PyInt_FromLong, CPyCppyy_PyLong_AsInt8, 'l')
1066 Short, short, long, c_short, PyInt_FromLong, CPyCppyy_PyLong_AsShort, 'l')
1068 UShort, unsigned short, long, c_ushort, PyInt_FromLong, CPyCppyy_PyLong_AsUShort, 'l')
1071
1072//----------------------------------------------------------------------------
1073bool CPyCppyy::ULongConverter::SetArg(
1075{
1076// convert <pyobject> to C++ unsigned long, set arg for call
1077 if (!ImplicitBool(pyobject, ctxt))
1078 return false;
1079
1080 para.fValue.fULong = PyLongOrInt_AsULong(pyobject);
1081 if (para.fValue.fULong == (unsigned long)-1 && PyErr_Occurred())
1082 return false;
1083 para.fTypeCode = 'L';
1084 return true;
1085}
1086
1087PyObject* CPyCppyy::ULongConverter::FromMemory(void* address)
1088{
1089// construct python object from C++ unsigned long read at <address>
1090 return PyLong_FromUnsignedLong(*((unsigned long*)address));
1091}
1092
1093bool CPyCppyy::ULongConverter::ToMemory(PyObject* value, void* address, PyObject* /* ctxt */)
1094{
1095// convert <value> to C++ unsigned long, write it at <address>
1096 unsigned long u = PyLongOrInt_AsULong(value);
1097 if (u == (unsigned long)-1 && PyErr_Occurred()) {
1099 PyErr_Clear();
1100 u = (unsigned long)0;
1101 } else
1102 return false;
1103 }
1104 *((unsigned long*)address) = u;
1105 return true;
1106}
1107
1108//----------------------------------------------------------------------------
1109PyObject* CPyCppyy::UIntConverter::FromMemory(void* address)
1110{
1111// construct python object from C++ unsigned int read at <address>
1112 return PyLong_FromUnsignedLong(*((unsigned int*)address));
1113}
1114
1115bool CPyCppyy::UIntConverter::ToMemory(PyObject* value, void* address, PyObject* /* ctxt */)
1116{
1117// convert <value> to C++ unsigned int, write it at <address>
1118 unsigned long u = PyLongOrInt_AsULong(value);
1119 if (u == (unsigned long)-1 && PyErr_Occurred())
1120 return false;
1121
1122 if (u > (unsigned long)UINT_MAX) {
1123 PyErr_SetString(PyExc_OverflowError, "value too large for unsigned int");
1124 return false;
1125 }
1126
1127 *((unsigned int*)address) = (unsigned int)u;
1128 return true;
1129}
1130
1131//- floating point converters ------------------------------------------------
1133 Float, float, double, c_float, PyFloat_FromDouble, PyFloat_AsDouble, 'f')
1135 Double, double, double, c_double, PyFloat_FromDouble, PyFloat_AsDouble, 'd')
1136
1139
1140CPyCppyy::ComplexDConverter::ComplexDConverter(bool keepControl) :
1141 InstanceConverter(Cppyy::GetScope("std::complex<double>"), keepControl) {}
1142
1143// special case for std::complex<double>, maps it to/from Python's complex
1144bool CPyCppyy::ComplexDConverter::SetArg(
1146{
1148 if (pc.real != -1.0 || !PyErr_Occurred()) {
1149 fBuffer.real(pc.real);
1150 fBuffer.imag(pc.imag);
1151 para.fValue.fVoidp = &fBuffer;
1152 para.fTypeCode = 'V';
1153 return true;
1154 }
1155
1156 return this->InstanceConverter::SetArg(pyobject, para, ctxt);
1157}
1158
1159PyObject* CPyCppyy::ComplexDConverter::FromMemory(void* address)
1160{
1161 std::complex<double>* dc = (std::complex<double>*)address;
1162 return PyComplex_FromDoubles(dc->real(), dc->imag());
1163}
1164
1165bool CPyCppyy::ComplexDConverter::ToMemory(PyObject* value, void* address, PyObject* ctxt)
1166{
1168 if (pc.real != -1.0 || !PyErr_Occurred()) {
1169 std::complex<double>* dc = (std::complex<double>*)address;
1170 dc->real(pc.real);
1171 dc->imag(pc.imag);
1172 return true;
1173 }
1174 return this->InstanceConverter::ToMemory(value, address, ctxt);
1175}
1176
1177//----------------------------------------------------------------------------
1178bool CPyCppyy::DoubleRefConverter::SetArg(
1179 PyObject* pyobject, Parameter& para, CallContext* /* ctxt */)
1180{
1181// convert <pyobject> to C++ double&, set arg for call
1182#if PY_VERSION_HEX < 0x03000000
1184 para.fValue.fVoidp = (void*)&((PyFloatObject*)pyobject)->ob_fval;
1185 para.fTypeCode = 'V';
1186 return true;
1187 }
1188#endif
1189
1190#if PY_VERSION_HEX >= 0x02050000
1192 para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr;
1193 para.fTypeCode = 'V';
1194 return true;
1195 }
1196#endif
1197
1198// alternate, pass pointer from buffer
1199 Py_ssize_t buflen = Utility::GetBuffer(pyobject, 'd', sizeof(double), para.fValue.fVoidp);
1200 if (para.fValue.fVoidp && buflen) {
1201 para.fTypeCode = 'V';
1202 return true;
1203 }
1204
1205#if PY_VERSION_HEX < 0x02050000
1206 PyErr_SetString(PyExc_TypeError, "use cppyy.Double for pass-by-ref of doubles");
1207#else
1208 PyErr_SetString(PyExc_TypeError, "use ctypes.c_double for pass-by-ref of doubles");
1209#endif
1210 return false;
1211}
1212
1213//----------------------------------------------------------------------------
1217
1218//----------------------------------------------------------------------------
1219bool CPyCppyy::VoidConverter::SetArg(PyObject*, Parameter&, CallContext*)
1220{
1221// can't happen (unless a type is mapped wrongly), but implemented for completeness
1222 PyErr_SetString(PyExc_SystemError, "void/unknown arguments can\'t be set");
1223 return false;
1224}
1225
1226//----------------------------------------------------------------------------
1227bool CPyCppyy::LLongConverter::SetArg(
1229{
1230// convert <pyobject> to C++ long long, set arg for call
1231 if (!ImplicitBool(pyobject, ctxt))
1232 return false;
1233
1235 if (PyErr_Occurred())
1236 return false;
1237 para.fTypeCode = 'q';
1238 return true;
1239}
1240
1241PyObject* CPyCppyy::LLongConverter::FromMemory(void* address)
1242{
1243// construct python object from C++ long long read at <address>
1244 return PyLong_FromLongLong(*(PY_LONG_LONG*)address);
1245}
1246
1247bool CPyCppyy::LLongConverter::ToMemory(PyObject* value, void* address, PyObject* /* ctxt */)
1248{
1249// convert <value> to C++ long long, write it at <address>
1251 if (ll == -1 && PyErr_Occurred()) {
1253 PyErr_Clear();
1254 ll = (PY_LONG_LONG)0;
1255 } else
1256 return false;
1257 }
1258 *((PY_LONG_LONG*)address) = ll;
1259 return true;
1260}
1261
1262//----------------------------------------------------------------------------
1263bool CPyCppyy::ULLongConverter::SetArg(
1265{
1266// convert <pyobject> to C++ unsigned long long, set arg for call
1267 if (!ImplicitBool(pyobject, ctxt))
1268 return false;
1269
1270 para.fValue.fULLong = PyLongOrInt_AsULong64(pyobject);
1271 if (PyErr_Occurred())
1272 return false;
1273 para.fTypeCode = 'Q';
1274 return true;
1275}
1276
1277PyObject* CPyCppyy::ULLongConverter::FromMemory(void* address)
1278{
1279// construct python object from C++ unsigned long long read at <address>
1280 return PyLong_FromUnsignedLongLong(*(PY_ULONG_LONG*)address);
1281}
1282
1283bool CPyCppyy::ULLongConverter::ToMemory(PyObject* value, void* address, PyObject* /* ctxt */)
1284{
1285// convert <value> to C++ unsigned long long, write it at <address>
1287 if (PyErr_Occurred()) {
1289 PyErr_Clear();
1290 ull = (PY_ULONG_LONG)0;
1291 } else
1292 return false;
1293 }
1294 *((PY_ULONG_LONG*)address) = ull;
1295 return true;
1296}
1297
1298//----------------------------------------------------------------------------
1299bool CPyCppyy::CStringConverter::SetArg(
1301{
1302// construct a new string and copy it in new memory
1305 if (!cstr) {
1306 // special case: allow ctypes c_char_p
1307 auto error = CPyCppyy::Utility::FetchPyError();
1309 SetLifeLine(ctxt->fPyContext, pyobject, (intptr_t)this);
1310 para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr;
1311 para.fTypeCode = 'V';
1312 return true;
1313 }
1315 return false;
1316 }
1317
1318// verify (too long string will cause truncation, no crash)
1319 if (fMaxSize != std::string::npos && fMaxSize < fBuffer.size())
1320 if (PyErr_WarnEx(PyExc_RuntimeWarning, (char*)"string too long for char array (truncated)", 1) < 0)
1321 return false;
1322
1323 if (!ctxt->fPyContext) {
1324 // use internal buffer as workaround
1325 fBuffer = std::string(cstr, len);
1326 if (fMaxSize != std::string::npos)
1327 fBuffer.resize(fMaxSize, '\0'); // pad remainder of buffer as needed
1328 cstr = fBuffer.c_str();
1329 } else
1330 SetLifeLine(ctxt->fPyContext, pyobject, (intptr_t)this);
1331
1332// set the value and declare success
1333 para.fValue.fVoidp = (void*)cstr;
1334 para.fTypeCode = 'p';
1335 return true;
1336}
1337
1338PyObject* CPyCppyy::CStringConverter::FromMemory(void* address)
1339{
1340// construct python object from C++ const char* read at <address>
1341 if (address && *(void**)address) {
1342 if (fMaxSize != std::string::npos) // need to prevent reading beyond boundary
1343 return CPyCppyy_PyText_FromStringAndSize(*(char**)address, (Py_ssize_t)fMaxSize);
1344
1345 if (*(void**)address == (void*)fBuffer.data()) // if we're buffering, we know the size
1346 return CPyCppyy_PyText_FromStringAndSize((char*)fBuffer.data(), fBuffer.size());
1347
1348 // no idea about lentgth: cut on \0
1349 return CPyCppyy_PyText_FromString(*(char**)address);
1350 }
1351
1352// empty string in case there's no address
1355}
1356
1357bool CPyCppyy::CStringConverter::ToMemory(PyObject* value, void* address, PyObject* ctxt)
1358{
1359// convert <value> to C++ const char*, write it at <address>
1362 if (!cstr) return false;
1363
1364// verify (too long string will cause truncation, no crash)
1365 if (fMaxSize != std::string::npos && fMaxSize < (std::string::size_type)len)
1366 if (PyErr_WarnEx(PyExc_RuntimeWarning, (char*)"string too long for char array (truncated)", 1) < 0)
1367 return false;
1368
1369// if address is available, and it wasn't set by this converter, assume a byte-wise copy;
1370// otherwise assume a pointer copy (this relies on the converter to be used for properties,
1371// or for argument passing, but not both at the same time; this is currently the case)
1372 void* ptrval = *(void**)address;
1373 if (ptrval == (void*)fBuffer.data()) {
1374 fBuffer = std::string(cstr, len);
1375 *(void**)address = (void*)fBuffer.data();
1376 return true;
1377 } else if (ptrval && HasLifeLine(ctxt, (intptr_t)ptrval)) {
1378 ptrval = nullptr;
1379 // fall through; ptrval is nullptr means we're managing it
1380 }
1381
1382// the string is (going to be) managed by us: assume pointer copy
1383 if (!ptrval) {
1384 SetLifeLine(ctxt, value, (intptr_t)address);
1385 *(void**)address = (void*)cstr;
1386 return true;
1387 }
1388
1389// the pointer value is non-zero and not ours: assume byte copy
1390 if (fMaxSize != std::string::npos)
1391 strncpy(*(char**)address, cstr, fMaxSize); // pads remainder
1392 else
1393 // coverity[secure_coding] - can't help it, it's intentional.
1394 strcpy(*(char**)address, cstr);
1395
1396 return true;
1397}
1398
1399//----------------------------------------------------------------------------
1400bool CPyCppyy::WCStringConverter::SetArg(
1401 PyObject* pyobject, Parameter& para, CallContext* /* ctxt */)
1402{
1403// construct a new string and copy it in new memory
1405 if (len == (Py_ssize_t)-1 && PyErr_Occurred())
1406 return false;
1407
1408 fBuffer = (wchar_t*)realloc(fBuffer, sizeof(wchar_t)*(len+1));
1410 if (res == -1)
1411 return false; // could free the buffer here
1412
1413// set the value and declare success
1414 fBuffer[len] = L'\0';
1415 para.fValue.fVoidp = (void*)fBuffer;
1416 para.fTypeCode = 'p';
1417 return true;
1418}
1419
1420PyObject* CPyCppyy::WCStringConverter::FromMemory(void* address)
1421{
1422// construct python object from C++ wchar_t* read at <address>
1423 if (address && *(wchar_t**)address) {
1424 if (fMaxSize != std::wstring::npos) // need to prevent reading beyond boundary
1425 return PyUnicode_FromWideChar(*(wchar_t**)address, (Py_ssize_t)fMaxSize);
1426 // with unknown size
1427 return PyUnicode_FromWideChar(*(wchar_t**)address, wcslen(*(wchar_t**)address));
1428 }
1429
1430// empty string in case there's no valid address
1431 wchar_t w = L'\0';
1432 return PyUnicode_FromWideChar(&w, 0);
1433}
1434
1435bool CPyCppyy::WCStringConverter::ToMemory(PyObject* value, void* address, PyObject* /* ctxt */)
1436{
1437// convert <value> to C++ wchar_t*, write it at <address>
1439 if (len == (Py_ssize_t)-1 && PyErr_Occurred())
1440 return false;
1441
1442// verify (too long string will cause truncation, no crash)
1443 if (fMaxSize != std::wstring::npos && fMaxSize < (std::wstring::size_type)len)
1444 if (PyErr_WarnEx(PyExc_RuntimeWarning, (char*)"string too long for wchar_t array (truncated)", 1) < 0)
1445 return false;
1446
1447 Py_ssize_t res = -1;
1448 if (fMaxSize != std::wstring::npos)
1449 res = CPyCppyy_PyUnicode_AsWideChar(value, *(wchar_t**)address, (Py_ssize_t)fMaxSize);
1450 else
1451 // coverity[secure_coding] - can't help it, it's intentional.
1452 res = CPyCppyy_PyUnicode_AsWideChar(value, *(wchar_t**)address, len);
1453
1454 if (res == -1) return false;
1455 return true;
1456}
1457
1458//----------------------------------------------------------------------------
1459#define CPYCPPYY_WIDESTRING_CONVERTER(name, type, encode, decode, snull) \
1460bool CPyCppyy::name##Converter::SetArg( \
1461 PyObject* pyobject, Parameter& para, CallContext* /* ctxt */) \
1462{ \
1463/* change string encoding and copy into local buffer */ \
1464 PyObject* bstr = encode(pyobject); \
1465 if (!bstr) return false; \
1466 \
1467 Py_ssize_t len = PyBytes_GET_SIZE(bstr) - sizeof(type) /*BOM*/; \
1468 fBuffer = (type*)realloc(fBuffer, len + sizeof(type)); \
1469 memcpy(fBuffer, PyBytes_AS_STRING(bstr) + sizeof(type) /*BOM*/, len); \
1470 Py_DECREF(bstr); \
1471 \
1472 fBuffer[len/sizeof(type)] = snull; \
1473 para.fValue.fVoidp = (void*)fBuffer; \
1474 para.fTypeCode = 'p'; \
1475 return true; \
1476} \
1477 \
1478PyObject* CPyCppyy::name##Converter::FromMemory(void* address) \
1479{ \
1480/* construct python object from C++ <type>* read at <address> */ \
1481 if (address && *(type**)address) { \
1482 if (fMaxSize != std::wstring::npos) \
1483 return decode(*(const char**)address, (Py_ssize_t)fMaxSize*sizeof(type), nullptr, nullptr);\
1484 return decode(*(const char**)address, \
1485 std::char_traits<type>::length(*(type**)address)*sizeof(type), nullptr, nullptr);\
1486 } \
1487 \
1488/* empty string in case there's no valid address */ \
1489 type w = snull; \
1490 return decode((const char*)&w, 0, nullptr, nullptr); \
1491} \
1492 \
1493bool CPyCppyy::name##Converter::ToMemory(PyObject* value, void* address, PyObject* /* ctxt */)\
1494{ \
1495/* convert <value> to C++ <type>*, write it at <address> */ \
1496 PyObject* bstr = encode(value); \
1497 if (!bstr) return false; \
1498 \
1499 Py_ssize_t len = PyBytes_GET_SIZE(bstr) - sizeof(type) /*BOM*/; \
1500 Py_ssize_t maxbytes = (Py_ssize_t)fMaxSize*sizeof(type); \
1501 \
1502/* verify (too long string will cause truncation, no crash) */ \
1503 if (fMaxSize != std::wstring::npos && maxbytes < len) { \
1504 if (PyErr_WarnEx(PyExc_RuntimeWarning, (char*)"string too long for "#type" array (truncated)", 1) < 0) { \
1505 Py_DECREF(bstr); \
1506 return false; \
1507 } \
1508 len = maxbytes; \
1509 } \
1510 \
1511 memcpy(*((void**)address), PyBytes_AS_STRING(bstr) + sizeof(type) /*BOM*/, len);\
1512 Py_DECREF(bstr); \
1513/* debatable, but probably more convenient in most cases to null-terminate if enough space */\
1514 if (len/sizeof(type) < fMaxSize) (*(type**)address)[len/sizeof(type)] = snull;\
1515 return true; \
1516}
1517
1520
1521//----------------------------------------------------------------------------
1522bool CPyCppyy::NonConstCStringConverter::SetArg(
1524{
1525// attempt base class first (i.e. passing a string), but if that fails, try a buffer
1526 if (this->CStringConverter::SetArg(pyobject, para, ctxt))
1527 return true;
1528
1529// apparently failed, try char buffer
1530 PyErr_Clear();
1531 return CArraySetArg(pyobject, para, 'c', sizeof(char));
1532}
1533
1534//----------------------------------------------------------------------------
1535PyObject* CPyCppyy::NonConstCStringConverter::FromMemory(void* address)
1536{
1537// assume this is a buffer access if the size is known; otherwise assume string
1538 if (fMaxSize != std::string::npos)
1539 return CPyCppyy_PyText_FromStringAndSize(*(char**)address, (Py_ssize_t)fMaxSize);
1540 return this->CStringConverter::FromMemory(address);
1541}
1542
1543//----------------------------------------------------------------------------
1545{
1546// (1): C++11 style "null pointer"
1548 address = nullptr;
1549 return true;
1550 }
1551
1552// (2): allow integer zero to act as a null pointer (C NULL), no deriveds
1554 intptr_t val = (intptr_t)PyLong_AsLongLong(pyobject);
1555 if (val == 0l) {
1556 address = (void*)val;
1557 return true;
1558 }
1559
1560 return false;
1561 }
1562
1563// (3): opaque PyCapsule (CObject in older pythons) from somewhere
1565 address = (void*)CPyCppyy_PyCapsule_GetPointer(pyobject, nullptr);
1566 return true;
1567 }
1568
1569 return false;
1570}
1571
1572//----------------------------------------------------------------------------
1575{
1576// just convert pointer if it is a C++ object
1578 if (pyobj) {
1579 // depending on memory policy, some objects are no longer owned when passed to C++
1581 pyobj->CppOwns();
1582
1583 // set pointer (may be null) and declare success
1584 para.fValue.fVoidp = pyobj->GetObject();
1585 para.fTypeCode = 'p';
1586 return true;
1587 }
1588
1589// handle special cases
1590 if (GetAddressSpecialCase(pyobject, para.fValue.fVoidp)) {
1591 para.fTypeCode = 'p';
1592 return true;
1593 }
1594
1595// allow ctypes voidp (which if got as a buffer will return void**, not void*); use
1596// isintance instead of an exact check, b/c c_void_p is the type mapper for typedefs
1597// of void* (typically opaque handles)
1599 para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr;
1600 para.fTypeCode = 'V';
1601 return true;
1602 }
1603
1604// allow any other ctypes pointer type
1606 void** payload = (void**)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr;
1607 if (payload) {
1608 para.fValue.fVoidp = *payload;
1609 para.fTypeCode = 'p';
1610 return true;
1611 }
1612 }
1613
1614// final try: attempt to get buffer
1615 Py_ssize_t buflen = Utility::GetBuffer(pyobject, '*', 1, para.fValue.fVoidp, false);
1616
1617// ok if buffer exists (can't perform any useful size checks)
1618 if (para.fValue.fVoidp && buflen != 0) {
1619 para.fTypeCode = 'p';
1620 return true;
1621 }
1622
1623// give up
1624 return false;
1625}
1626
1627//----------------------------------------------------------------------------
1629{
1630// nothing sensible can be done, just return <address> as pylong
1631 if (!address || *(uintptr_t*)address == 0) {
1633 return gNullPtrObject;
1634 }
1635 return CreatePointerView(*(uintptr_t**)address);
1636}
1637
1638//----------------------------------------------------------------------------
1640{
1641// just convert pointer if it is a C++ object
1643 if (pyobj) {
1644 // depending on memory policy, some objects are no longer owned when passed to C++
1646 pyobj->CppOwns();
1647
1648 // set pointer (may be null) and declare success
1649 *(void**)address = pyobj->GetObject();
1650 return true;
1651 }
1652
1653// handle special cases
1654 void* ptr = nullptr;
1655 if (GetAddressSpecialCase(value, ptr)) {
1656 *(void**)address = ptr;
1657 return true;
1658 }
1659
1660// final try: attempt to get buffer
1661 void* buf = nullptr;
1662 Py_ssize_t buflen = Utility::GetBuffer(value, '*', 1, buf, false);
1663 if (!buf || buflen == 0)
1664 return false;
1665
1666 *(void**)address = buf;
1667 return true;
1668}
1669
1670#if __cplusplus >= 202002L
1671
1672namespace CPyCppyy {
1673
1674class StdSpanConverter : public InstanceConverter {
1675public:
1676 StdSpanConverter(std::string const &typeName, Cppyy::TCppType_t klass, bool keepControl = false)
1677 : InstanceConverter{klass, keepControl}, fTypeName{typeName}
1678 {
1679 }
1680
1682 {
1683 if (fHasBuffer) {
1685 }
1686 }
1687
1688 bool SetArg(PyObject *, Parameter &, CallContext * = nullptr) override;
1689 bool HasState() override { return true; }
1690
1691private:
1692 std::string fTypeName;
1693 std::span<std::size_t> fBuffer;
1694 bool fHasBuffer = false;
1696};
1697
1698} // namespace CPyCppyy
1699
1700//----------------------------------------------------------------------------
1701bool CPyCppyy::StdSpanConverter::SetArg(PyObject *pyobject, Parameter &para, CallContext *ctxt)
1702{
1703 auto typecodeFound = Utility::TypecodeMap().find(fTypeName);
1704
1705// attempt to get buffer if the C++ type maps to a buffer type
1707 // Fall back to regular InstanceConverter
1708 return this->InstanceConverter::SetArg(pyobject, para, ctxt);
1709 }
1710
1711 Py_ssize_t buflen = 0;
1712 char typecode = typecodeFound->second;
1713 memset(&fBufinfo, 0, sizeof(Py_buffer));
1714
1716 if (!strchr(fBufinfo.format, typecode)) {
1718 "buffer has incompatible type: expected '%c' for C++ type '%s', but got format '%s'", typecode,
1719 fTypeName.c_str(), fBufinfo.format ? fBufinfo.format : "<null>");
1721 return false;
1722 }
1723 buflen = Utility::GetBuffer(pyobject, typecode, 1, para.fValue.fVoidp, false);
1724 }
1725
1726// ok if buffer exists (can't perform any useful size checks)
1727 if (para.fValue.fVoidp && buflen != 0) {
1728 // We assume the layout for any std::span<T> is the same, and just use
1729 // std::span<std::size_t> as a placeholder. Not elegant, but works.
1730 fBuffer = std::span<std::size_t>{(std::size_t *)para.fValue.fVoidp, static_cast<std::size_t>(buflen)};
1731 fHasBuffer = true;
1732 para.fValue.fVoidp = &fBuffer;
1733 para.fTypeCode = 'V';
1734 return true;
1735 }
1736
1737 return false;
1738}
1739
1740#endif // __cplusplus >= 202002L
1741
1742namespace {
1743
1744// Copy a buffer to memory address with an array converter.
1745template<class type>
1746bool ToArrayFromBuffer(PyObject* owner, void* address, PyObject* ctxt,
1747 const void * buf, Py_ssize_t buflen,
1748 CPyCppyy::dims_t& shape, bool isFixed)
1749{
1750 if (buflen == 0)
1751 return false;
1752
1753 Py_ssize_t oldsz = 1;
1754 for (Py_ssize_t idim = 0; idim < shape.ndim(); ++idim) {
1755 if (shape[idim] == CPyCppyy::UNKNOWN_SIZE) {
1756 oldsz = -1;
1757 break;
1758 }
1759 oldsz *= shape[idim];
1760 }
1761 if (shape.ndim() != CPyCppyy::UNKNOWN_SIZE && 0 < oldsz && oldsz < buflen) {
1762 PyErr_SetString(PyExc_ValueError, "buffer too large for value");
1763 return false;
1764 }
1765
1766 if (isFixed)
1767 memcpy(*(type**)address, buf, (0 < buflen ? buflen : 1)*sizeof(type));
1768 else {
1769 *(type**)address = (type*)buf;
1770 shape.ndim(1);
1771 shape[0] = buflen;
1772 SetLifeLine(ctxt, owner, (intptr_t)address);
1773 }
1774 return true;
1775}
1776
1777}
1778
1779//----------------------------------------------------------------------------
1780#define CPPYY_IMPL_ARRAY_CONVERTER(name, ctype, type, code, suffix) \
1781CPyCppyy::name##ArrayConverter::name##ArrayConverter(cdims_t dims) : \
1782 fShape(dims) { \
1783 fIsFixed = dims ? fShape[0] != UNKNOWN_SIZE : false; \
1784} \
1785 \
1786bool CPyCppyy::name##ArrayConverter::SetArg( \
1787 PyObject* pyobject, Parameter& para, CallContext* ctxt) \
1788{ \
1789 /* filter ctypes first b/c their buffer conversion will be wrong */ \
1790 bool convOk = false; \
1791 \
1792 /* 2-dim case: ptr-ptr types */ \
1793 if (fShape.ndim() == 2) { \
1794 if (Py_TYPE(pyobject) == GetCTypesPtrType(ct_##ctype)) { \
1795 para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr;\
1796 para.fTypeCode = 'p'; \
1797 convOk = true; \
1798 } else if (Py_TYPE(pyobject) == GetCTypesType(ct_c_void_p)) { \
1799 /* special case: pass address of c_void_p buffer to return the address */\
1800 para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr;\
1801 para.fTypeCode = 'p'; \
1802 convOk = true; \
1803 } else if (LowLevelView_Check(pyobject) && \
1804 ((LowLevelView*)pyobject)->fBufInfo.ndim == 2 && \
1805 strchr(((LowLevelView*)pyobject)->fBufInfo.format, code)) { \
1806 para.fValue.fVoidp = ((LowLevelView*)pyobject)->get_buf(); \
1807 para.fTypeCode = 'p'; \
1808 convOk = true; \
1809 } \
1810 } \
1811 \
1812 /* 1-dim (accept pointer), or unknown (accept pointer as cast) */ \
1813 if (!convOk) { \
1814 PyTypeObject* ctypes_type = GetCTypesType(ct_##ctype); \
1815 if (Py_TYPE(pyobject) == ctypes_type) { \
1816 para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr;\
1817 para.fTypeCode = 'p'; \
1818 convOk = true; \
1819 } else if (Py_TYPE(pyobject) == GetCTypesPtrType(ct_##ctype)) { \
1820 para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr;\
1821 para.fTypeCode = 'V'; \
1822 convOk = true; \
1823 } else if (IsPyCArgObject(pyobject)) { \
1824 CPyCppyy_tagPyCArgObject* carg = (CPyCppyy_tagPyCArgObject*)pyobject;\
1825 if (carg->obj && Py_TYPE(carg->obj) == ctypes_type) { \
1826 para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)carg->obj)->b_ptr;\
1827 para.fTypeCode = 'p'; \
1828 convOk = true; \
1829 } \
1830 } \
1831 } \
1832 \
1833 /* cast pointer type */ \
1834 if (!convOk) { \
1835 bool ismulti = fShape.ndim() > 1; \
1836 convOk = CArraySetArg(pyobject, para, code, ismulti ? sizeof(void*) : sizeof(type), true);\
1837 } \
1838 \
1839 /* memory management and offsetting */ \
1840 if (convOk) SetLifeLine(ctxt->fPyContext, pyobject, (intptr_t)this); \
1841 \
1842 return convOk; \
1843} \
1844 \
1845PyObject* CPyCppyy::name##ArrayConverter::FromMemory(void* address) \
1846{ \
1847 if (!fIsFixed) \
1848 return CreateLowLevelView##suffix((type**)address, fShape); \
1849 return CreateLowLevelView##suffix(*(type**)address, fShape); \
1850} \
1851 \
1852bool CPyCppyy::name##ArrayConverter::ToMemory( \
1853 PyObject* value, void* address, PyObject* ctxt) \
1854{ \
1855 if (fShape.ndim() <= 1 || fIsFixed) { \
1856 void* buf = nullptr; \
1857 Py_ssize_t buflen = Utility::GetBuffer(value, code, sizeof(type), buf);\
1858 return ToArrayFromBuffer<type>(value, address, ctxt, buf, buflen, fShape, fIsFixed);\
1859 } else { /* multi-dim, non-flat array; assume structure matches */ \
1860 void* buf = nullptr; /* TODO: GetBuffer() assumes flat? */ \
1861 Py_ssize_t buflen = Utility::GetBuffer(value, code, sizeof(void*), buf);\
1862 if (buflen == 0) return false; \
1863 *(type**)address = (type*)buf; \
1864 SetLifeLine(ctxt, value, (intptr_t)address); \
1865 } \
1866 return true; \
1867}
1868
1869
1870//----------------------------------------------------------------------------
1871CPPYY_IMPL_ARRAY_CONVERTER(Bool, c_bool, bool, '?', )
1872CPPYY_IMPL_ARRAY_CONVERTER(SChar, c_char, signed char, 'b', )
1873CPPYY_IMPL_ARRAY_CONVERTER(UChar, c_ubyte, unsigned char, 'B', )
1874CPPYY_IMPL_ARRAY_CONVERTER(Byte, c_ubyte, std::byte, 'B', )
1881CPPYY_IMPL_ARRAY_CONVERTER(Short, c_short, short, 'h', )
1882CPPYY_IMPL_ARRAY_CONVERTER(UShort, c_ushort, unsigned short, 'H', )
1883CPPYY_IMPL_ARRAY_CONVERTER(Int, c_int, int, 'i', )
1884CPPYY_IMPL_ARRAY_CONVERTER(UInt, c_uint, unsigned int, 'I', )
1885CPPYY_IMPL_ARRAY_CONVERTER(Long, c_long, long, 'l', )
1886CPPYY_IMPL_ARRAY_CONVERTER(ULong, c_ulong, unsigned long, 'L', )
1888CPPYY_IMPL_ARRAY_CONVERTER(ULLong, c_ulonglong, unsigned long long, 'Q', )
1889CPPYY_IMPL_ARRAY_CONVERTER(Float, c_float, float, 'f', )
1890CPPYY_IMPL_ARRAY_CONVERTER(Double, c_double, double, 'd', )
1892CPPYY_IMPL_ARRAY_CONVERTER(ComplexF, c_fcomplex, std::complex<float>, 'z', )
1893CPPYY_IMPL_ARRAY_CONVERTER(ComplexD, c_complex, std::complex<double>, 'Z', )
1894
1895
1896//----------------------------------------------------------------------------
1897bool CPyCppyy::CStringArrayConverter::SetArg(
1899{
1902 // 2nd predicate is ebatable: it's a catch-all for ctypes-styled multi-dimensional objects,
1903 // which at this point does not check further dimensionality
1904 para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr;
1905 para.fTypeCode = 'V';
1906 return true;
1907
1909#if PY_VERSION_HEX >= 0x03000000
1911#endif
1912 ) {
1913 //for (auto& p : fBuffer) free(p);
1914 fBuffer.clear();
1915
1916 size_t len = (size_t)PySequence_Size(pyobject);
1917 if (len == (size_t)-1) {
1918 PyErr_SetString(PyExc_ValueError, "can not convert sequence object of unknown length");
1919 return false;
1920 }
1921
1922 fBuffer.reserve(len);
1923 for (size_t i = 0; i < len; ++i) {
1925 if (item) {
1926 Py_ssize_t sz;
1927 const char* p = CPyCppyy_PyText_AsStringAndSize(item, &sz);
1928 Py_DECREF(item);
1929
1930 if (p) fBuffer.push_back(p);
1931 else {
1932 PyErr_Format(PyExc_TypeError, "could not convert item %d to string", (int)i);
1933 return false;
1934 }
1935
1936 } else
1937 return false;
1938 }
1939
1940 para.fValue.fVoidp = (void*)fBuffer.data();
1941 para.fTypeCode = 'p';
1942 return true;
1943 }
1944
1945 return SCharArrayConverter::SetArg(pyobject, para, ctxt);
1946}
1947
1948
1949//----------------------------------------------------------------------------
1950PyObject* CPyCppyy::CStringArrayConverter::FromMemory(void* address)
1951{
1952 if (fIsFixed)
1953 return CreateLowLevelView(*(char**)address, fShape);
1954 else if (fShape[0] == UNKNOWN_SIZE)
1955 return CreateLowLevelViewString((const char**)address, fShape);
1956 return CreateLowLevelViewString(*(const char***)address, fShape);
1957}
1958
1959//----------------------------------------------------------------------------
1960bool CPyCppyy::CStringArrayConverter::ToMemory(PyObject* value, void* address, PyObject* ctxt)
1961{
1962// As a special array converter, the CStringArrayConverter one can also copy strings in the array,
1963// and not only buffers.
1965 if (const char* cstr = CPyCppyy_PyText_AsStringAndSize(value, &len)) {
1967 }
1968 return SCharArrayConverter::ToMemory(value, address, ctxt);
1969}
1970
1971//----------------------------------------------------------------------------
1972PyObject* CPyCppyy::NonConstCStringArrayConverter::FromMemory(void* address)
1973{
1974 if (fIsFixed)
1975 return CreateLowLevelView(*(char**)address, fShape);
1976 else if (fShape[0] == UNKNOWN_SIZE)
1977 return CreateLowLevelViewString((char**)address, fShape);
1978 return CreateLowLevelViewString(*(char***)address, fShape);
1979}
1980
1981//- converters for special cases ---------------------------------------------
1982bool CPyCppyy::NullptrConverter::SetArg(PyObject* pyobject, Parameter& para, CallContext* /* ctxt */)
1983{
1984// Only allow C++11 style nullptr to pass
1986 para.fValue.fVoidp = nullptr;
1987 para.fTypeCode = 'p';
1988 return true;
1989 }
1990 return false;
1991}
1992
1993
1994//----------------------------------------------------------------------------
1995template<typename T>
1996static inline bool CPyCppyy_PyUnicodeAsBytes2Buffer(PyObject* pyobject, T& buffer) {
1997 PyObject* pybytes = nullptr;
1998 if (PyBytes_Check(pyobject)) {
2000 pybytes = pyobject;
2001 } else if (PyUnicode_Check(pyobject)) {
2002#if PY_VERSION_HEX < 0x03030000
2005#else
2007#endif
2008 }
2009
2010 if (pybytes) {
2012 const char* cstr = nullptr;
2014 if (cstr) buffer = T{cstr, (typename T::size_type)len};
2016 return (bool)cstr;
2017 }
2018
2019 return false;
2020}
2021
2022#define CPPYY_IMPL_STRING_AS_PRIMITIVE_CONVERTER(name, type, F1, F2) \
2023CPyCppyy::name##Converter::name##Converter(bool keepControl) : \
2024 InstanceConverter(Cppyy::GetScope(#type), keepControl) {} \
2025 \
2026bool CPyCppyy::name##Converter::SetArg( \
2027 PyObject* pyobject, Parameter& para, CallContext* ctxt) \
2028{ \
2029 if (CPyCppyy_PyUnicodeAsBytes2Buffer(pyobject, fBuffer)) { \
2030 para.fValue.fVoidp = &fBuffer; \
2031 para.fTypeCode = 'V'; \
2032 return true; \
2033 } \
2034 \
2035 PyErr_Clear(); \
2036 if (!(PyInt_Check(pyobject) || PyLong_Check(pyobject))) { \
2037 bool result = InstanceConverter::SetArg(pyobject, para, ctxt); \
2038 para.fTypeCode = 'V'; \
2039 return result; \
2040 } \
2041 \
2042 return false; \
2043} \
2044 \
2045PyObject* CPyCppyy::name##Converter::FromMemory(void* address) \
2046{ \
2047 if (address) \
2048 return InstanceConverter::FromMemory(address); \
2049 auto* empty = new type(); \
2050 return BindCppObjectNoCast(empty, fClass, CPPInstance::kIsOwner); \
2051} \
2052 \
2053bool CPyCppyy::name##Converter::ToMemory( \
2054 PyObject* value, void* address, PyObject* ctxt) \
2055{ \
2056 if (CPyCppyy_PyUnicodeAsBytes2Buffer(value, *((type*)address))) \
2057 return true; \
2058 return InstanceConverter::ToMemory(value, address, ctxt); \
2059}
2060
2062
2063
2064CPyCppyy::STLWStringConverter::STLWStringConverter(bool keepControl) :
2065 InstanceConverter(Cppyy::GetScope("std::wstring"), keepControl) {}
2066
2067bool CPyCppyy::STLWStringConverter::SetArg(
2069{
2072 fBuffer.resize(len);
2074 para.fValue.fVoidp = &fBuffer;
2075 para.fTypeCode = 'V';
2076 return true;
2077 }
2078#if PY_VERSION_HEX < 0x03000000
2079 else if (PyString_Check(pyobject)) {
2080#ifdef HAS_CODECVT
2081 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> cnv;
2082 fBuffer = cnv.from_bytes(PyString_AS_STRING(pyobject));
2083#else
2085 if (!pyu) return false;
2087 fBuffer.resize(len);
2089#endif
2090 para.fValue.fVoidp = &fBuffer;
2091 para.fTypeCode = 'V';
2092 return true;
2093 }
2094#endif
2095
2098 para.fTypeCode = 'V';
2099 return result;
2100 }
2101
2102 return false;
2103}
2104
2105PyObject* CPyCppyy::STLWStringConverter::FromMemory(void* address)
2106{
2107 if (address)
2108 return PyUnicode_FromWideChar(((std::wstring*)address)->c_str(), ((std::wstring*)address)->size());
2109 wchar_t w = L'\0';
2110 return PyUnicode_FromWideChar(&w, 0);
2111}
2112
2113bool CPyCppyy::STLWStringConverter::ToMemory(PyObject* value, void* address, PyObject* ctxt)
2114{
2115 if (PyUnicode_Check(value)) {
2117 wchar_t* buf = new wchar_t[len+1];
2119 *((std::wstring*)address) = std::wstring(buf, len);
2120 delete[] buf;
2121 return true;
2122 }
2123 return InstanceConverter::ToMemory(value, address, ctxt);
2124}
2125
2126
2127CPyCppyy::STLStringViewConverter::STLStringViewConverter(bool keepControl) :
2128 InstanceConverter(Cppyy::GetScope("std::string_view"), keepControl) {}
2129
2130bool CPyCppyy::STLStringViewConverter::SetArg(
2132{
2133// normal instance convertion (eg. string_view object passed)
2136 if (InstanceConverter::SetArg(pyobject, para, ctxt)) {
2137 para.fTypeCode = 'V';
2138 return true;
2139 } else
2140 PyErr_Clear();
2141 }
2142
2143// passing of a Python string; buffering done Python-side b/c str is immutable
2146 if (cstr) {
2147 SetLifeLine(ctxt->fPyContext, pyobject, (intptr_t)this);
2148 fBuffer = std::string_view(cstr, (std::string_view::size_type)len);
2149 para.fValue.fVoidp = &fBuffer;
2150 para.fTypeCode = 'V';
2151 return true;
2152 }
2153
2155 return false;
2156
2157// special case of a C++ std::string object; life-time management is left to
2158// the caller to ensure any external changes propagate correctly
2160 static Cppyy::TCppScope_t sStringID = Cppyy::GetScope("std::string");
2162 if (pyobj->ObjectIsA() == sStringID) {
2163 void* ptr = pyobj->GetObject();
2164 if (!ptr)
2165 return false; // leaves prior conversion error for report
2166
2167 PyErr_Clear();
2168
2169 fBuffer = *((std::string*)ptr);
2170 para.fValue.fVoidp = &fBuffer;
2171 para.fTypeCode = 'V';
2172 return true;
2173 }
2174 }
2175
2176 return false;
2177}
2178
2179PyObject* CPyCppyy::STLStringViewConverter::FromMemory(void* address)
2180{
2181 if (address)
2182 return InstanceConverter::FromMemory(address);
2183 auto* empty = new std::string_view();
2185}
2186
2187bool CPyCppyy::STLStringViewConverter::ToMemory(
2188 PyObject* value, void* address, PyObject* ctxt)
2189{
2190// common case of simple object assignment
2191 if (InstanceConverter::ToMemory(value, address, ctxt))
2192 return true;
2193
2194// assignment of a Python string; buffering done Python-side b/c str is immutable
2197 if (cstr) {
2198 SetLifeLine(ctxt, value, (intptr_t)this);
2199 *reinterpret_cast<std::string_view*>(address) = \
2200 std::string_view(cstr, (std::string_view::size_type)len);
2201 return true;
2202 }
2203
2204 return false;
2205}
2206
2207
2208bool CPyCppyy::STLStringMoveConverter::SetArg(
2210{
2211// convert <pyobject> to C++ std::string&&, set arg for call
2212 int moveit_reason = 3; // move on temporary fBuffer
2215 if (pyobj->fFlags & CPPInstance::kIsRValue) {
2216 pyobj->fFlags &= ~CPPInstance::kIsRValue;
2217 moveit_reason = 2;
2219 moveit_reason = 1;
2220 } else
2221 moveit_reason = 0;
2222 }
2223
2224 if (moveit_reason) {
2225 bool result = this->STLStringConverter::SetArg(pyobject, para, ctxt);
2226 if (!result && moveit_reason == 2) // restore the movability flag?
2228 return result;
2229 }
2230
2231 PyErr_SetString(PyExc_ValueError, "object is not an rvalue");
2232 return false; // not a temporary or movable object
2233}
2234
2235
2236//----------------------------------------------------------------------------
2237template <bool ISCONST>
2240{
2241// convert <pyobject> to C++ instance*, set arg for call
2243 if (!pyobj) {
2244 if (GetAddressSpecialCase(pyobject, para.fValue.fVoidp)) {
2245 para.fTypeCode = 'p'; // allow special cases such as nullptr
2246 return true;
2247 }
2248
2249 // not a cppyy object (TODO: handle SWIG etc.)
2250 return false;
2251 }
2252
2253 // smart pointers should only extract the pointer if this is NOT an implicit
2254 // conversion to another smart pointer
2255 if (pyobj->IsSmart() && IsConstructor(ctxt->fFlags) && Cppyy::IsSmartPtr(ctxt->fCurScope))
2256 return false;
2257
2258 Cppyy::TCppType_t oisa = pyobj->ObjectIsA();
2259 if (oisa && (oisa == fClass || Cppyy::IsSubtype(oisa, fClass))) {
2260 // depending on memory policy, some objects need releasing when passed into functions
2261 if (!KeepControl() && !UseStrictOwnership())
2262 pyobj->CppOwns();
2263
2264 // calculate offset between formal and actual arguments
2265 para.fValue.fVoidp = pyobj->GetObject();
2266 if (oisa != fClass) {
2267 para.fValue.fIntPtr += Cppyy::GetBaseOffset(
2268 oisa, fClass, para.fValue.fVoidp, 1 /* up-cast */);
2269 }
2270
2271 // set pointer (may be null) and declare success
2272 para.fTypeCode = 'p';
2273 return true;
2274 }
2275
2276 return false;
2277}
2278
2279//----------------------------------------------------------------------------
2280template <bool ISCONST>
2282{
2283// construct python object from C++ instance read at <address>
2284 if (ISCONST)
2285 return BindCppObject(*(void**)address, fClass); // by pointer value
2286 return BindCppObject(address, fClass, CPPInstance::kIsReference); // modifiable
2287}
2288
2289//----------------------------------------------------------------------------
2290template <bool ISCONST>
2292{
2293// convert <value> to C++ instance, write it at <address>
2295 if (!pyobj) {
2296 void* ptr = nullptr;
2297 if (GetAddressSpecialCase(value, ptr)) {
2298 *(void**)address = ptr; // allow special cases such as nullptr
2299 return true;
2300 }
2301
2302 // not a cppyy object (TODO: handle SWIG etc.)
2303 return false;
2304 }
2305
2306 if (Cppyy::IsSubtype(pyobj->ObjectIsA(), fClass)) {
2307 // depending on memory policy, some objects need releasing when passed into functions
2308 if (!KeepControl() && !UseStrictOwnership())
2309 ((CPPInstance*)value)->CppOwns();
2310
2311 *(void**)address = pyobj->GetObject();
2312 return true;
2313 }
2314
2315 return false;
2316}
2317
2318// TODO: CONSOLIDATE Instance, InstanceRef, InstancePtr ...
2319
2320//----------------------------------------------------------------------------
2321bool CPyCppyy::InstanceConverter::SetArg(
2323{
2324// convert <pyobject> to C++ instance, set arg for call
2326 if (pyobj) {
2327 auto oisa = pyobj->ObjectIsA();
2328 if (oisa && (oisa == fClass || Cppyy::IsSubtype(oisa, fClass))) {
2329 // calculate offset between formal and actual arguments
2330 para.fValue.fVoidp = pyobj->GetObject();
2331 if (!para.fValue.fVoidp)
2332 return false;
2333
2334 if (oisa != fClass) {
2335 para.fValue.fIntPtr += Cppyy::GetBaseOffset(
2336 pyobj->ObjectIsA(), fClass, para.fValue.fVoidp, 1 /* up-cast */);
2337 }
2338
2339 para.fTypeCode = 'V';
2340 return true;
2341 }
2342 }
2343
2344 return (bool)ConvertImplicit(fClass, pyobject, para, ctxt);
2345}
2346
2347//----------------------------------------------------------------------------
2348PyObject* CPyCppyy::InstanceConverter::FromMemory(void* address)
2349{
2350// This should not need a cast (ie. BindCppObjectNoCast), but performing the cast
2351// here means callbacks receive down-casted object when passed by-ptr, which is
2352// needed for object identity. The latter case is assumed to be more common than
2353// conversion of (global) objects.
2354 return BindCppObject((Cppyy::TCppObject_t)address, fClass);
2355}
2356
2357//----------------------------------------------------------------------------
2358bool CPyCppyy::InstanceConverter::ToMemory(PyObject* value, void* address, PyObject* /* ctxt */)
2359{
2360// assign value to C++ instance living at <address> through assignment operator
2362#if PY_VERSION_HEX >= 0x03080000
2364#else
2365 PyObject* result = PyObject_CallMethod(pyobj, (char*)"__assign__", (char*)"O", value);
2366#endif
2368
2369 if (result) {
2371 return true;
2372 }
2373 return false;
2374}
2375
2376
2377//----------------------------------------------------------------------------
2378bool CPyCppyy::InstanceRefConverter::SetArg(
2380{
2381// convert <pyobject> to C++ instance&, set arg for call
2383 if (pyobj) {
2384
2385 // reject moves
2386 if (pyobj->fFlags & CPPInstance::kIsRValue)
2387 return false;
2388
2389 // smart pointers can end up here in case of a move, so preferentially match
2390 // the smart type directly
2391 bool argset = false;
2393 if (pyobj->IsSmart()) {
2394 cls = pyobj->ObjectIsA(false);
2395 if (cls && Cppyy::IsSubtype(cls, fClass)) {
2396 para.fValue.fVoidp = pyobj->GetObjectRaw();
2397 argset = true;
2398 }
2399 }
2400
2401 if (!argset) {
2402 cls = pyobj->ObjectIsA();
2403 if (cls && Cppyy::IsSubtype(cls, fClass)) {
2404 para.fValue.fVoidp = pyobj->GetObject();
2405 argset = true;
2406 }
2407 }
2408
2409 if (argset) {
2410 // do not allow null pointers through references
2411 if (!para.fValue.fVoidp) {
2412 PyErr_SetString(PyExc_ReferenceError, "attempt to access a null-pointer");
2413 return false;
2414 }
2415
2416 // calculate offset between formal and actual arguments
2417 if (cls != fClass) {
2418 para.fValue.fIntPtr += Cppyy::GetBaseOffset(
2419 cls, fClass, para.fValue.fVoidp, 1 /* up-cast */);
2420 }
2421
2422 para.fTypeCode = 'V';
2423 return true;
2424 }
2425 }
2426
2427 if (!fIsConst) // no implicit conversion possible
2428 return false;
2429
2430 return (bool)ConvertImplicit(fClass, pyobject, para, ctxt);
2431}
2432
2433//----------------------------------------------------------------------------
2434PyObject* CPyCppyy::InstanceRefConverter::FromMemory(void* address)
2435{
2437}
2438
2439//----------------------------------------------------------------------------
2440bool CPyCppyy::InstanceMoveConverter::SetArg(
2442{
2443// convert <pyobject> to C++ instance&&, set arg for call
2444 CPPInstance* pyobj = GetCppInstance(pyobject, fClass, true /* accept_rvalue */);
2445 if (!pyobj || (pyobj->fFlags & CPPInstance::kIsLValue)) {
2446 // implicit conversion is fine as the temporary by definition is moveable
2447 return (bool)ConvertImplicit(fClass, pyobject, para, ctxt);
2448 }
2449
2450// moving is same as by-ref, but have to check that move is allowed
2451 int moveit_reason = 0;
2452 if (pyobj->fFlags & CPPInstance::kIsRValue) {
2453 pyobj->fFlags &= ~CPPInstance::kIsRValue;
2454 moveit_reason = 2;
2456 moveit_reason = 1;
2457 }
2458
2459 if (moveit_reason) {
2460 bool result = this->InstanceRefConverter::SetArg(pyobject, para, ctxt);
2461 if (!result && moveit_reason == 2) // restore the movability flag?
2463 return result;
2464 }
2465
2466 PyErr_SetString(PyExc_ValueError, "object is not an rvalue");
2467 return false; // not a temporary or movable object
2468}
2469
2470//----------------------------------------------------------------------------
2471template <bool ISREFERENCE>
2472bool CPyCppyy::InstancePtrPtrConverter<ISREFERENCE>::SetArg(
2474{
2475// convert <pyobject> to C++ instance**, set arg for call
2477 if (!pyobj) {
2479 // allow nullptr as a special case
2480 para.fValue.fVoidp = nullptr;
2481 para.fTypeCode = 'p';
2482 return true;
2483 }
2484 return false; // not a cppyy object (TODO: handle SWIG etc.)
2485 }
2486
2487 if (Cppyy::IsSubtype(pyobj->ObjectIsA(), fClass)) {
2488 // depending on memory policy, some objects need releasing when passed into functions
2489 if (!KeepControl() && !UseStrictOwnership())
2490 pyobj->CppOwns();
2491
2492 // set pointer (may be null) and declare success
2493 if (pyobj->fFlags & CPPInstance::kIsReference) // already a ptr to object?
2494 para.fValue.fVoidp = pyobj->GetObjectRaw();
2495 else
2496 para.fValue.fVoidp = &pyobj->GetObjectRaw();
2497 para.fTypeCode = ISREFERENCE ? 'V' : 'p';
2498 return true;
2499 }
2500
2501 return false;
2502}
2503
2504//----------------------------------------------------------------------------
2505template <bool ISREFERENCE>
2506PyObject* CPyCppyy::InstancePtrPtrConverter<ISREFERENCE>::FromMemory(void* address)
2507{
2508// construct python object from C++ instance* read at <address>
2510}
2511
2512//----------------------------------------------------------------------------
2513template <bool ISREFERENCE>
2514bool CPyCppyy::InstancePtrPtrConverter<ISREFERENCE>::ToMemory(
2515 PyObject* value, void* address, PyObject* /* ctxt */)
2516{
2517// convert <value> to C++ instance*, write it at <address>
2519 if (!pyobj) {
2521 // allow nullptr as a special case
2522 *(void**)address = nullptr;
2523 return true;
2524 }
2525 return false; // not a cppyy object (TODO: handle SWIG etc.)
2526 }
2527
2528 if (Cppyy::IsSubtype(pyobj->ObjectIsA(), fClass)) {
2529 // depending on memory policy, some objects need releasing when passed into functions
2530 if (!KeepControl() && !UseStrictOwnership())
2531 pyobj->CppOwns();
2532
2533 // register the value for potential recycling
2535
2536 // set pointer (may be null) and declare success
2537 *(void**)address = pyobj->GetObject();
2538 return true;
2539 }
2540
2541 return false;
2542}
2543
2544
2545namespace CPyCppyy {
2546// Instantiate the templates
2549 template class CPyCppyy::InstancePtrPtrConverter<true>;
2550 template class CPyCppyy::InstancePtrPtrConverter<false>;
2551}
2552
2553//----------------------------------------------------------------------------
2554bool CPyCppyy::InstanceArrayConverter::SetArg(
2556{
2557// convert <pyobject> to C++ instance**, set arg for call
2559 return false; // no guarantee that the tuple is okay
2560
2561// treat the first instance of the tuple as the start of the array, and pass it
2562// by pointer (TODO: store and check sizes)
2563 if (PyTuple_Size(pyobject) < 1)
2564 return false;
2565
2566 PyObject* first = PyTuple_GetItem(pyobject, 0);
2567 if (!CPPInstance_Check(first))
2568 return false; // should not happen
2569
2570 if (Cppyy::IsSubtype(((CPPInstance*)first)->ObjectIsA(), fClass)) {
2571 // no memory policies supported; set pointer (may be null) and declare success
2572 para.fValue.fVoidp = ((CPPInstance*)first)->GetObject();
2573 para.fTypeCode = 'p';
2574 return true;
2575 }
2576
2577 return false;
2578}
2579
2580//----------------------------------------------------------------------------
2581PyObject* CPyCppyy::InstanceArrayConverter::FromMemory(void* address)
2582{
2583// construct python tuple of instances from C++ array read at <address>
2584 return BindCppObjectArray(*(char**)address, fClass, fShape);
2585}
2586
2587//----------------------------------------------------------------------------
2588bool CPyCppyy::InstanceArrayConverter::ToMemory(
2589 PyObject* /* value */, void* /* address */, PyObject* /* ctxt */)
2590{
2591// convert <value> to C++ array of instances, write it at <address>
2592
2593// TODO: need to have size both for the array and from the input
2595 "access to C-arrays of objects not yet implemented!");
2596 return false;
2597}
2598
2599//___________________________________________________________________________
2600// Cling WORKAROUND -- classes for STL iterators are completely undefined in that
2601// they come in a bazillion different guises, so just do whatever
2602bool CPyCppyy::STLIteratorConverter::SetArg(
2603 PyObject* pyobject, Parameter& para, CallContext* /* ctxt */)
2604{
2606 return false;
2607
2608// just set the pointer value, no check
2610 para.fValue.fVoidp = pyobj->GetObject();
2611 para.fTypeCode = 'V';
2612 return true;
2613}
2614// -- END Cling WORKAROUND
2615
2616//----------------------------------------------------------------------------
2617bool CPyCppyy::VoidPtrRefConverter::SetArg(
2618 PyObject* pyobject, Parameter& para, CallContext* /* ctxt */)
2619{
2620// convert <pyobject> to C++ void*&, set arg for call
2622 if (pyobj) {
2623 para.fValue.fVoidp = &pyobj->GetObjectRaw();
2624 para.fTypeCode = 'V';
2625 return true;
2626 }
2627
2628 return false;
2629}
2630
2631//----------------------------------------------------------------------------
2632CPyCppyy::VoidPtrPtrConverter::VoidPtrPtrConverter(cdims_t dims) :
2633 fShape(dims) {
2634 fIsFixed = dims ? fShape[0] != UNKNOWN_SIZE : false;
2635}
2636
2637//----------------------------------------------------------------------------
2638bool CPyCppyy::VoidPtrPtrConverter::SetArg(
2639 PyObject* pyobject, Parameter& para, CallContext* /* ctxt */)
2640{
2641// convert <pyobject> to C++ void**, set arg for call
2643 if (pyobj) {
2644 // this is a C++ object, take and set its address
2645 para.fValue.fVoidp = &pyobj->GetObjectRaw();
2646 para.fTypeCode = 'p';
2647 return true;
2648 } else if (IsPyCArgObject(pyobject)) {
2650 if (carg->obj) {
2651 para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)carg->obj)->b_ptr;
2652 para.fTypeCode = 'p';
2653 return true;
2654 }
2655 }
2656
2657// buffer objects are allowed under "user knows best" (this includes the buffer
2658// interface to ctypes.c_void_p, which results in a void**)
2659 Py_ssize_t buflen = Utility::GetBuffer(pyobject, '*', 1, para.fValue.fVoidp, false);
2660
2661// ok if buffer exists (can't perform any useful size checks)
2662 if (para.fValue.fVoidp && buflen != 0) {
2663 para.fTypeCode = 'p';
2664 return true;
2665 }
2666
2667 return false;
2668}
2669
2670//----------------------------------------------------------------------------
2671PyObject* CPyCppyy::VoidPtrPtrConverter::FromMemory(void* address)
2672{
2673// read a void** from address; since this is unknown, uintptr_t is used (user can cast)
2674 if (!address || *(ptrdiff_t*)address == 0) {
2676 return gNullPtrObject;
2677 }
2678 if (!fIsFixed)
2679 return CreatePointerView((uintptr_t**)address, fShape);
2680 return CreatePointerView(*(uintptr_t**)address, fShape);
2681}
2682
2683//----------------------------------------------------------------------------
2684bool CPyCppyy::PyObjectConverter::SetArg(
2685 PyObject* pyobject, Parameter& para, CallContext* /* ctxt */)
2686{
2687// by definition: set and declare success
2688 para.fValue.fVoidp = pyobject;
2689 para.fTypeCode = 'p';
2690 return true;
2691}
2692
2693PyObject* CPyCppyy::PyObjectConverter::FromMemory(void* address)
2694{
2695// construct python object from C++ PyObject* read at <address>
2696 PyObject* pyobject = *((PyObject**)address);
2697
2698 if (!pyobject) {
2700 }
2701
2703 return pyobject;
2704}
2705
2706bool CPyCppyy::PyObjectConverter::ToMemory(PyObject* value, void* address, PyObject* /* ctxt */)
2707{
2708// no conversion needed, write <value> at <address>
2710 Py_XDECREF(*((PyObject**)address));
2711 *((PyObject**)address) = value;
2712 return true;
2713}
2714
2715
2716//- function pointer converter -----------------------------------------------
2717static unsigned int sWrapperCounter = 0;
2718// cache mapping signature/return type to python callable and corresponding wrapper
2719typedef std::string RetSigKey_t;
2720static std::map<RetSigKey_t, std::vector<void*>> sWrapperFree;
2721static std::map<RetSigKey_t, std::map<PyObject*, void*>> sWrapperLookup;
2722static std::map<PyObject*, std::pair<void*, RetSigKey_t>> sWrapperWeakRefs;
2723static std::map<void*, PyObject**> sWrapperReference;
2724
2726{
2727 auto ipos = sWrapperWeakRefs.find(pyref);
2728 if (ipos != sWrapperWeakRefs.end()) {
2729 auto key = ipos->second.second;
2730
2731 // disable this callback and store on free list for possible re-use
2732 void* wpraddress = ipos->second.first;
2734 const auto& lookup = sWrapperLookup.find(key);
2735 if (lookup != sWrapperLookup.end()) lookup->second.erase(*oldref);
2736 *oldref = nullptr; // to detect deletions
2737 sWrapperFree[ipos->second.second].push_back(wpraddress);
2738
2739 // clean up and remove weak reference from admin
2740 Py_DECREF(ipos->first);
2741 sWrapperWeakRefs.erase(ipos);
2742 }
2743
2745}
2747 const_cast<char*>("internal_WrapperCacheEraser"),
2749 METH_O, nullptr
2750};
2751
2753 const std::string& rettype, const std::string& signature, bool allowCppInstance)
2754{
2755// Convert a bound C++ function pointer or callable python object to a C-style
2756// function pointer. The former is direct, the latter involves a JIT-ed wrapper.
2758
2759 using namespace CPyCppyy;
2760
2763 if (!ol->fMethodInfo || ol->fMethodInfo->fMethods.empty())
2764 return nullptr;
2765
2766 // find the overload with matching signature
2767 for (auto& m : ol->fMethodInfo->fMethods) {
2768 PyObject* sig = m->GetSignature(false);
2769 bool found = signature == CPyCppyy_PyText_AsString(sig);
2770 Py_DECREF(sig);
2771 if (found) {
2772 void* fptr = (void*)m->GetFunctionAddress();
2773 if (fptr) return fptr;
2774 break; // fall-through, with calling through Python
2775 }
2776 }
2777 }
2778
2780 // get the actual underlying template matching the signature
2782 std::string fullname = pytmpl->fTI->fCppName;
2783 if (pytmpl->fTemplateArgs)
2784 fullname += CPyCppyy_PyText_AsString(pytmpl->fTemplateArgs);
2785 Cppyy::TCppScope_t scope = ((CPPClass*)pytmpl->fTI->fPyClass)->fCppType;
2787 if (cppmeth) {
2788 void* fptr = (void*)Cppyy::GetFunctionAddress(cppmeth, false);
2789 if (fptr) return fptr;
2790 }
2791 // fall-through, with calling through Python
2792 }
2793
2795 // ctypes function pointer
2796 void* fptr = *(void**)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr;
2797 return fptr;
2798 }
2799
2800
2802 // generic python callable: create a C++ wrapper function
2803 // Sometimes we don't want to take this branch if the object is a C++
2804 // instance, because C++ doesn't allow converting functor objects to
2805 // function pointers, but only to std::function.
2806 void* wpraddress = nullptr;
2807
2808 // re-use existing wrapper if possible
2809 auto key = rettype+signature;
2810 const auto& lookup = sWrapperLookup.find(key);
2811 if (lookup != sWrapperLookup.end()) {
2812 const auto& existing = lookup->second.find(pyobject);
2813 if (existing != lookup->second.end() && *sWrapperReference[existing->second] == pyobject)
2814 wpraddress = existing->second;
2815 }
2816
2817 // check for a pre-existing, unused, wrapper if not found
2818 if (!wpraddress) {
2819 const auto& freewrap = sWrapperFree.find(key);
2820 if (freewrap != sWrapperFree.end() && !freewrap->second.empty()) {
2821 wpraddress = freewrap->second.back();
2822 freewrap->second.pop_back();
2826 if (wref) sWrapperWeakRefs[wref] = std::make_pair(wpraddress, key);
2827 else PyErr_Clear(); // happens for builtins which don't need this
2828 }
2829 }
2830
2831 // create wrapper if no re-use possible
2832 if (!wpraddress) {
2834 return nullptr;
2835
2836 // extract argument types
2837 const std::vector<std::string>& argtypes = TypeManip::extract_arg_types(signature);
2838 int nArgs = (int)argtypes.size();
2839
2840 // wrapper name
2841 std::ostringstream wname;
2842 wname << "fptr_wrap" << ++sWrapperCounter;
2843
2844 // build wrapper function code
2845 std::ostringstream code;
2846 code << "namespace __cppyy_internal {\n "
2847 << rettype << " " << wname.str() << "(";
2848 for (int i = 0; i < nArgs; ++i) {
2849 code << argtypes[i] << " arg" << i;
2850 if (i != nArgs-1) code << ", ";
2851 }
2852 code << ") {\n";
2853
2854 // start function body
2856
2857 // create a referenceable pointer
2858 PyObject** ref = new PyObject*{pyobject};
2859
2860 // function call itself and cleanup
2861 code << " PyObject** ref = (PyObject**)" << (intptr_t)ref << ";\n"
2862 " PyObject* pyresult = nullptr;\n"
2863 " if (*ref) pyresult = PyObject_CallFunctionObjArgs(*ref";
2864 for (int i = 0; i < nArgs; ++i)
2865 code << ", pyargs[" << i << "]";
2866 code << ", NULL);\n"
2867 " else PyErr_SetString(PyExc_TypeError, \"callable was deleted\");\n";
2868
2869 // close
2871
2872 // end of namespace
2873 code << "}";
2874
2875 // finally, compile the code
2876 if (!Cppyy::Compile(code.str()))
2877 return nullptr;
2878
2879 // TODO: is there no easier way?
2880 static Cppyy::TCppScope_t scope = Cppyy::GetScope("__cppyy_internal");
2881 const auto& idx = Cppyy::GetMethodIndicesFromName(scope, wname.str());
2884
2885 // cache the new wrapper
2888 if (wref) sWrapperWeakRefs[wref] = std::make_pair(wpraddress, key);
2889 else PyErr_Clear(); // happens for builtins which don't need this
2890 }
2891
2892 // now pass the pointer to the wrapper function (may be null)
2893 return wpraddress;
2894 }
2895
2896 return nullptr;
2897}
2898
2899bool CPyCppyy::FunctionPointerConverter::SetArg(
2901{
2902// special case: allow nullptr singleton:
2904 para.fValue.fVoidp = nullptr;
2905 para.fTypeCode = 'p';
2906 return true;
2907 }
2908
2909// normal case, get a function pointer
2911 if (fptr) {
2912 SetLifeLine(ctxt->fPyContext, pyobject, (intptr_t)this);
2913 para.fValue.fVoidp = fptr;
2914 para.fTypeCode = 'p';
2915 return true;
2916 }
2917
2918 return false;
2919}
2920
2921PyObject* CPyCppyy::FunctionPointerConverter::FromMemory(void* address)
2922{
2923// A function pointer in clang is represented by a Type, not a FunctionDecl and it's
2924// not possible to get the latter from the former: the backend will need to support
2925// both. Since that is far in the future, we'll use a std::function instead.
2926 if (address)
2927 return Utility::FuncPtr2StdFunction(fRetType, fSignature, *(void**)address);
2928 PyErr_SetString(PyExc_TypeError, "can not convert null function pointer");
2929 return nullptr;
2930}
2931
2932bool CPyCppyy::FunctionPointerConverter::ToMemory(
2933 PyObject* pyobject, void* address, PyObject* ctxt)
2934{
2935// special case: allow nullptr singleton:
2937 *((void**)address) = nullptr;
2938 return true;
2939 }
2940
2941// normal case, get a function pointer
2943 if (fptr) {
2944 SetLifeLine(ctxt, pyobject, (intptr_t)address);
2945 *((void**)address) = fptr;
2946 return true;
2947 }
2948
2949 return false;
2950}
2951
2952
2953//- std::function converter --------------------------------------------------
2954bool CPyCppyy::StdFunctionConverter::SetArg(
2956{
2957// prefer normal "object" conversion
2960 return true;
2961
2962 PyErr_Clear();
2963
2964// else create a wrapper function
2965 if (this->FunctionPointerConverter::SetArg(pyobject, para, ctxt)) {
2966 // retrieve the wrapper pointer and capture it in a temporary std::function,
2967 // then try normal conversion a second time
2968 PyObject* func = this->FunctionPointerConverter::FromMemory(&para.fValue.fVoidp);
2969 if (func) {
2970 SetLifeLine(ctxt->fPyContext, func, (intptr_t)this);
2971 bool result = fConverter->SetArg(func, para, ctxt);
2972 if (result) ctxt->AddTemporary(func);
2973 else Py_DECREF(func);
2974 return result;
2975 }
2976 }
2977
2978 return false;
2979}
2980
2981PyObject* CPyCppyy::StdFunctionConverter::FromMemory(void* address)
2982{
2983 return fConverter->FromMemory(address);
2984}
2985
2986bool CPyCppyy::StdFunctionConverter::ToMemory(PyObject* value, void* address, PyObject* ctxt)
2987{
2988// if the value is not an std::function<> but a generic Python callable, the
2989// conversion is done through the assignment, which may involve a temporary
2990 if (address) SetLifeLine(ctxt, value, (intptr_t)address);
2991 return fConverter->ToMemory(value, address, ctxt);
2992}
2993
2994
2995//- smart pointer converters -------------------------------------------------
2996bool CPyCppyy::SmartPtrConverter::SetArg(
2998{
2999 char typeCode = fIsRef ? 'p' : 'V';
3000
3002 // TODO: not sure how this is correct for pass-by-ref nor does it help with
3003 // implicit conversions for pass-by-value
3004 if (fIsRef && GetAddressSpecialCase(pyobject, para.fValue.fVoidp)) {
3005 para.fTypeCode = typeCode; // allow special cases such as nullptr
3006 return true;
3007 }
3008
3009 return false;
3010 }
3011
3013 Cppyy::TCppType_t oisa = pyobj->ObjectIsA();
3014
3015// for the case where we have a 'hidden' smart pointer:
3016 if (Cppyy::TCppType_t tsmart = pyobj->GetSmartIsA()) {
3018 // depending on memory policy, some objects need releasing when passed into functions
3020 ((CPPInstance*)pyobject)->CppOwns();
3021
3022 // calculate offset between formal and actual arguments
3023 para.fValue.fVoidp = pyobj->GetSmartObject();
3024 if (tsmart != fSmartPtrType) {
3025 para.fValue.fIntPtr += Cppyy::GetBaseOffset(
3026 tsmart, fSmartPtrType, para.fValue.fVoidp, 1 /* up-cast */);
3027 }
3028
3029 // set pointer (may be null) and declare success
3030 para.fTypeCode = typeCode;
3031 return true;
3032 }
3033 }
3034
3035// for the case where we have an 'exposed' smart pointer:
3036 if (!pyobj->IsSmart() && Cppyy::IsSubtype(oisa, fSmartPtrType)) {
3037 // calculate offset between formal and actual arguments
3038 para.fValue.fVoidp = pyobj->GetObject();
3039 if (oisa != fSmartPtrType) {
3040 para.fValue.fIntPtr += Cppyy::GetBaseOffset(
3041 oisa, fSmartPtrType, para.fValue.fVoidp, 1 /* up-cast */);
3042 }
3043
3044 // set pointer (may be null) and declare success
3045 para.fTypeCode = typeCode;
3046 return true;
3047 }
3048
3049// for the case where we have an ordinary object to convert
3051 // create the relevant smart pointer and make the pyobject "smart"
3053 if (!CPPInstance_Check(pysmart)) {
3055 return false;
3056 }
3057
3058 // copy internals from the fresh smart object to the original, making it smart
3059 pyobj->GetObjectRaw() = pysmart->GetSmartObject();
3060 pyobj->SetSmart(CreateScopeProxy(fSmartPtrType)); //(PyObject*)Py_TYPE(pysmart));
3061 pyobj->PythonOwns();
3062 pysmart->CppOwns();
3064
3065 return true;
3066 }
3067
3068// final option, try mapping pointer types held (TODO: do not allow for non-const ref)
3069 if (pyobj->IsSmart() && Cppyy::IsSubtype(oisa, fUnderlyingType)) {
3070 para.fValue.fVoidp = ((CPPInstance*)pyobject)->GetSmartObject();
3071 para.fTypeCode = 'V';
3072 return true;
3073 }
3074
3075 return false;
3076}
3077
3078PyObject* CPyCppyy::SmartPtrConverter::FromMemory(void* address)
3079{
3080 if (!address || !fSmartPtrType)
3081 return nullptr;
3082
3083 return BindCppObjectNoCast(address, fSmartPtrType);
3084}
3085
3086bool CPyCppyy::SmartPtrConverter::ToMemory(PyObject* value, void* address, PyObject*)
3087{
3088// assign value to C++ instance living at <address> through assignment operator (this
3089// is similar to InstanceConverter::ToMemory, but prevents wrapping the smart ptr)
3091#if PY_VERSION_HEX >= 0x03080000
3093#else
3094 PyObject* result = PyObject_CallMethod(pyobj, (char*)"__assign__", (char*)"O", value);
3095#endif
3097
3098 if (result) {
3100 return true;
3101 }
3102 return false;
3103}
3104
3105
3106//----------------------------------------------------------------------------
3107namespace {
3108
3109// clang libcpp and gcc use the same structure (ptr, size)
3110#if defined (_LIBCPP_INITIALIZER_LIST) || defined(__GNUC__)
3111struct faux_initlist
3112{
3113 typedef size_t size_type;
3114 typedef void* iterator;
3115 iterator _M_array;
3116 size_type _M_len;
3117};
3118#elif defined (_MSC_VER)
3119struct faux_initlist
3120{
3121 typedef char* iterator;
3122 iterator _M_array; // ie. _First;
3123 iterator _Last;
3124};
3125#else
3126#define NO_KNOWN_INITIALIZER_LIST 1
3127#endif
3128
3129} // unnamed namespace
3130
3131CPyCppyy::InitializerListConverter::InitializerListConverter(Cppyy::TCppType_t klass, std::string const &value_type)
3132
3133 : InstanceConverter{klass},
3134 fValueTypeName{value_type},
3135 fValueType{Cppyy::GetScope(value_type)},
3136 fValueSize{Cppyy::SizeOf(value_type)}
3137{
3138}
3139
3140CPyCppyy::InitializerListConverter::~InitializerListConverter()
3141{
3143 if (converter && converter->HasState()) delete converter;
3144 }
3145 if (fBuffer) Clear();
3146}
3147
3148void CPyCppyy::InitializerListConverter::Clear() {
3149 if (fValueType) {
3151#if defined (_LIBCPP_INITIALIZER_LIST) || defined(__GNUC__)
3152 for (faux_initlist::size_type i = 0; i < fake->_M_len; ++i) {
3153#elif defined (_MSC_VER)
3154 for (size_t i = 0; (fake->_M_array+i*fValueSize) != fake->_Last; ++i) {
3155#endif
3156 void* memloc = (char*)fake->_M_array + i*fValueSize;
3158 }
3159 }
3160
3162 fBuffer = nullptr;
3163}
3164
3165bool CPyCppyy::InitializerListConverter::SetArg(
3167{
3168#ifdef NO_KNOWN_INITIALIZER_LIST
3169 return false;
3170#else
3171 if (fBuffer) Clear();
3172
3173// convert the given argument to an initializer list temporary; this is purely meant
3174// to be a syntactic thing, so only _python_ sequences are allowed; bound C++ proxies
3175// (likely explicitly created std::initializer_list, go through an instance converter
3177#if PY_VERSION_HEX >= 0x03000000
3179#else
3181#endif
3182 )
3183 return false;
3184
3186 return this->InstanceConverter::SetArg(pyobject, para, ctxt);
3187
3188 void* buf = nullptr;
3189 Py_ssize_t buflen = Utility::GetBuffer(pyobject, '*', (int)fValueSize, buf, true);
3190 faux_initlist* fake = nullptr;
3191 size_t entries = 0;
3192 if (buf && buflen) {
3193 // dealing with an array here, pass on whole-sale
3195 fBuffer = (void*)fake;
3196 fake->_M_array = (faux_initlist::iterator)buf;
3197#if defined (_LIBCPP_INITIALIZER_LIST) || defined(__GNUC__)
3198 fake->_M_len = (faux_initlist::size_type)buflen;
3199#elif defined (_MSC_VER)
3200 fake->_Last = fake->_M_array+buflen*fValueSize;
3201#endif
3202 } else if (fValueSize) {
3203 // Remove any errors set by GetBuffer(); note that if the argument was an array
3204 // that failed to extract because of a type mismatch, the following will perform
3205 // a (rather inefficient) copy. No warning is issued b/c e.g. numpy doesn't do
3206 // so either.
3207 PyErr_Clear();
3208
3209 // Can only construct empty lists, so use a fake initializer list. For that we
3210 // need to construct default objects. Fail early if that cannot work.
3212 PyErr_SetString(PyExc_TypeError, "default constructor needed for initializer list of objects");
3213 return false;
3214 }
3215
3216 size_t len = (size_t)PySequence_Size(pyobject);
3218 fBuffer = (void*)fake;
3219 fake->_M_array = (faux_initlist::iterator)((char*)fake+sizeof(faux_initlist));
3220#if defined (_LIBCPP_INITIALIZER_LIST) || defined(__GNUC__)
3221 fake->_M_len = (faux_initlist::size_type)len;
3222 for (faux_initlist::size_type i = 0; i < fake->_M_len; ++i) {
3223#elif defined (_MSC_VER)
3224 fake->_Last = fake->_M_array+len*fValueSize;
3225 for (size_t i = 0; (fake->_M_array+i*fValueSize) != fake->_Last; ++i) {
3226#endif
3228 bool convert_ok = false;
3229 if (item) {
3230 if (fConverters.empty())
3232 if (!fConverters.back()) {
3233 if (CPPInstance_Check(item)) {
3234 // by convention, use byte copy
3235 memcpy((char*)fake->_M_array + i*fValueSize,
3236 ((CPPInstance*)item)->GetObject(), fValueSize);
3237 convert_ok = true;
3238 }
3239 } else {
3240 void* memloc = (char*)fake->_M_array + i*fValueSize;
3241 if (fValueType) {
3242 // we need to construct a default object for the constructor to assign into; this is
3243 // clunky, but the use of a copy constructor isn't much better as the Python object
3244 // need not be a C++ object
3246 // We checked above that we are able to construct default objects of fValueType.
3247 assert(memloc);
3248 entries += 1;
3249 }
3250 if (memloc) {
3251 if (i >= fConverters.size()) {
3253 }
3254 convert_ok = fConverters[i]->ToMemory(item, memloc);
3255 }
3256 }
3257
3258
3259 Py_DECREF(item);
3260 } else
3261 PyErr_Format(PyExc_TypeError, "failed to get item %d from sequence", (int)i);
3262
3263 if (!convert_ok) {
3264#if defined (_LIBCPP_INITIALIZER_LIST) || defined(__GNUC__)
3265 fake->_M_len = (faux_initlist::size_type)entries;
3266#elif defined (_MSC_VER)
3267 fake->_Last = fake->_M_array+entries*fValueSize;
3268#endif
3269 Clear();
3270 return false;
3271 }
3272 }
3273 }
3274
3275 if (!fake) // no buffer and value size indeterminate
3276 return false;
3277
3278 para.fValue.fVoidp = (void*)fake;
3279 para.fTypeCode = 'V'; // means ptr that backend has to free after call
3280 return true;
3281#endif
3282}
3283
3284namespace CPyCppyy {
3285
3286// raising converter to take out overloads
3288public:
3289 NotImplementedConverter(PyObject *errorType, std::string const &message) : fErrorType{errorType}, fMessage{message} {}
3290 bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override;
3291private:
3293 std::string fMessage;
3294};
3295
3296} // namespace CPyCppyy
3297
3298//----------------------------------------------------------------------------
3300{
3301// raise a NotImplemented exception to take a method out of overload resolution
3302 PyErr_SetString(fErrorType, fMessage.c_str());
3303 return false;
3304}
3305
3306
3307//- helper to refactor some code from CreateConverter ------------------------
3309 const std::string& cpd, CPyCppyy::cdims_t dims, bool isConst, bool control)
3310{
3311 using namespace CPyCppyy;
3312 Converter* result = nullptr;
3313
3314 if (cpd == "**" || cpd == "*[]" || cpd == "&*")
3316 else if (cpd == "*&")
3318 else if (cpd == "*" && dims.ndim() == UNKNOWN_SIZE) {
3321 }
3322 else if (cpd == "&")
3323 result = new InstanceRefConverter(klass, isConst);
3324 else if (cpd == "&&")
3325 result = new InstanceMoveConverter(klass);
3326 else if (cpd == "[]" || dims)
3327 result = new InstanceArrayConverter(klass, dims, false);
3328 else if (cpd == "") // by value
3329 result = new InstanceConverter(klass, true);
3330
3331 return result;
3332}
3333
3334//- factories ----------------------------------------------------------------
3337{
3338// The matching of the fulltype to a converter factory goes through up to five levels:
3339// 1) full, exact match
3340// 2) match of decorated, unqualified type
3341// 3) accept const ref as by value
3342// 4) accept ref as pointer
3343// 5) generalized cases (covers basically all C++ classes)
3344//
3345// If all fails, void is used, which will generate a run-time warning when used.
3346
3347// an exactly matching converter is best
3348 ConvFactories_t::iterator h = gConvFactories.find(fullType);
3349 if (h != gConvFactories.end()) {
3350 return (h->second)(dims);
3351 }
3352
3353// resolve typedefs etc.
3354 const std::string& resolvedType = Cppyy::ResolveName(fullType);
3355
3356// a full, qualified matching converter is preferred
3357 if (resolvedType != fullType) {
3359 if (h != gConvFactories.end())
3360 return (h->second)(dims);
3361 }
3362
3363//-- nothing? ok, collect information about the type and possible qualifiers/decorators
3364 bool isConst = strncmp(resolvedType.c_str(), "const", 5) == 0;
3365 const std::string& cpd = TypeManip::compound(resolvedType);
3366 std::string realType = TypeManip::clean_type(resolvedType, false, true);
3367
3368// mutable pointer references (T*&) are incompatible with Python's object model
3369 if (cpd == "*&") {
3371 "argument type '" + resolvedType + "' is not supported: non-const references to pointers (T*&) allow a"
3372 " function to replace the pointer itself. Python cannot represent this safely. Consider changing the"
3373 " C++ API to return the new pointer or use a wrapper"};
3374 }
3375
3376// accept unqualified type (as python does not know about qualifiers)
3377 h = gConvFactories.find((isConst ? "const " : "") + realType + cpd);
3378 if (h != gConvFactories.end())
3379 return (h->second)(dims);
3380
3381// drop const, as that is mostly meaningless to python (with the exception
3382// of c-strings, but those are specialized in the converter map)
3383 if (isConst) {
3384 h = gConvFactories.find(realType + cpd);
3385 if (h != gConvFactories.end())
3386 return (h->second)(dims);
3387 }
3388
3389//-- still nothing? try pointer instead of array (for builtins)
3390 if (cpd.compare(0, 3, "*[]") == 0) {
3391 // special case, array of pointers
3392 h = gConvFactories.find(realType + " ptr");
3393 if (h != gConvFactories.end()) {
3394 // upstream treats the pointer type as the array element type, but that pointer is
3395 // treated as a low-level view as well, unless it's a void*/char* so adjust the dims
3396 if (realType != "void" && realType != "char") {
3397 dim_t newdim = dims.ndim() == UNKNOWN_SIZE ? 2 : dims.ndim()+1;
3399 // TODO: sometimes the array size is known and can thus be verified; however,
3400 // currently the meta layer does not provide this information
3401 newdims[0] = dims ? dims[0] : UNKNOWN_SIZE; // the array
3402 newdims[1] = UNKNOWN_SIZE; // the pointer
3403 if (2 < newdim) {
3404 for (int i = 2; i < (newdim-1); ++i)
3405 newdims[i] = dims[i-1];
3406 }
3407
3408 return (h->second)(newdims);
3409 }
3410 return (h->second)(dims);
3411 }
3412
3413 } else if (!cpd.empty() && (std::string::size_type)std::count(cpd.begin(), cpd.end(), '*') == cpd.size()) {
3414 // simple array; set or resize as necessary
3415 h = gConvFactories.find(realType + " ptr");
3416 if (h != gConvFactories.end())
3417 return (h->second)((!dims && 1 < cpd.size()) ? dims_t(cpd.size()) : dims);
3418
3419 } else if (2 <= cpd.size() && (std::string::size_type)std::count(cpd.begin(), cpd.end(), '[') == cpd.size() / 2) {
3420 // fixed array, dims will have size if available
3421 h = gConvFactories.find(realType + " ptr");
3422 if (h != gConvFactories.end())
3423 return (h->second)(dims);
3424 }
3425
3426//-- special case: initializer list
3427 if (realType.compare(0, 16, "initializer_list") == 0) {
3428 // get the type of the list and create a converter (TODO: get hold of value_type?)
3429 auto pos = realType.find('<');
3430 std::string value_type = realType.substr(pos+1, realType.size()-pos-2);
3431 return new InitializerListConverter(Cppyy::GetScope(realType), value_type);
3432 }
3433
3434//-- still nothing? use a generalized converter
3435 bool control = cpd == "&" || isConst;
3436
3437//-- special case: std::function
3438 auto pos = resolvedType.find("function<");
3439 if (pos == 0 /* no std:: */ || pos == 5 /* with std:: */ ||
3440 pos == 6 /* const no std:: */ || pos == 11 /* const with std:: */ ) {
3441
3442 // get actual converter for normal passing
3445
3446 if (cnv) {
3447 // get the type of the underlying (TODO: use target_type?)
3448 auto pos1 = resolvedType.find("(", pos+9);
3449 auto pos2 = resolvedType.rfind(")");
3450 if (pos1 != std::string::npos && pos2 != std::string::npos) {
3451 auto sz1 = pos1-pos-9;
3452 if (resolvedType[pos+9+sz1-1] == ' ') sz1 -= 1;
3453
3454 return new StdFunctionConverter(cnv,
3455 resolvedType.substr(pos+9, sz1), resolvedType.substr(pos1, pos2-pos1+1));
3456 } else if (cnv->HasState())
3457 delete cnv;
3458 }
3459 }
3460
3461#if __cplusplus >= 202002L
3462//-- special case: std::span
3463 pos = resolvedType.find("span<");
3464 if (pos == 0 /* no std:: */ || pos == 5 /* with std:: */ ||
3465 pos == 6 /* const no std:: */ || pos == 11 /* const with std:: */ ) {
3466
3467 auto pos1 = realType.find('<');
3468 auto pos21 = realType.find(','); // for the case there are more template args
3469 auto pos22 = realType.find('>');
3470 auto len = std::min(pos21 - pos1, pos22 - pos1) - 1;
3471 std::string value_type = realType.substr(pos1+1, len);
3472
3473 // strip leading "const "
3474 const std::string cprefix = "const ";
3475 if (value_type.compare(0, cprefix.size(), cprefix) == 0) {
3476 value_type = value_type.substr(cprefix.size());
3477 }
3478
3479 std::string span_type = "std::span<" + value_type + ">";
3480
3481 return new StdSpanConverter{value_type, Cppyy::GetScope(span_type)};
3482 }
3483#endif
3484
3485// converters for known C++ classes and default (void*)
3486 Converter* result = nullptr;
3488 Cppyy::TCppType_t raw{0};
3489 if (Cppyy::GetSmartPtrInfo(realType, &raw, nullptr)) {
3490 if (cpd == "") {
3491 result = new SmartPtrConverter(klass, raw, control);
3492 } else if (cpd == "&") {
3493 result = new SmartPtrConverter(klass, raw);
3494 } else if (cpd == "*" && dims.ndim() == UNKNOWN_SIZE) {
3495 result = new SmartPtrConverter(klass, raw, control, true);
3496 }
3497 }
3498
3499 if (!result) {
3500 // Cling WORKAROUND -- special case for STL iterators
3502 static STLIteratorConverter c;
3503 result = &c;
3504 } else
3505 // -- Cling WORKAROUND
3507 }
3508 } else {
3509 std::smatch sm;
3510 if (std::regex_search(resolvedType, sm, s_fnptr)) {
3511 // this is a function pointer
3512 auto pos1 = sm.position(0);
3513 auto pos2 = resolvedType.rfind(')');
3514 result = new FunctionPointerConverter(
3515 resolvedType.substr(0, pos1), resolvedType.substr(pos1+sm.length(), pos2-1));
3516 }
3517 }
3518
3519 if (!result && cpd == "&&") {
3520 // for builtin, can use const-ref for r-ref
3521 h = gConvFactories.find("const " + realType + "&");
3522 if (h != gConvFactories.end())
3523 return (h->second)(dims);
3524 // else, unhandled moves
3525 result = new NotImplementedConverter{PyExc_NotImplementedError, "this method cannot (yet) be called"};
3526 }
3527
3528 if (!result && h != gConvFactories.end())
3529 // converter factory available, use it to create converter
3530 result = (h->second)(dims);
3531 else if (!result) {
3532 // default to something reasonable, assuming "user knows best"
3533 if (cpd.size() == 2 && cpd != "&&") // "**", "*[]", "*&"
3534 result = new VoidPtrPtrConverter(dims.ndim());
3535 else if (!cpd.empty())
3536 result = new VoidArrayConverter(); // "user knows best"
3537 else
3538 // fails on use
3539 result = new NotImplementedConverter{PyExc_NotImplementedError, "this method cannot (yet) be called"};
3540 }
3541
3542 return result;
3543}
3544
3545//----------------------------------------------------------------------------
3548{
3549 if (p && p->HasState())
3550 delete p; // state-less converters are always shared
3551}
3552
3553//----------------------------------------------------------------------------
3555bool CPyCppyy::RegisterConverter(const std::string& name, cf_t fac)
3556{
3557// register a custom converter
3558 auto f = gConvFactories.find(name);
3559 if (f != gConvFactories.end())
3560 return false;
3561
3563 return true;
3564}
3565
3566//----------------------------------------------------------------------------
3568bool CPyCppyy::RegisterConverterAlias(const std::string& name, const std::string& target)
3569{
3570// register a custom converter that is a reference to an existing converter
3571 auto f = gConvFactories.find(name);
3572 if (f != gConvFactories.end())
3573 return false;
3574
3575 auto t = gConvFactories.find(target);
3576 if (t == gConvFactories.end())
3577 return false;
3578
3579 gConvFactories[name] = t->second;
3580 return true;
3581}
3582
3583//----------------------------------------------------------------------------
3585bool CPyCppyy::UnregisterConverter(const std::string& name)
3586{
3587// remove a custom converter
3588 auto f = gConvFactories.find(name);
3589 if (f != gConvFactories.end()) {
3590 gConvFactories.erase(f);
3591 return true;
3592 }
3593 return false;
3594}
3595
3596
3597//----------------------------------------------------------------------------
3598namespace {
3599
3600using namespace CPyCppyy;
3601
3602inline static
3603std::string::size_type dims2stringsz(cdims_t d) {
3604 return (d && d.ndim() != UNKNOWN_SIZE) ? d[0] : std::string::npos;
3605}
3606
3607#define STRINGVIEW "basic_string_view<char,char_traits<char> >"
3608#define WSTRING1 "std::basic_string<wchar_t>"
3609#define WSTRING2 "std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>"
3610
3611//-- aliasing special case: C complex (is binary compatible with C++ std::complex)
3612#ifndef _WIN32
3613#define CCOMPLEX_D "_Complex double"
3614#define CCOMPLEX_F "_Complex float"
3615#else
3616#define CCOMPLEX_D "_C_double_complex"
3617#define CCOMPLEX_F "_C_float_complex"
3618#endif
3619
3620static struct InitConvFactories_t {
3621public:
3622 InitConvFactories_t() {
3623 // load all converter factories in the global map 'gConvFactories'
3625
3626 // factories for built-ins
3627 gf["bool"] = (cf_t)+[](cdims_t) { static BoolConverter c{}; return &c; };
3628 gf["const bool&"] = (cf_t)+[](cdims_t) { static ConstBoolRefConverter c{}; return &c; };
3629 gf["bool&"] = (cf_t)+[](cdims_t) { static BoolRefConverter c{}; return &c; };
3630 gf["char"] = (cf_t)+[](cdims_t) { static CharConverter c{}; return &c; };
3631 gf["const char&"] = (cf_t)+[](cdims_t) { static ConstCharRefConverter c{}; return &c; };
3632 gf["char&"] = (cf_t)+[](cdims_t) { static CharRefConverter c{}; return &c; };
3633 gf["signed char&"] = (cf_t)+[](cdims_t) { static SCharRefConverter c{}; return &c; };
3634 gf["unsigned char"] = (cf_t)+[](cdims_t) { static UCharConverter c{}; return &c; };
3635 gf["const unsigned char&"] = (cf_t)+[](cdims_t) { static ConstUCharRefConverter c{}; return &c; };
3636 gf["unsigned char&"] = (cf_t)+[](cdims_t) { static UCharRefConverter c{}; return &c; };
3637 gf["SCharAsInt"] = (cf_t)+[](cdims_t) { static SCharAsIntConverter c{}; return &c; };
3638 gf["UCharAsInt"] = (cf_t)+[](cdims_t) { static UCharAsIntConverter c{}; return &c; };
3639 gf["wchar_t"] = (cf_t)+[](cdims_t) { static WCharConverter c{}; return &c; };
3640 gf["char16_t"] = (cf_t)+[](cdims_t) { static Char16Converter c{}; return &c; };
3641 gf["char32_t"] = (cf_t)+[](cdims_t) { static Char32Converter c{}; return &c; };
3642 gf["wchar_t&"] = (cf_t)+[](cdims_t) { static WCharRefConverter c{}; return &c; };
3643 gf["char16_t&"] = (cf_t)+[](cdims_t) { static Char16RefConverter c{}; return &c; };
3644 gf["char32_t&"] = (cf_t)+[](cdims_t) { static Char32RefConverter c{}; return &c; };
3645 gf["int8_t"] = (cf_t)+[](cdims_t) { static Int8Converter c{}; return &c; };
3646 gf["const int8_t&"] = (cf_t)+[](cdims_t) { static ConstInt8RefConverter c{}; return &c; };
3647 gf["int8_t&"] = (cf_t)+[](cdims_t) { static Int8RefConverter c{}; return &c; };
3648 gf["int16_t"] = (cf_t)+[](cdims_t) { static Int16Converter c{}; return &c; };
3649 gf["const int16_t&"] = (cf_t)+[](cdims_t) { static ConstInt16RefConverter c{}; return &c; };
3650 gf["int16_t&"] = (cf_t)+[](cdims_t) { static Int16RefConverter c{}; return &c; };
3651 gf["int32_t"] = (cf_t)+[](cdims_t) { static Int32Converter c{}; return &c; };
3652 gf["const int32_t&"] = (cf_t)+[](cdims_t) { static ConstInt32RefConverter c{}; return &c; };
3653 gf["int32_t&"] = (cf_t)+[](cdims_t) { static Int32RefConverter c{}; return &c; };
3654 gf["uint8_t"] = (cf_t)+[](cdims_t) { static UInt8Converter c{}; return &c; };
3655 gf["const uint8_t&"] = (cf_t)+[](cdims_t) { static ConstUInt8RefConverter c{}; return &c; };
3656 gf["uint8_t&"] = (cf_t)+[](cdims_t) { static UInt8RefConverter c{}; return &c; };
3657 gf["uint16_t"] = (cf_t)+[](cdims_t) { static UInt16Converter c{}; return &c; };
3658 gf["const uint16_t&"] = (cf_t)+[](cdims_t) { static ConstUInt16RefConverter c{}; return &c; };
3659 gf["uint16_t&"] = (cf_t)+[](cdims_t) { static UInt16RefConverter c{}; return &c; };
3660 gf["uint32_t"] = (cf_t)+[](cdims_t) { static UInt32Converter c{}; return &c; };
3661 gf["const uint32_t&"] = (cf_t)+[](cdims_t) { static ConstUInt32RefConverter c{}; return &c; };
3662 gf["uint32_t&"] = (cf_t)+[](cdims_t) { static UInt32RefConverter c{}; return &c; };
3663 gf["short"] = (cf_t)+[](cdims_t) { static ShortConverter c{}; return &c; };
3664 gf["const short&"] = (cf_t)+[](cdims_t) { static ConstShortRefConverter c{}; return &c; };
3665 gf["short&"] = (cf_t)+[](cdims_t) { static ShortRefConverter c{}; return &c; };
3666 gf["unsigned short"] = (cf_t)+[](cdims_t) { static UShortConverter c{}; return &c; };
3667 gf["const unsigned short&"] = (cf_t)+[](cdims_t) { static ConstUShortRefConverter c{}; return &c; };
3668 gf["unsigned short&"] = (cf_t)+[](cdims_t) { static UShortRefConverter c{}; return &c; };
3669 gf["int"] = (cf_t)+[](cdims_t) { static IntConverter c{}; return &c; };
3670 gf["int&"] = (cf_t)+[](cdims_t) { static IntRefConverter c{}; return &c; };
3671 gf["const int&"] = (cf_t)+[](cdims_t) { static ConstIntRefConverter c{}; return &c; };
3672 gf["unsigned int"] = (cf_t)+[](cdims_t) { static UIntConverter c{}; return &c; };
3673 gf["const unsigned int&"] = (cf_t)+[](cdims_t) { static ConstUIntRefConverter c{}; return &c; };
3674 gf["unsigned int&"] = (cf_t)+[](cdims_t) { static UIntRefConverter c{}; return &c; };
3675 gf["long"] = (cf_t)+[](cdims_t) { static LongConverter c{}; return &c; };
3676 gf["long&"] = (cf_t)+[](cdims_t) { static LongRefConverter c{}; return &c; };
3677 gf["const long&"] = (cf_t)+[](cdims_t) { static ConstLongRefConverter c{}; return &c; };
3678 gf["unsigned long"] = (cf_t)+[](cdims_t) { static ULongConverter c{}; return &c; };
3679 gf["const unsigned long&"] = (cf_t)+[](cdims_t) { static ConstULongRefConverter c{}; return &c; };
3680 gf["unsigned long&"] = (cf_t)+[](cdims_t) { static ULongRefConverter c{}; return &c; };
3681 gf["long long"] = (cf_t)+[](cdims_t) { static LLongConverter c{}; return &c; };
3682 gf["const long long&"] = (cf_t)+[](cdims_t) { static ConstLLongRefConverter c{}; return &c; };
3683 gf["long long&"] = (cf_t)+[](cdims_t) { static LLongRefConverter c{}; return &c; };
3684 gf["unsigned long long"] = (cf_t)+[](cdims_t) { static ULLongConverter c{}; return &c; };
3685 gf["const unsigned long long&"] = (cf_t)+[](cdims_t) { static ConstULLongRefConverter c{}; return &c; };
3686 gf["unsigned long long&"] = (cf_t)+[](cdims_t) { static ULLongRefConverter c{}; return &c; };
3687
3688 gf["float"] = (cf_t)+[](cdims_t) { static FloatConverter c{}; return &c; };
3689 gf["const float&"] = (cf_t)+[](cdims_t) { static ConstFloatRefConverter c{}; return &c; };
3690 gf["float&"] = (cf_t)+[](cdims_t) { static FloatRefConverter c{}; return &c; };
3691 gf["double"] = (cf_t)+[](cdims_t) { static DoubleConverter c{}; return &c; };
3692 gf["double&"] = (cf_t)+[](cdims_t) { static DoubleRefConverter c{}; return &c; };
3693 gf["const double&"] = (cf_t)+[](cdims_t) { static ConstDoubleRefConverter c{}; return &c; };
3694 gf["long double"] = (cf_t)+[](cdims_t) { static LDoubleConverter c{}; return &c; };
3695 gf["const long double&"] = (cf_t)+[](cdims_t) { static ConstLDoubleRefConverter c{}; return &c; };
3696 gf["long double&"] = (cf_t)+[](cdims_t) { static LDoubleRefConverter c{}; return &c; };
3697 gf["std::complex<double>"] = (cf_t)+[](cdims_t) { return new ComplexDConverter{}; };
3698 gf["const std::complex<double>&"] = (cf_t)+[](cdims_t) { return new ComplexDConverter{}; };
3699 gf["void"] = (cf_t)+[](cdims_t) { static VoidConverter c{}; return &c; };
3700
3701 // pointer/array factories
3702 gf["bool ptr"] = (cf_t)+[](cdims_t d) { return new BoolArrayConverter{d}; };
3703 gf["signed char ptr"] = (cf_t)+[](cdims_t d) { return new SCharArrayConverter{d}; };
3704 gf["signed char**"] = (cf_t)+[](cdims_t) { return new SCharArrayConverter{{UNKNOWN_SIZE, UNKNOWN_SIZE}}; };
3705 gf["const unsigned char*"] = (cf_t)+[](cdims_t d) { return new UCharArrayConverter{d}; };
3706 gf["unsigned char ptr"] = (cf_t)+[](cdims_t d) { return new UCharArrayConverter{d}; };
3707 gf["SCharAsInt*"] = gf["signed char ptr"];
3708 gf["SCharAsInt[]"] = gf["signed char ptr"];
3709 gf["UCharAsInt*"] = gf["unsigned char ptr"];
3710 gf["UCharAsInt[]"] = gf["unsigned char ptr"];
3711 gf["std::byte ptr"] = (cf_t)+[](cdims_t d) { return new ByteArrayConverter{d}; };
3712 gf["int8_t ptr"] = (cf_t)+[](cdims_t d) { return new Int8ArrayConverter{d}; };
3713 gf["int16_t ptr"] = (cf_t)+[](cdims_t d) { return new Int16ArrayConverter{d}; };
3714 gf["int32_t ptr"] = (cf_t)+[](cdims_t d) { return new Int32ArrayConverter{d}; };
3715 gf["uint8_t ptr"] = (cf_t)+[](cdims_t d) { return new UInt8ArrayConverter{d}; };
3716 gf["uint16_t ptr"] = (cf_t)+[](cdims_t d) { return new UInt16ArrayConverter{d}; };
3717 gf["uint32_t ptr"] = (cf_t)+[](cdims_t d) { return new UInt32ArrayConverter{d}; };
3718 gf["short ptr"] = (cf_t)+[](cdims_t d) { return new ShortArrayConverter{d}; };
3719 gf["unsigned short ptr"] = (cf_t)+[](cdims_t d) { return new UShortArrayConverter{d}; };
3720 gf["int ptr"] = (cf_t)+[](cdims_t d) { return new IntArrayConverter{d}; };
3721 gf["unsigned int ptr"] = (cf_t)+[](cdims_t d) { return new UIntArrayConverter{d}; };
3722 gf["long ptr"] = (cf_t)+[](cdims_t d) { return new LongArrayConverter{d}; };
3723 gf["unsigned long ptr"] = (cf_t)+[](cdims_t d) { return new ULongArrayConverter{d}; };
3724 gf["long long ptr"] = (cf_t)+[](cdims_t d) { return new LLongArrayConverter{d}; };
3725 gf["unsigned long long ptr"] = (cf_t)+[](cdims_t d) { return new ULLongArrayConverter{d}; };
3726 gf["float ptr"] = (cf_t)+[](cdims_t d) { return new FloatArrayConverter{d}; };
3727 gf["double ptr"] = (cf_t)+[](cdims_t d) { return new DoubleArrayConverter{d}; };
3728 gf["long double ptr"] = (cf_t)+[](cdims_t d) { return new LDoubleArrayConverter{d}; };
3729 gf["std::complex<float> ptr"] = (cf_t)+[](cdims_t d) { return new ComplexFArrayConverter{d}; };
3730 gf["std::complex<double> ptr"] = (cf_t)+[](cdims_t d) { return new ComplexDArrayConverter{d}; };
3731 gf["void*"] = (cf_t)+[](cdims_t d) { return new VoidArrayConverter{(bool)d}; };
3732
3733 // aliases
3734 gf["signed char"] = gf["char"];
3735 gf["const signed char&"] = gf["const char&"];
3736 gf["std::byte"] = gf["uint8_t"];
3737 gf["byte"] = gf["uint8_t"];
3738 gf["const std::byte&"] = gf["const uint8_t&"];
3739 gf["const byte&"] = gf["const uint8_t&"];
3740 gf["std::byte&"] = gf["uint8_t&"];
3741 gf["byte&"] = gf["uint8_t&"];
3742 gf["std::int8_t"] = gf["int8_t"];
3743 gf["const std::int8_t&"] = gf["const int8_t&"];
3744 gf["std::int8_t&"] = gf["int8_t&"];
3745 gf["std::uint8_t"] = gf["uint8_t"];
3746 gf["const std::uint8_t&"] = gf["const uint8_t&"];
3747 gf["std::uint8_t&"] = gf["uint8_t&"];
3748 gf["internal_enum_type_t"] = gf["int"];
3749 gf["internal_enum_type_t&"] = gf["int&"];
3750 gf["const internal_enum_type_t&"] = gf["const int&"];
3751 gf["internal_enum_type_t ptr"] = gf["int ptr"];
3752#ifdef _WIN32
3753 gf["__int64"] = gf["long long"];
3754 gf["const __int64&"] = gf["const long long&"];
3755 gf["__int64&"] = gf["long long&"];
3756 gf["__int64 ptr"] = gf["long long ptr"];
3757 gf["unsigned __int64"] = gf["unsigned long long"];
3758 gf["const unsigned __int64&"] = gf["const unsigned long long&"];
3759 gf["unsigned __int64&"] = gf["unsigned long long&"];
3760 gf["unsigned __int64 ptr"] = gf["unsigned long long ptr"];
3761#endif
3762 gf[CCOMPLEX_D] = gf["std::complex<double>"];
3763 gf["const " CCOMPLEX_D "&"] = gf["const std::complex<double>&"];
3764 gf[CCOMPLEX_F " ptr"] = gf["std::complex<float> ptr"];
3765 gf[CCOMPLEX_D " ptr"] = gf["std::complex<double> ptr"];
3766
3767 // We always need these converters when cppyy is based on an unpatched
3768 // ROOT, because the "long long" types are always converted to Long64_t
3769 // and ULong64_t already at the ROOT Meta level.
3770 // See https://github.com/root-project/root/issues/15872#issuecomment-2174092763
3771 gf["Long64_t"] = gf["long long"];
3772 gf["Long64_t ptr"] = gf["long long ptr"];
3773 gf["Long64_t&"] = gf["long long&"];
3774 gf["const Long64_t&"] = gf["const long long&"];
3775 gf["ULong64_t"] = gf["unsigned long long"];
3776 gf["ULong64_t ptr"] = gf["unsigned long long ptr"];
3777 gf["ULong64_t&"] = gf["unsigned long long&"];
3778 gf["const ULong64_t&"] = gf["const unsigned long long&"];
3779
3780 // factories for special cases
3781 gf["nullptr_t"] = (cf_t)+[](cdims_t) { static NullptrConverter c{}; return &c;};
3782 gf["const char*"] = (cf_t)+[](cdims_t) { return new CStringConverter{}; };
3783 gf["const signed char*"] = gf["const char*"];
3784 gf["const char*&&"] = gf["const char*"];
3785 gf["const char[]"] = (cf_t)+[](cdims_t) { return new CStringConverter{}; };
3786 gf["char*"] = (cf_t)+[](cdims_t d) { return new NonConstCStringConverter{dims2stringsz(d)}; };
3787 gf["char[]"] = (cf_t)+[](cdims_t d) { return new NonConstCStringArrayConverter{d, true}; };
3788 gf["signed char*"] = gf["char*"];
3789 gf["wchar_t*"] = (cf_t)+[](cdims_t) { return new WCStringConverter{}; };
3790 // TODO: The libc++ library that is used most prominently by Apple for
3791 // macOS is using the _Nonnull Clang attribute for some of its string
3792 // constructors (_LIBCPP_DIAGNOSE_NULLPTR is a preprocessor macro that
3793 // resolves to _Nullable). The cppyy backend doesn't strip away such Clang
3794 // attributes, so the wchar_t* converter fails to match. To make the
3795 // construction of std::wstring work on macOS, we are explicitly creating
3796 // a converter for the wchar_t*_Nonnull type here, but we should try to
3797 // find a more general solution for dealing with Clang attributes.
3798 // [1] https://clang.llvm.org/docs/AttributeReference.html#id656
3799 // [2] https://github.com/llvm/llvm-project/blob/2078da43e25a4623cab2d0d60decddf709aaea28/libcxx/include/string#L1061
3800 gf["wchar_t*_Nonnull"] = (cf_t)+[](cdims_t) { return new WCStringConverter{}; };
3801 gf["char16_t*"] = (cf_t)+[](cdims_t) { return new CString16Converter{}; };
3802 gf["char16_t[]"] = (cf_t)+[](cdims_t d) { return new CString16Converter{dims2stringsz(d)}; };
3803 gf["char32_t*"] = (cf_t)+[](cdims_t) { return new CString32Converter{}; };
3804 gf["char32_t[]"] = (cf_t)+[](cdims_t d) { return new CString32Converter{dims2stringsz(d)}; };
3805 // TODO: the following are handled incorrectly upstream (char16_t** where char16_t* intended)?!
3806 gf["char16_t**"] = gf["char16_t*"];
3807 gf["char32_t**"] = gf["char32_t*"];
3808 gf["const char**"] = (cf_t)+[](cdims_t) { return new CStringArrayConverter{{UNKNOWN_SIZE, UNKNOWN_SIZE}, false}; };
3809 gf["char**"] = gf["const char**"];
3810 gf["const char*[]"] = (cf_t)+[](cdims_t d) { return new CStringArrayConverter{d, false}; };
3811 gf["char*[]"] = (cf_t)+[](cdims_t d) { return new NonConstCStringArrayConverter{d, false}; };
3812 gf["char ptr"] = gf["char*[]"];
3813 gf["std::string"] = (cf_t)+[](cdims_t) { return new STLStringConverter{}; };
3814 gf["const std::string&"] = gf["std::string"];
3815 gf["string"] = gf["std::string"];
3816 gf["const string&"] = gf["std::string"];
3817 gf["std::string&&"] = (cf_t)+[](cdims_t) { return new STLStringMoveConverter{}; };
3818 gf["string&&"] = gf["std::string&&"];
3819 gf["std::string_view"] = (cf_t)+[](cdims_t) { return new STLStringViewConverter{}; };
3820 gf[STRINGVIEW] = gf["std::string_view"];
3821 gf["std::string_view&"] = gf["std::string_view"];
3822 gf["const std::string_view&"] = gf["std::string_view"];
3823 gf["const " STRINGVIEW "&"] = gf["std::string_view"];
3824 gf["std::wstring"] = (cf_t)+[](cdims_t) { return new STLWStringConverter{}; };
3825 gf[WSTRING1] = gf["std::wstring"];
3826 gf[WSTRING2] = gf["std::wstring"];
3827 gf["const std::wstring&"] = gf["std::wstring"];
3828 gf["const " WSTRING1 "&"] = gf["std::wstring"];
3829 gf["const " WSTRING2 "&"] = gf["std::wstring"];
3830 gf["void*&"] = (cf_t)+[](cdims_t) { static VoidPtrRefConverter c{}; return &c; };
3831 gf["void**"] = (cf_t)+[](cdims_t d) { return new VoidPtrPtrConverter{d}; };
3832 gf["void ptr"] = gf["void**"];
3833 gf["PyObject*"] = (cf_t)+[](cdims_t) { static PyObjectConverter c{}; return &c; };
3834 gf["_object*"] = gf["PyObject*"];
3835 gf["FILE*"] = (cf_t)+[](cdims_t) { return new VoidArrayConverter{}; };
3836 }
3838
3839} // unnamed namespace
#define Py_TYPE(ob)
Definition CPyCppyy.h:196
static Py_ssize_t CPyCppyy_PyUnicode_AsWideChar(PyObject *pyobj, wchar_t *w, Py_ssize_t size)
Definition CPyCppyy.h:201
#define PyBytes_AS_STRING
Definition CPyCppyy.h:63
#define PyBytes_AsStringAndSize
Definition CPyCppyy.h:65
#define CPyCppyy_PyText_FromStringAndSize
Definition CPyCppyy.h:85
#define CPyCppyy_PyUnicode_GET_SIZE
Definition CPyCppyy.h:96
#define PY_SSIZE_T_FORMAT
Definition CPyCppyy.h:218
#define PyBytes_Check
Definition CPyCppyy.h:61
static void * CPyCppyy_PyCapsule_GetPointer(PyObject *capsule, const char *)
Definition CPyCppyy.h:104
#define CPyCppyy_PyText_AsString
Definition CPyCppyy.h:76
#define CPyCppyy_PyText_GET_SIZE
Definition CPyCppyy.h:78
#define CPyCppyy_PyCapsule_CheckExact
Definition CPyCppyy.h:103
static PyObject * PyObject_CallMethodOneArg(PyObject *obj, PyObject *name, PyObject *arg)
Definition CPyCppyy.h:377
#define PyBool_FromLong
Definition CPyCppyy.h:251
#define PyBytes_AsString
Definition CPyCppyy.h:64
#define Py_RETURN_NONE
Definition CPyCppyy.h:268
static const char * CPyCppyy_PyText_AsStringAndSize(PyObject *pystr, Py_ssize_t *size)
Definition CPyCppyy.h:87
#define PyBytes_GET_SIZE
Definition CPyCppyy.h:66
static PyObject * PyObject_CallMethodNoArgs(PyObject *obj, PyObject *name)
Definition CPyCppyy.h:373
#define CPyCppyy_PyText_FromString
Definition CPyCppyy.h:81
#define CPyCppyy_PyText_Check
Definition CPyCppyy.h:74
static bool StrictBool(PyObject *pyobject, CPyCppyy::CallContext *ctxt)
static unsigned short CPyCppyy_PyLong_AsUShort(PyObject *pyobject)
static int32_t CPyCppyy_PyLong_AsInt32(PyObject *pyobject)
#define ct_c_funcptr
static std::array< PyTypeObject *, 27 > gCTypesTypes
static std::map< void *, PyObject ** > sWrapperReference
static bool CPyCppyy_PyLong_AsBool(PyObject *pyobject)
static bool IsPyCArgObject(PyObject *pyobject)
static PyObject * WrapperCacheEraser(PyObject *, PyObject *pyref)
static bool CPyCppyy_PyUnicodeAsBytes2Buffer(PyObject *pyobject, T &buffer)
#define CPPYY_IMPL_STRING_AS_PRIMITIVE_CONVERTER(name, type, F1, F2)
#define CPPYY_IMPL_BASIC_CHAR_CONVERTER(name, type, low, high)
#define ct_c_pointer
static std::array< PyTypeObject *, 27 > gCTypesPtrTypes
#define ct_c_long
#define CPPYY_IMPL_BASIC_CONST_CHAR_REFCONVERTER(name, type, ctype, low, high)
static bool SetLifeLine(PyObject *holder, PyObject *target, intptr_t ref)
static PY_LONG_LONG CPyCppyy_PyLong_AsStrictLongLong(PyObject *pyobject)
static std::map< RetSigKey_t, std::map< PyObject *, void * > > sWrapperLookup
fBuffer
static bool ImplicitBool(PyObject *pyobject, CPyCppyy::CallContext *ctxt)
static bool CArraySetArg(PyObject *pyobject, CPyCppyy::Parameter &para, char tc, int size, bool check=true)
static int ExtractChar(PyObject *pyobject, const char *tname, int low, int high)
static bool HasLifeLine(PyObject *holder, intptr_t ref)
static uint32_t CPyCppyy_PyLong_AsUInt32(PyObject *pyobject)
const Py_ssize_t MOVE_REFCOUNT_CUTOFF
static PyTypeObject * GetCTypesPtrType(int nidx)
#define ct_c_double
static std::map< PyObject *, std::pair< void *, RetSigKey_t > > sWrapperWeakRefs
#define CPYCPPYY_WIDESTRING_CONVERTER(name, type, encode, decode, snull)
static void * PyFunction_AsCPointer(PyObject *pyobject, const std::string &rettype, const std::string &signature, bool allowCppInstance)
#define ct_c_char_p
std::string RetSigKey_t
#define ct_c_int
static int8_t CPyCppyy_PyLong_AsInt8(PyObject *pyobject)
#define CPPYY_IMPL_BASIC_CONVERTER_IB(name, type, stype, ctype, F1, F2, tc)
#define CCOMPLEX_D
static CPyCppyy::Converter * selectInstanceCnv(Cppyy::TCppScope_t klass, const std::string &cpd, CPyCppyy::cdims_t dims, bool isConst, bool control)
static CPyCppyy::CPPInstance * GetCppInstance(PyObject *pyobject, Cppyy::TCppType_t klass=(Cppyy::TCppType_t) 0, bool accept_rvalue=false)
static PyMethodDef gWrapperCacheEraserMethodDef
#define CPPYY_IMPL_BASIC_CONST_REFCONVERTER(name, type, ctype, F1)
static std::array< const char *, 27 > gCTypesNames
static uint8_t CPyCppyy_PyLong_AsUInt8(PyObject *pyobject)
#define CCOMPLEX_F
static short CPyCppyy_PyLong_AsShort(PyObject *pyobject)
static long CPyCppyy_PyLong_AsStrictLong(PyObject *pyobject)
#define CPPYY_IMPL_ARRAY_CONVERTER(name, ctype, type, code, suffix)
#define WSTRING1
#define CPPYY_IMPL_BASIC_CONVERTER_NB(name, type, stype, ctype, F1, F2, tc)
#define CPPYY_IMPL_REFCONVERTER(name, ctype, type, code)
#define CPPYY_IMPL_BASIC_CONVERTER_NI(name, type, stype, ctype, F1, F2, tc)
#define WSTRING2
static PyTypeObject * GetCTypesType(int nidx)
bool PyUnstable_Object_IsUniqueReferencedTemporary(PyObject *pyobject)
#define ct_c_void_p
static CPyCppyy::CPPInstance * ConvertImplicit(Cppyy::TCppType_t klass, PyObject *pyobject, CPyCppyy::Parameter &para, CPyCppyy::CallContext *ctxt, bool manage=true)
static int CPyCppyy_PyLong_AsStrictInt(PyObject *pyobject)
static int16_t CPyCppyy_PyLong_AsInt16(PyObject *pyobject)
static std::map< RetSigKey_t, std::vector< void * > > sWrapperFree
static bool IsCTypesArrayOrPointer(PyObject *pyobject)
static unsigned int sWrapperCounter
static uint16_t CPyCppyy_PyLong_AsUInt16(PyObject *pyobject)
#define STRINGVIEW
#define CPPYY_PYLONG_AS_TYPE(name, type, limit_low, limit_high)
#define CPPYY_IMPL_REFCONVERTER_FROM_MEMORY(name, ctype)
std::string fRetType
bool fAllowCppInstance
dims_t fShape
size_t fValueSize
bool fIsConst
std::string fValueTypeName
bool fIsFixed
Cppyy::TCppType_t fValueType
bool fKeepControl
bool fIsRef
Cppyy::TCppType_t fClass
Cppyy::TCppType_t fSmartPtrType
std::string fSignature
Cppyy::TCppType_t fUnderlyingType
Converter * fConverter
std::string::size_type fMaxSize
std::vector< Converter * > fConverters
_object PyObject
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define g(i)
Definition RSha256.hxx:105
#define h(i)
Definition RSha256.hxx:106
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t target
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 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 Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t UChar_t len
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 Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
char name[80]
Definition TGX11.cxx:157
float * q
#define CPYCPPYY_EXPORT
Definition CommonDefs.h:24
#define realloc
Definition civetweb.c:1577
#define free
Definition civetweb.c:1578
#define malloc
Definition civetweb.c:1575
virtual bool SetArg(PyObject *, Parameter &, CallContext *=nullptr)=0
virtual bool ToMemory(PyObject *value, void *address, PyObject *ctxt=nullptr)
virtual PyObject * FromMemory(void *address)
dim_t ndim() const
Definition Dimensions.h:61
bool ToMemory(PyObject *value, void *address, PyObject *ctxt=nullptr) override
bool SetArg(PyObject *, Parameter &, CallContext *=nullptr) override
PyObject * FromMemory(void *address) override
static bool RegisterPyObject(CPPInstance *pyobj, void *cppobj)
bool SetArg(PyObject *, Parameter &, CallContext *=nullptr) override
NotImplementedConverter(PyObject *errorType, std::string const &message)
PyObject * FromMemory(void *address) override
virtual bool GetAddressSpecialCase(PyObject *pyobject, void *&address)
bool ToMemory(PyObject *value, void *address, PyObject *ctxt=nullptr) override
bool SetArg(PyObject *, Parameter &, CallContext *=nullptr) override
Internal::TypedIter< T, decltype(std::begin(std::declval< Range_t >())), isDynamic > iterator
const_iterator begin() const
const_iterator end() const
unsigned long long PY_ULONG_LONG
Definition cpp_cppyy.h:24
long long PY_LONG_LONG
Definition cpp_cppyy.h:16
#define I(x, y, z)
#define H(x, y, z)
PyObject * gCastCpp
Definition PyStrings.cxx:15
PyObject * gAssign
Definition PyStrings.cxx:7
PyObject * gEmptyString
Definition PyStrings.cxx:21
std::string clean_type(const std::string &cppname, bool template_strip=true, bool const_strip=true)
std::string compound(const std::string &name)
std::vector< std::string > extract_arg_types(const std::string &sig)
void ConstructCallbackPreamble(const std::string &retType, const std::vector< std::string > &argtypes, std::ostringstream &code)
Definition Utility.cxx:714
void ConstructCallbackReturn(const std::string &retType, int nArgs, std::ostringstream &code)
Definition Utility.cxx:780
void RestorePyError(PyError_t &error)
Definition Utility.cxx:1209
std::map< std::string, char > const & TypecodeMap()
Definition Utility.cxx:893
Py_ssize_t GetBuffer(PyObject *pyobject, char tc, int size, void *&buf, bool check=true)
Definition Utility.cxx:920
PyObject * FuncPtr2StdFunction(const std::string &retType, const std::string &signature, void *address)
Definition Utility.cxx:817
PyError_t FetchPyError()
Definition Utility.cxx:1189
bool IsSTLIterator(const std::string &classname)
Definition Utility.cxx:1129
unsigned long PyLongOrInt_AsULong(PyObject *pyobject)
Definition Utility.cxx:133
Converter *(* cf_t)(cdims_t d)
Definition Converters.h:37
PyObject * gDefaultObject
PyObject * GetScopeProxy(Cppyy::TCppScope_t)
bool TupleOfInstances_CheckExact(T *object)
PyObject * CreateScopeProxy(Cppyy::TCppScope_t, const unsigned flags=0)
bool RefFloat_CheckExact(T *object)
Dimensions dims_t
Definition API.h:103
bool CPPExcInstance_Check(T *object)
bool NoImplicit(CallContext *ctxt)
static ConvFactories_t gConvFactories
PyObject * CreateLowLevelView(bool *, cdims_t shape)
PyObject * BindCppObjectNoCast(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
bool CPPOverload_Check(T *object)
Definition CPPOverload.h:94
bool RefInt_CheckExact(T *object)
PyObject * CreateLowLevelViewString(char **, cdims_t shape)
bool CPPScope_Check(T *object)
Definition CPPScope.h:81
CPYCPPYY_EXTERN bool RegisterConverter(const std::string &name, ConverterFactory_t)
static const dim_t UNKNOWN_SIZE
Definition Dimensions.h:11
bool LowLevelView_Check(T *object)
static std::regex s_fnptr("\\‍((\\w*:*)*\\*&*\\‍)")
bool AllowImplicit(CallContext *ctxt)
PY_ULONG_LONG PyLongOrInt_AsULong64(PyObject *pyobject)
Definition Utility.cxx:160
bool CPPInstance_Check(T *object)
PyObject * BindCppObjectArray(Cppyy::TCppObject_t address, Cppyy::TCppType_t klass, cdims_t dims)
PyObject * CreatePointerView(void *ptr, cdims_t shape=0)
PyObject * gNullPtrObject
bool IsConstructor(uint64_t flags)
CPYCPPYY_EXTERN bool RegisterConverterAlias(const std::string &name, const std::string &target)
CPYCPPYY_EXTERN Converter * CreateConverter(const std::string &name, cdims_t=0)
PyObject * BindCppObject(Cppyy::TCppObject_t object, Cppyy::TCppType_t klass, const unsigned flags=0)
bool UseStrictOwnership()
CPYCPPYY_EXTERN void DestroyConverter(Converter *p)
bool TemplateProxy_Check(T *object)
std::map< std::string, cf_t > ConvFactories_t
CPYCPPYY_EXTERN bool UnregisterConverter(const std::string &name)
RPY_EXPORTED ptrdiff_t GetBaseOffset(TCppType_t derived, TCppType_t base, TCppObject_t address, int direction, bool rerror=false)
RPY_EXPORTED size_t SizeOf(TCppType_t klass)
intptr_t TCppMethod_t
Definition cpp_cppyy.h:38
RPY_EXPORTED bool IsDefaultConstructable(TCppType_t type)
RPY_EXPORTED std::vector< TCppIndex_t > GetMethodIndicesFromName(TCppScope_t scope, const std::string &name)
RPY_EXPORTED bool Compile(const std::string &code, bool silent=false)
RPY_EXPORTED void CallDestructor(TCppType_t type, TCppObject_t self)
RPY_EXPORTED bool IsSubtype(TCppType_t derived, TCppType_t base)
RPY_EXPORTED TCppMethod_t GetMethodTemplate(TCppScope_t scope, const std::string &name, const std::string &proto)
void * TCppObject_t
Definition cpp_cppyy.h:37
RPY_EXPORTED TCppObject_t Construct(TCppType_t type, void *arena=nullptr)
RPY_EXPORTED bool GetSmartPtrInfo(const std::string &, TCppType_t *raw, TCppMethod_t *deref)
RPY_EXPORTED std::string ResolveName(const std::string &cppitem_name)
TCppScope_t TCppType_t
Definition cpp_cppyy.h:35
RPY_EXPORTED TCppMethod_t GetMethod(TCppScope_t scope, TCppIndex_t imeth)
RPY_EXPORTED bool IsSmartPtr(TCppType_t type)
RPY_EXPORTED TCppScope_t GetScope(const std::string &scope_name)
size_t TCppScope_t
Definition cpp_cppyy.h:34
RPY_EXPORTED TCppFuncAddr_t GetFunctionAddress(TCppMethod_t method, bool check_enabled=true)
RooArgList L(Args_t &&... args)
Definition RooArgList.h:156
PyObject_HEAD char * b_ptr
PyObject_HEAD void * pffi_type
union CPyCppyy_tagPyCArgObject::@198 value
TMarker m
Definition textangle.C:8
TLine l
Definition textangle.C:4