Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
Converters.cxx
Go to the documentation of this file.
1// Bindings
2#include "cpyrt.h"
3
4using namespace cppjit;
5#include "CPPExcInstance.h"
6#include "CPPInstance.h"
7#include "CPPOverload.h"
8#include "CallContext.h"
9#include "CustomPyTypes.h"
10#include "DeclareConverters.h"
11#include "LowLevelViews.h"
12#include "MemoryRegulator.h"
13#include "ProxyWrappers.h"
14#include "PyStrings.h"
15#include "TemplateProxy.h"
16#include "TupleOfInstances.h"
17#include "TypeManip.h"
18#include "Utility.h"
19#include "cppjit_interop.h"
20
21// Standard
22#include <algorithm>
23#include <array>
24#include <cassert>
25#include <complex>
26#include <cstddef>
27#include <limits.h>
28#include <locale> // for wstring_convert
29#include <sstream>
30#include <stddef.h> // for ptrdiff_t
31#include <string.h>
32#include <string_view>
33#include <utility>
34#include <vector>
35#if __cplusplus >= 202002L
36#include <span>
37#endif
38
39//- data _____________________________________________________________________
40namespace cppjit::cpyrt {
41
42// factories
43typedef std::unordered_map<std::string, cf_t> ConvFactories_t;
45
46// special objects
49} // namespace cppjit::cpyrt
50
51// Define our own PyUnstable_Object_IsUniqueReferencedTemporary function if the
52// Python version is lower than 3.14, the version where that function got
53// introduced.
54#if PY_VERSION_HEX < 0x030e0000
55const Py_ssize_t MOVE_REFCOUNT_CUTOFF = 1;
57 return Py_REFCNT(pyobject) <= MOVE_REFCOUNT_CUTOFF;
58}
59#endif
60
61//- pretend-ctypes helpers ---------------------------------------------------
62struct cpyrt_tagCDataObject { // non-public (but stable)
63 PyObject_HEAD char* b_ptr;
65};
66
67struct cpyrt_tagPyCArgObject { // not public (but stable; note that older
68 PyObject_HEAD // Pythons protect 'D' with HAVE_LONG_LONG)
69 void* pffi_type;
70 char tag;
71 union { // for convenience, kept only relevant vals
72 char c;
73 char b;
74 short h;
75 int i;
76 long l;
77 long long q;
78 long double g;
79 void* p;
80#if PY_VERSION_HEX >= 0x030e0000
81 double D[2];
82 float F[2];
83 long double G[2];
84#endif
87#if PY_VERSION_HEX >= 0x030e0000
88 Py_ssize_t size;
89#endif
90};
91
92// indices of ctypes types into the array caches (note that c_complex and
93// c_fcomplex do not exist as types in ctypes)
94#define ct_c_bool 0
95#define ct_c_char 1
96#define ct_c_shar 1
97#define ct_c_wchar 2
98#define ct_c_byte 3
99#define ct_c_int8 3
100#define ct_c_ubyte 4
101#define ct_c_uchar 4
102#define ct_c_uint8 4
103#define ct_c_short 5
104#define ct_c_ushort 6
105#define ct_c_uint16 7
106#define ct_c_int 8
107#define ct_c_uint 9
108#define ct_c_uint32 10
109#define ct_c_long 11
110#define ct_c_ulong 12
111#define ct_c_longlong 13
112#define ct_c_ulonglong 14
113#define ct_c_float 15
114#define ct_c_double 16
115#define ct_c_longdouble 17
116#define ct_c_char_p 18
117#define ct_c_wchar_p 19
118#define ct_c_void_p 20
119#define ct_c_fcomplex 21
120#define ct_c_complex 22
121#define ct_c_pointer 23
122#define ct_c_funcptr 24
123#define ct_c_int16 25
124#define ct_c_int32 26
125#define NTYPES 27
126
127static std::array<const char*, NTYPES> gCTypesNames = {
128 "c_bool", "c_char", "c_wchar", "c_byte", "c_ubyte",
129 "c_short", "c_ushort", "c_uint16", "c_int", "c_uint",
130 "c_uint32", "c_long", "c_ulong", "c_longlong", "c_ulonglong",
131 "c_float", "c_double", "c_longdouble", "c_char_p", "c_wchar_p",
132 "c_void_p", "c_fcomplex", "c_complex", "_Pointer", "_CFuncPtr",
133 "c_int16", "c_int32"};
134static std::array<PyTypeObject*, NTYPES> gCTypesTypes;
135static std::array<PyTypeObject*, NTYPES> gCTypesPtrTypes;
136
137// Both GetCTypesType and GetCTypesPtrType, rely on the ctypes module itself
138// caching the types (thus also making them unique), so no ref-count is needed.
139// Further, by keeping a ref-count on the module, it won't be off-loaded until
140// the 2nd cleanup cycle.
141static PyTypeObject* GetCTypesType(int nidx) {
142 static PyObject* ctmod = PyImport_ImportModule("ctypes"); // ref-count kept
143 if (!ctmod) {
144 PyErr_Clear();
145 return nullptr;
146 }
147 PyTypeObject* ct_t = gCTypesTypes[nidx];
148 if (!ct_t) {
149 ct_t = (PyTypeObject*)PyObject_GetAttrString(ctmod, gCTypesNames[nidx]);
150 if (!ct_t)
151 PyErr_Clear();
152 else {
153 gCTypesTypes[nidx] = ct_t;
154 Py_DECREF(ct_t);
155 }
156 }
157 return ct_t;
158}
159
160static PyTypeObject* GetCTypesPtrType(int nidx) {
161 static PyObject* ctmod = PyImport_ImportModule("ctypes"); // ref-count kept
162 if (!ctmod) {
163 PyErr_Clear();
164 return nullptr;
165 }
166 PyTypeObject* cpt_t = gCTypesPtrTypes[nidx];
167 if (!cpt_t) {
168 if (strcmp(gCTypesNames[nidx], "c_char") == 0) {
169 cpt_t = (PyTypeObject*)PyObject_GetAttrString(ctmod, "c_char_p");
170 } else {
171 PyObject* ct_t = (PyObject*)GetCTypesType(nidx);
172 if (ct_t) {
173 PyObject* ptrcreat = PyObject_GetAttrString(ctmod, "POINTER");
174 cpt_t =
175 (PyTypeObject*)PyObject_CallFunctionObjArgs(ptrcreat, ct_t, NULL);
176 Py_DECREF(ptrcreat);
177 }
178 }
179 if (cpt_t) {
180 gCTypesPtrTypes[nidx] = cpt_t;
181 Py_DECREF(cpt_t);
182 }
183 }
184 return cpt_t;
185}
186
187static bool IsPyCArgObject(PyObject* pyobject) {
188 static PyTypeObject* pycarg_type = nullptr;
189 if (!pycarg_type) {
190 PyObject* ctmod = PyImport_ImportModule("ctypes");
191 if (!ctmod)
192 PyErr_Clear();
193 else {
194 PyTypeObject* ct_t =
195 (PyTypeObject*)PyObject_GetAttrString(ctmod, "c_int");
196 PyObject* cobj = ct_t->tp_new(ct_t, nullptr, nullptr);
197 PyObject* byref = PyObject_GetAttrString(ctmod, "byref");
198 PyObject* pyptr = PyObject_CallFunctionObjArgs(byref, cobj, NULL);
199 Py_DECREF(byref);
200 Py_DECREF(cobj);
201 Py_DECREF(ct_t);
202 pycarg_type = Py_TYPE(pyptr); // static, no ref-count needed
203 Py_DECREF(pyptr);
204 Py_DECREF(ctmod);
205 }
206 }
207 return Py_TYPE(pyobject) == pycarg_type;
208}
209
210#if PY_VERSION_HEX < 0x30d0000
211static bool IsCTypesArrayOrPointer(PyObject* pyobject) {
212 static PyTypeObject* cstgdict_type = nullptr;
213 if (!cstgdict_type) {
214 // get any pointer type to initialize the extended dictionary type
215 PyTypeObject* ct_int = GetCTypesType(ct_c_int);
216 if (ct_int && ct_int->tp_dict) {
217 cstgdict_type = Py_TYPE(ct_int->tp_dict);
218 }
219 }
220
221 PyTypeObject* pytype = Py_TYPE(pyobject);
222 if (pytype->tp_dict && Py_TYPE(pytype->tp_dict) == cstgdict_type)
223 return true;
224 return false;
225}
226#else
227// the internals of ctypes have been redone, requiring a more complex checking
228namespace {
229
230typedef struct {
231 PyTypeObject* DictRemover_Type;
232 PyTypeObject* PyCArg_Type;
233 PyTypeObject* PyCField_Type;
234 PyTypeObject* PyCThunk_Type;
235 PyTypeObject* StructParam_Type;
236 PyTypeObject* PyCType_Type;
237 PyTypeObject* PyCStructType_Type;
238 PyTypeObject* UnionType_Type;
239 PyTypeObject* PyCPointerType_Type;
240 // ... unused fields omitted ...
241} _cppjit_ctypes_state;
242
243} // unnamed namespace
244
245static bool IsCTypesArrayOrPointer(PyObject* pyobject) {
246 static _cppjit_ctypes_state* state = nullptr;
247 if (!state) {
248 PyObject* ctmod = PyImport_AddModule(
249 "_ctypes"); // the extension module, not the Python one
250 if (ctmod)
251 state = (_cppjit_ctypes_state*)PyModule_GetState(ctmod);
252 }
253
254 // verify for object types that have a C payload
255 if (state && (PyObject_IsInstance((PyObject*)Py_TYPE(pyobject),
256 (PyObject*)state->PyCType_Type) ||
257 PyObject_IsInstance((PyObject*)Py_TYPE(pyobject),
258 (PyObject*)state->PyCPointerType_Type))) {
259 return true;
260 }
261
262 return false;
263}
264#endif
265
266//- helper to establish life lines -------------------------------------------
267static inline bool SetLifeLine(PyObject* holder, PyObject* target,
268 intptr_t ref) {
269 // set a lifeline from on the holder to the target, using the ref as label
270 if (!holder)
271 return false;
272
273 // 'ref' is expected to be the converter address or data memory location, so
274 // that the combination of holder and ref is unique, but also identifiable for
275 // reuse when the C++ side is being overwritten
276 std::ostringstream attr_name;
277 attr_name << "__" << ref;
278 auto res =
279 PyObject_SetAttrString(holder, (char*)attr_name.str().c_str(), target);
280 return res != -1;
281}
282
283static bool HasLifeLine(PyObject* holder, intptr_t ref) {
284 // determine if a lifeline was previously set for the ref on the holder
285 if (!holder)
286 return false;
287
288 std::ostringstream attr_name;
289 attr_name << "__" << ref;
290 PyObject* res =
291 PyObject_GetAttrString(holder, (char*)attr_name.str().c_str());
292
293 if (res) {
294 Py_DECREF(res);
295 return true;
296 }
297
298 PyErr_Clear();
299 return false;
300}
301
302//- helper to work with both CPPInstance and CPPExcInstance ------------------
303static inline cpyrt::CPPInstance*
306 bool accept_rvalue = false) {
307 using namespace cppjit::cpyrt;
308 if (CPPInstance_Check(pyobject))
309 return (CPPInstance*)pyobject;
310 if (CPPExcInstance_Check(pyobject))
311 return (CPPInstance*)((CPPExcInstance*)pyobject)->fCppInstance;
312
313 // this is not a C++ proxy; allow custom cast to C++
314 PyObject* castobj = PyObject_CallMethodNoArgs(pyobject, PyStrings::gCastCpp);
315 if (castobj) {
316 if (CPPInstance_Check(castobj))
317 return (CPPInstance*)castobj;
318 else if (klass && PyTuple_CheckExact(castobj)) {
319 // allow implicit conversion from a tuple of arguments
320 PyObject* pyclass = GetScopeProxy(klass);
321 if (pyclass) {
322 CPPInstance* pytmp =
323 (CPPInstance*)PyObject_Call(pyclass, castobj, NULL);
324 Py_DECREF(pyclass);
325 if (CPPInstance_Check(pytmp)) {
326 if (accept_rvalue)
328 Py_DECREF(castobj);
329 return pytmp;
330 }
331 Py_XDECREF(pytmp);
332 }
333 }
334
335 Py_DECREF(castobj);
336 return nullptr;
337 }
338
339 PyErr_Clear();
340 return nullptr;
341}
342
343//- custom helpers to check ranges -------------------------------------------
344static inline bool IsCPPEnumInstance(PyObject* pyobject) {
345 // cppjit enums are int subclasses created with __underlying on the class
346 if (!PyLong_Check(pyobject) || PyLong_CheckExact(pyobject) ||
347 PyBool_Check(pyobject))
348 return false;
349 return PyObject_HasAttr((PyObject*)Py_TYPE(pyobject),
351}
352
353// bools and enum instances match integer parameters in the implicit round
354// only; a nested call under kNoImplicit is that round (an implicit
355// construction from the argument), so enum instances pass there
356static inline bool ImplicitBool(PyObject* pyobject, cpyrt::CallContext* ctxt) {
357 using namespace cppjit::cpyrt;
358 if (!AllowImplicit(ctxt) &&
359 (PyBool_Check(pyobject) ||
360 (IsCPPEnumInstance(pyobject) && !NoImplicit(ctxt)))) {
361 if (!NoImplicit(ctxt))
363 return false;
364 }
365 return true;
366}
367
368static inline bool StrictBool(PyObject* pyobject, cpyrt::CallContext* ctxt) {
369 using namespace cppjit::cpyrt;
370 if (!AllowImplicit(ctxt) && !PyBool_Check(pyobject)) {
371 if (!NoImplicit(ctxt))
373 return false;
374 }
375 return true;
376}
377
378static inline bool cpyrt_PyLong_AsBool(PyObject* pyobject) {
379 // range-checking python integer to C++ bool conversion
380 long l = PyLong_AsLong(pyobject);
381 // fail to pass float -> bool; the problem is rounding (0.1 -> 0 -> False)
382 if (!(l == 0 || l == 1) || PyFloat_Check(pyobject)) {
383 PyErr_SetString(PyExc_ValueError,
384 "boolean value should be bool, or integer 1 or 0");
385 return (bool)-1;
386 }
387 return (bool)l;
388}
389
390// range-checking python integer to C++ integer conversion (prevents p2.7 silent
391// conversions)
392#define CPPJIT_PYLONG_AS_TYPE(name, type, limit_low, limit_high) \
393 static inline type cpyrt_PyLong_As##name(PyObject* pyobject) { \
394 if (!PyLong_Check(pyobject)) { \
395 if (pyobject == cpyrt::gDefaultObject) \
396 return (type)0; \
397 PyErr_SetString(PyExc_TypeError, \
398 #type " conversion expects an integer object"); \
399 return (type) - 1; \
400 } \
401 long l = PyLong_AsLong(pyobject); \
402 if (l < limit_low || limit_high < l) { \
403 PyErr_Format(PyExc_ValueError, "integer %ld out of range for " #type, \
404 l); \
405 return (type) - 1; \
406 } \
407 return (type)l; \
408 }
409
410CPPJIT_PYLONG_AS_TYPE(UInt8, uint8_t, 0, UCHAR_MAX)
411CPPJIT_PYLONG_AS_TYPE(Int8, int8_t, SCHAR_MIN, SCHAR_MAX)
412CPPJIT_PYLONG_AS_TYPE(UInt16, uint16_t, 0, UINT16_MAX)
413CPPJIT_PYLONG_AS_TYPE(Int16, int16_t, INT16_MIN, INT16_MAX)
414CPPJIT_PYLONG_AS_TYPE(UInt32, uint32_t, 0, UINT32_MAX)
415CPPJIT_PYLONG_AS_TYPE(Int32, int32_t, INT32_MIN, INT32_MAX)
416CPPJIT_PYLONG_AS_TYPE(UShort, unsigned short, 0, USHRT_MAX)
417CPPJIT_PYLONG_AS_TYPE(Short, short, SHRT_MIN, SHRT_MAX)
418CPPJIT_PYLONG_AS_TYPE(StrictInt, int, INT_MIN, INT_MAX)
419
420static inline long cpyrt_PyLong_AsStrictLong(PyObject* pyobject) {
421 // strict python integer to C++ long integer conversion
422
423 // prevent float -> long (see cpyrt_PyLong_AsStrictInt)
424 if (!PyLong_Check(pyobject)) {
425 if (pyobject == cpyrt::gDefaultObject)
426 return (long)0;
427 PyErr_SetString(PyExc_TypeError,
428 "int/long conversion expects an integer object");
429 return (long)-1;
430 }
431
432 return (long)PyLong_AsLong(pyobject); // already does long range check
433}
434
436 // strict python integer to C++ long long integer conversion
437
438 // prevent float -> long (see cpyrt_PyLong_AsStrictInt)
439 if (!PyLong_Check(pyobject)) {
440 if (pyobject == cpyrt::gDefaultObject)
441 return (PY_LONG_LONG)0;
442 PyErr_SetString(PyExc_TypeError,
443 "int/long conversion expects an integer object");
444 return (PY_LONG_LONG)-1;
445 }
446
447 return PyLong_AsLongLong(pyobject); // already does long range check
448}
449
450//- helper for pointer/array/reference conversions ---------------------------
451static inline bool CArraySetArg(PyObject* pyobject, cpyrt::Parameter& para,
452 char tc, int size, bool check = true) {
453 // Case of LowLevelView. In general, they also implement the buffer protocol,
454 // but for views around nullptr or C-style arrays without size info the buffer
455 // protocol implementation is incomplete and PyObject_GetBuffer will fail.
456 if (cpyrt::LowLevelView_Check(pyobject)) {
457 auto llview = ((cpyrt::LowLevelView*)pyobject);
458 if (llview->fBufInfo.itemsize != size ||
459 !cpyrt::Utility::BufferFormatCompatible(tc, llview->fBufInfo.format)) {
460 PyErr_Format(PyExc_TypeError,
461 "could not convert argument to buffer or nullptr");
462 return false;
463 }
464
465 para.fValue.fVoidp = llview->get_buf();
466 }
467 // general case of loading a C array pointer (void* + type code) as function
468 // argument
469 else if (pyobject == cpyrt::gNullPtrObject ||
470 pyobject == cpyrt::gDefaultObject)
471 para.fValue.fVoidp = nullptr;
472 else {
473 Py_ssize_t buflen = cpyrt::Utility::GetBuffer(pyobject, tc, size,
474 para.fValue.fVoidp, check);
475 if (!buflen) {
476 // stuck here as it's the least common
477 if (cpyrt_PyLong_AsStrictInt(pyobject) == 0)
478 para.fValue.fVoidp = nullptr;
479 else {
480 PyErr_Format(PyExc_TypeError, // ValueError?
481 "could not convert argument to buffer or nullptr");
482 return false;
483 }
484 }
485 }
486 para.fTypeCode = 'p';
487 return true;
488}
489
490//- helper for implicit conversions ------------------------------------------
492 PyObject* pyobject,
493 cpyrt::Parameter& para,
494 cpyrt::CallContext* ctxt,
495 bool manage = true) {
496 using namespace cppjit::cpyrt;
497
498 // filter out copy and move constructors
499 if (IsConstructor(ctxt->fFlags) && klass == ctxt->fCurScope &&
500 ctxt->GetSize() == 1)
501 return nullptr;
502
503 // never attempt implicit conversion to standard library implementation
504 // details (leading underscore, e.g. MSVC's std::_Swc_base, the base of its
505 // random engines, which has an unconstrained generator constructor that
506 // accepts anything): no user-facing API expects them, and every failed
507 // attempt JIT-compiles a constructor wrapper, which is prohibitively slow
508 // when it happens per call in overload resolution (seen as pyunittests
509 // timeouts on the Windows CI). The one legitimate case is a conversion
510 // between instantiations of the _same_ implementation-detail template,
511 // e.g. libc++'s iterator to const_iterator (std::__wrap_iter<char*> to
512 // std::__wrap_iter<const char*>), which uses a converting constructor.
513 {
514 const std::string& clName = interop::GetScopedFinalName(klass);
515 if (clName.compare(0, 6, "std::_") == 0 ||
516 (!clName.empty() && clName[0] == '_')) {
517 bool sameTemplate = false;
518 if (CPPInstance_Check(pyobject)) {
519 interop::TCppScope_t src = ((CPPInstance*)pyobject)->ObjectIsA();
520 if (src && src != klass) {
521 const std::string& srcName = interop::GetScopedFinalName(src);
522 std::string::size_type lt = clName.find('<');
523 sameTemplate = lt != std::string::npos && srcName.size() > lt &&
524 srcName[lt] == '<' &&
525 srcName.compare(0, lt, clName, 0, lt) == 0;
526 }
527 }
528 if (!sameTemplate)
529 return nullptr;
530 }
531 }
532
533 // only proceed if implicit conversions are allowed (in "round 2") or if the
534 // argument is exactly a tuple or list, as these are the equivalent of
535 // initializer lists and thus "syntax" not a conversion
536 if (!AllowImplicit(ctxt)) {
537 PyTypeObject* pytype = (PyTypeObject*)Py_TYPE(pyobject);
538 if (!(pytype == &PyList_Type ||
539 pytype == &PyTuple_Type)) { // || !CPPInstance_Check(pyobject))) {
540 if (!NoImplicit(ctxt))
542 return nullptr;
543 }
544 }
545
546 // exercise implicit conversion
547 PyObject* pyscope = CreateScopeProxy(klass);
548 if (!CPPScope_Check(pyscope)) {
549 Py_XDECREF(pyscope);
550 return nullptr;
551 }
552
553 // call constructor of argument type to attempt implicit conversion (disallow
554 // any implicit conversions by the scope's constructor itself)
555 PyObject* args = PyTuple_New(1);
556 Py_INCREF(pyobject);
557 PyTuple_SET_ITEM(args, 0, pyobject);
558
559 ((CPPScope*)pyscope)->fFlags |= CPPScope::kActiveImplicitCall;
560 CPPInstance* pytmp = (CPPInstance*)PyObject_Call(pyscope, args, NULL);
561 if (!pytmp && PyTuple_CheckExact(pyobject)) {
562 // special case: allow implicit conversion from given set of arguments in
563 // tuple
564 PyErr_Clear();
565 pytmp = (CPPInstance*)PyObject_Call(pyscope, pyobject, NULL);
566 }
567 ((CPPScope*)pyscope)->fFlags &= ~CPPScope::kActiveImplicitCall;
568
569 Py_DECREF(args);
570 Py_DECREF(pyscope);
571
572 if (pytmp) {
573 // implicit conversion succeeded!
574 if (manage)
575 ctxt->AddTemporary((PyObject*)pytmp);
576 para.fValue.fVoidp = pytmp->GetObjectRaw();
577 para.fTypeCode = 'V';
578 return pytmp;
579 }
580
581 PyErr_Clear();
582 return nullptr;
583}
584
585//- base converter implementation --------------------------------------------
587
588//----------------------------------------------------------------------------
590 // could happen if no derived class override
591 PyErr_SetString(PyExc_TypeError, "C++ type cannot be converted from memory");
592 return nullptr;
593}
594
595//----------------------------------------------------------------------------
597 PyObject* /* ctxt */) {
598 // could happen if no derived class override
599 PyErr_SetString(PyExc_TypeError, "C++ type cannot be converted to memory");
600 return false;
601}
602
603//- helper macro's -----------------------------------------------------------
604#define CPPJIT_IMPL_BASIC_CONVERTER_BODY(name, type, stype, ctype, F1, F2, tc) \
605 /* convert <pyobject> to C++ 'type', set arg for call */ \
606 type val = (type)F2(pyobject); \
607 if (val == (type) - 1 && PyErr_Occurred()) { \
608 static PyTypeObject* ctypes_type = nullptr; \
609 if (!ctypes_type) { \
610 auto error = cpyrt::Utility::FetchPyError(); \
611 ctypes_type = GetCTypesType(ct_##ctype); \
612 cpyrt::Utility::RestorePyError(error); \
613 } \
614 if (Py_TYPE(pyobject) == ctypes_type) { \
615 PyErr_Clear(); \
616 val = *((type*)((cpyrt_tagCDataObject*)pyobject)->b_ptr); \
617 } else if (pyobject == cpyrt::gDefaultObject) { \
618 PyErr_Clear(); \
619 val = (type)0; \
620 } else \
621 return false; \
622 } \
623 para.fValue.f##name = val; \
624 para.fTypeCode = tc; \
625 return true;
626
627#define CPPJIT_IMPL_BASIC_CONVERTER_METHODS(name, type, stype, ctype, F1, F2) \
628 PyObject* cpyrt::name##Converter::FromMemory(void* address) { \
629 return F1((stype) * ((type*)address)); \
630 } \
631 \
632 bool cpyrt::name##Converter::ToMemory(PyObject* value, void* address, \
633 PyObject* /* ctxt */) { \
634 type s = (type)F2(value); \
635 if (s == (type) - 1 && PyErr_Occurred()) { \
636 if (value == cpyrt::gDefaultObject) { \
637 PyErr_Clear(); \
638 s = (type)0; \
639 } else \
640 return false; \
641 } \
642 *((type*)address) = (type)s; \
643 return true; \
644 }
645
646#define CPPJIT_IMPL_BASIC_CONVERTER_NI(name, type, stype, ctype, F1, F2, tc) \
647 bool cpyrt::name##Converter::SetArg(PyObject* pyobject, Parameter& para, \
648 CallContext* ctxt) { \
649 if (!StrictBool(pyobject, ctxt)) \
650 return false; \
651 CPPJIT_IMPL_BASIC_CONVERTER_BODY(name, type, stype, ctype, F1, F2, tc) \
652 } \
653 CPPJIT_IMPL_BASIC_CONVERTER_METHODS(name, type, stype, ctype, F1, F2)
654
655#define CPPJIT_IMPL_BASIC_CONVERTER_IB(name, type, stype, ctype, F1, F2, tc) \
656 bool cpyrt::name##Converter::SetArg(PyObject* pyobject, Parameter& para, \
657 CallContext* ctxt) { \
658 if (!ImplicitBool(pyobject, ctxt)) \
659 return false; \
660 CPPJIT_IMPL_BASIC_CONVERTER_BODY(name, type, stype, ctype, F1, F2, tc) \
661 } \
662 CPPJIT_IMPL_BASIC_CONVERTER_METHODS(name, type, stype, ctype, F1, F2)
663
664#define CPPJIT_IMPL_BASIC_CONVERTER_NB(name, type, stype, ctype, F1, F2, tc) \
665 bool cpyrt::name##Converter::SetArg(PyObject* pyobject, Parameter& para, \
666 CallContext* /*ctxt*/) { \
667 if (PyBool_Check(pyobject)) \
668 return false; \
669 CPPJIT_IMPL_BASIC_CONVERTER_BODY(name, type, stype, ctype, F1, F2, tc) \
670 } \
671 CPPJIT_IMPL_BASIC_CONVERTER_METHODS(name, type, stype, ctype, F1, F2)
672
673//----------------------------------------------------------------------------
674static inline int ExtractChar(PyObject* pyobject, const char* tname, int low,
675 int high) {
676 int lchar = -1;
677 if (PyBytes_Check(pyobject)) {
678 if (PyBytes_GET_SIZE(pyobject) == 1)
679 lchar = (int)(PyBytes_AsString(pyobject)[0]);
680 else
681 PyErr_Format(PyExc_ValueError,
682 "%s expected, got bytes of size " PY_SSIZE_T_FORMAT, tname,
683 PyBytes_GET_SIZE(pyobject));
684 } else if (PyUnicode_Check(pyobject)) {
685 if (PyUnicode_GET_LENGTH(pyobject) == 1)
686 lchar = (int)(PyUnicode_AsUTF8(pyobject)[0]);
687 else
688 PyErr_Format(PyExc_ValueError,
689 "%s expected, got str of size " PY_SSIZE_T_FORMAT, tname,
690 PyUnicode_GET_LENGTH(pyobject));
691 } else if (pyobject == cpyrt::gDefaultObject) {
692 lchar = (int)'\0';
693 } else if (!PyFloat_Check(pyobject)) { // don't allow truncating conversion
694 lchar = (int)PyLong_AsLong(pyobject);
695 if (lchar == -1 && PyErr_Occurred())
696 ; // empty, as error already set
697 else if (!(low <= lchar && lchar <= high)) {
698 PyErr_Format(PyExc_ValueError,
699 "integer to character: value %d not in range [%d,%d]", lchar,
700 low, high);
701 lchar = -1;
702 }
703 } else
704 PyErr_SetString(PyExc_TypeError, "char or small int type expected");
705
706 return lchar;
707}
708
709//----------------------------------------------------------------------------
710#define CPPJIT_IMPL_REFCONVERTER_FROM_MEMORY(name, ctype) \
711 PyObject* cpyrt::name##RefConverter::FromMemory(void* ptr) { \
712 /* convert a reference to int to Python through ctypes pointer object */ \
713 PyTypeObject* ctypes_type = GetCTypesType(ct_##ctype); \
714 if (!ctypes_type) { \
715 PyErr_SetString(PyExc_RuntimeError, "no ctypes available"); \
716 return nullptr; \
717 } \
718 PyObject* ref = ctypes_type->tp_new(ctypes_type, nullptr, nullptr); \
719 ((cpyrt_tagCDataObject*)ref)->b_ptr = (char*)ptr; \
720 ((cpyrt_tagCDataObject*)ref)->b_needsfree = 0; \
721 return ref; \
722 }
723
724//----------------------------------------------------------------------------
725#define CPPJIT_IMPL_BASIC_CONST_REFCONVERTER(name, type, ctype, F1) \
726 bool cpyrt::Const##name##RefConverter::SetArg( \
727 PyObject* pyobject, Parameter& para, CallContext* /* ctxt */) { \
728 type val = (type)F1(pyobject); \
729 if (val == (type) - 1 && PyErr_Occurred()) { \
730 if (pyobject == cpyrt::gDefaultObject) { \
731 PyErr_Clear(); \
732 val = (type)0; \
733 } else \
734 return false; \
735 } \
736 para.fValue.f##name = val; \
737 para.fRef = &para.fValue.f##name; \
738 para.fTypeCode = 'r'; \
739 return true; \
740 } \
741 CPPJIT_IMPL_REFCONVERTER_FROM_MEMORY(Const##name, ctype)
742
743//----------------------------------------------------------------------------
744#define CPPJIT_IMPL_BASIC_CONST_CHAR_REFCONVERTER(name, type, ctype, low, \
745 high) \
746 bool cpyrt::Const##name##RefConverter::SetArg( \
747 PyObject* pyobject, Parameter& para, CallContext* /* ctxt */) { \
748 /* convert <pyobject> to C++ <<type>>, set arg for call, allow int -> char \
749 */ \
750 type val = (type)ExtractChar(pyobject, #type, low, high); \
751 if (val == (type) - 1 && PyErr_Occurred()) \
752 return false; \
753 para.fValue.fLong = val; \
754 para.fTypeCode = 'l'; \
755 return true; \
756 } \
757 CPPJIT_IMPL_REFCONVERTER_FROM_MEMORY(Const##name, ctype)
758
759//----------------------------------------------------------------------------
760#define CPPJIT_IMPL_BASIC_CHAR_CONVERTER(name, type, low, high) \
761 bool cpyrt::name##Converter::SetArg(PyObject* pyobject, Parameter& para, \
762 CallContext* /* ctxt */) { \
763 /* convert <pyobject> to C++ <<type>>, set arg for call, allow int -> char \
764 */ \
765 long val = ExtractChar(pyobject, #type, low, high); \
766 if (val == -1 && PyErr_Occurred()) \
767 return false; \
768 para.fValue.fLong = val; \
769 para.fTypeCode = 'l'; \
770 return true; \
771 } \
772 \
773 PyObject* cpyrt::name##Converter::FromMemory(void* address) { \
774 /* return char in "native" str type as that's more natural in use */ \
775 return PyUnicode_FromFormat("%c", *((type*)address)); \
776 } \
777 \
778 bool cpyrt::name##Converter::ToMemory(PyObject* value, void* address, \
779 PyObject* /* ctxt */) { \
780 Py_ssize_t len; \
781 const char* cstr = nullptr; \
782 if (PyBytes_Check(value)) \
783 PyBytes_AsStringAndSize(value, (char**)&cstr, &len); \
784 else \
785 cstr = cpyrt_PyText_AsStringAndSize(value, &len); \
786 if (cstr) { \
787 if (len != 1) { \
788 PyErr_Format(PyExc_TypeError, \
789 #type " expected, got string of size %zd", len); \
790 return false; \
791 } \
792 *((type*)address) = (type)cstr[0]; \
793 } else { \
794 PyErr_Clear(); \
795 long l = PyLong_AsLong(value); \
796 if (l == -1 && PyErr_Occurred()) { \
797 if (value == cpyrt::gDefaultObject) { \
798 PyErr_Clear(); \
799 l = (long)0; \
800 } else \
801 return false; \
802 } \
803 if (!(low <= l && l <= high)) { \
804 PyErr_Format(PyExc_ValueError, \
805 "integer to character: value %ld not in range [%d,%d]", \
806 l, low, high); \
807 return false; \
808 } \
809 *((type*)address) = (type)l; \
810 } \
811 return true; \
812 }
813
814//- converters for built-ins -------------------------------------------------
815CPPJIT_IMPL_BASIC_CONVERTER_IB(Long, long, long, c_long, PyLong_FromLong,
817
818//----------------------------------------------------------------------------
819bool cpyrt::LongRefConverter::SetArg(PyObject* pyobject, Parameter& para,
820 CallContext* /* ctxt */) {
821 // convert <pyobject> to C++ long&, set arg for call
822
823 if (Py_TYPE(pyobject) == GetCTypesType(ct_c_long)) {
824 para.fValue.fVoidp = (void*)((cpyrt_tagCDataObject*)pyobject)->b_ptr;
825 para.fTypeCode = 'V';
826 return true;
827 }
828
829 if (CArraySetArg(pyobject, para, 'l', sizeof(long))) {
830 para.fTypeCode = 'V';
831 return true;
832 }
833
834 PyErr_SetString(PyExc_TypeError,
835 "use ctypes.c_long for pass-by-ref of longs");
836 return false;
837}
838
839//----------------------------------------------------------------------------
840// clang-format off
841CPPJIT_IMPL_BASIC_CONST_CHAR_REFCONVERTER(Char, char, c_char, CHAR_MIN, CHAR_MAX)
842CPPJIT_IMPL_BASIC_CONST_CHAR_REFCONVERTER(UChar, unsigned char, c_uchar, 0, UCHAR_MAX)
843
852CPPJIT_IMPL_BASIC_CONST_REFCONVERTER(UShort, unsigned short, c_ushort, cpyrt_PyLong_AsUShort)
856CPPJIT_IMPL_BASIC_CONST_REFCONVERTER(ULong, unsigned long, c_ulong, PyLongOrInt_AsULong)
859// clang-format on
860
861//----------------------------------------------------------------------------
862bool cpyrt::IntRefConverter::SetArg(PyObject* pyobject, Parameter& para,
863 CallContext* /* ctxt */) {
864 // convert <pyobject> to C++ (pseudo)int&, set arg for call
865
866 if (Py_TYPE(pyobject) == GetCTypesType(ct_c_int)) {
867 para.fValue.fVoidp = (void*)((cpyrt_tagCDataObject*)pyobject)->b_ptr;
868 para.fTypeCode = 'V';
869 return true;
870 }
871
872 // alternate, pass pointer from buffer
873 Py_ssize_t buflen =
874 Utility::GetBuffer(pyobject, 'i', sizeof(int), para.fValue.fVoidp);
875 if (para.fValue.fVoidp && buflen) {
876 para.fTypeCode = 'V';
877 return true;
878 };
879
880 PyErr_SetString(PyExc_TypeError, "use ctypes.c_int for pass-by-ref of ints");
881 return false;
882}
884//----------------------------------------------------------------------------
885#define CPPJIT_IMPL_REFCONVERTER(name, ctype, type, code) \
886 bool cpyrt::name##RefConverter::SetArg(PyObject* pyobject, Parameter& para, \
887 CallContext* /* ctxt */) { \
888 /* convert a reference to int to Python through ctypes pointer object */ \
889 if (Py_TYPE(pyobject) == GetCTypesType(ct_##ctype)) { \
890 para.fValue.fVoidp = (void*)((cpyrt_tagCDataObject*)pyobject)->b_ptr; \
891 para.fTypeCode = 'V'; \
892 return true; \
893 } \
894 bool res = CArraySetArg(pyobject, para, code, sizeof(type)); \
895 if (!res) { \
896 PyErr_SetString(PyExc_TypeError, \
897 "use ctypes." #ctype " for pass-by-ref of " #type); \
898 return false; \
899 } \
900 para.fTypeCode = 'V'; \
901 return res; \
902 } \
903 CPPJIT_IMPL_REFCONVERTER_FROM_MEMORY(name, ctype)
904
905// clang-format off
906CPPJIT_IMPL_REFCONVERTER(Bool, c_bool, bool, '?');
907CPPJIT_IMPL_REFCONVERTER(Char, c_char, char, 'b');
908CPPJIT_IMPL_REFCONVERTER(WChar, c_wchar, wchar_t, 'u');
909CPPJIT_IMPL_REFCONVERTER(Char16, c_uint16, char16_t, 'H');
910CPPJIT_IMPL_REFCONVERTER(Char32, c_uint32, char32_t, 'I');
911CPPJIT_IMPL_REFCONVERTER(SChar, c_byte, signed char, 'b');
912CPPJIT_IMPL_REFCONVERTER(UChar, c_ubyte, unsigned char, 'B');
913CPPJIT_IMPL_REFCONVERTER(Int8, c_int8, int8_t, 'b');
914CPPJIT_IMPL_REFCONVERTER(UInt8, c_uint8, uint8_t, 'B');
915CPPJIT_IMPL_REFCONVERTER(Int16, c_int16, int16_t, 'h');
916CPPJIT_IMPL_REFCONVERTER(UInt16, c_uint16, uint16_t, 'H');
917CPPJIT_IMPL_REFCONVERTER(Int32, c_int32, int32_t, 'i');
918CPPJIT_IMPL_REFCONVERTER(UInt32, c_uint32, uint32_t, 'I');
919CPPJIT_IMPL_REFCONVERTER(Short, c_short, short, 'h');
920CPPJIT_IMPL_REFCONVERTER(UShort, c_ushort, unsigned short, 'H');
922CPPJIT_IMPL_REFCONVERTER(UInt, c_uint, unsigned int, 'I');
924CPPJIT_IMPL_REFCONVERTER(ULong, c_ulong, unsigned long, 'L');
925CPPJIT_IMPL_REFCONVERTER(LLong, c_longlong, long long, 'q');
926CPPJIT_IMPL_REFCONVERTER(ULLong, c_ulonglong, unsigned long long, 'Q');
927CPPJIT_IMPL_REFCONVERTER(Float, c_float, float, 'f');
929CPPJIT_IMPL_REFCONVERTER(LDouble, c_longdouble, PY_LONG_DOUBLE, 'g');
930// clang-format on
931
932//----------------------------------------------------------------------------
933// convert <pyobject> to C++ bool, allow int/long -> bool, set arg for call
934CPPJIT_IMPL_BASIC_CONVERTER_NI(Bool, bool, long, c_bool, PyBool_FromLong,
936
937//----------------------------------------------------------------------------
938CPPJIT_IMPL_BASIC_CHAR_CONVERTER(Char, char, CHAR_MIN, CHAR_MAX)
939CPPJIT_IMPL_BASIC_CHAR_CONVERTER(UChar, unsigned char, 0, UCHAR_MAX)
940
941PyObject* cpyrt::SCharAsIntConverter::FromMemory(void* address) {
942 // special case to be used with arrays: return a Python int instead of str
943 // (following the same convention as module array.array)
944 return PyLong_FromLong((long)*((signed char*)address));
945}
946
947PyObject* cpyrt::UCharAsIntConverter::FromMemory(void* address) {
948 // special case to be used with arrays: return a Python int instead of str
949 // (following the same convention as module array.array)
950 return PyLong_FromLong((long)*((unsigned char*)address));
951}
952
953//----------------------------------------------------------------------------
954bool cpyrt::WCharConverter::SetArg(PyObject* pyobject, Parameter& para,
955 CallContext* /* ctxt */) {
956 // convert <pyobject> to C++ <wchar_t>, set arg for call
957 if (!PyUnicode_Check(pyobject) || PyUnicode_GET_LENGTH(pyobject) != 1) {
958 PyErr_SetString(PyExc_ValueError, "single wchar_t character expected");
959 return false;
960 }
961 wchar_t val;
962 Py_ssize_t res = PyUnicode_AsWideChar(pyobject, &val, 1);
963 if (res == -1)
964 return false;
965 para.fValue.fLong = (long)val;
966 para.fTypeCode = 'U';
967 return true;
968}
969
970PyObject* cpyrt::WCharConverter::FromMemory(void* address) {
971 return PyUnicode_FromWideChar((const wchar_t*)address, 1);
972}
973
974bool cpyrt::WCharConverter::ToMemory(PyObject* value, void* address,
975 PyObject* /* ctxt */) {
976 if (!PyUnicode_Check(value) || PyUnicode_GET_LENGTH(value) != 1) {
977 PyErr_SetString(PyExc_ValueError, "single wchar_t character expected");
978 return false;
979 }
980 wchar_t val;
981 Py_ssize_t res = PyUnicode_AsWideChar(value, &val, 1);
982 if (res == -1)
983 return false;
984 *((wchar_t*)address) = val;
985 return true;
986}
987
988//----------------------------------------------------------------------------
989bool cpyrt::Char16Converter::SetArg(PyObject* pyobject, Parameter& para,
990 CallContext* /* ctxt */) {
991 // convert <pyobject> to C++ <char16_t>, set arg for call
992 if (!PyUnicode_Check(pyobject) || PyUnicode_GET_LENGTH(pyobject) != 1) {
993 PyErr_SetString(PyExc_ValueError, "single char16_t character expected");
994 return false;
995 }
996
997 PyObject* bstr = PyUnicode_AsUTF16String(pyobject);
998 if (!bstr)
999 return false;
1000
1001 char16_t val =
1002 *(char16_t*)(PyBytes_AS_STRING(bstr) + sizeof(char16_t) /*BOM*/);
1003 Py_DECREF(bstr);
1004 para.fValue.fLong = (long)val;
1005 para.fTypeCode = 'U';
1006 return true;
1007}
1008
1009PyObject* cpyrt::Char16Converter::FromMemory(void* address) {
1010 return PyUnicode_DecodeUTF16((const char*)address, sizeof(char16_t), nullptr,
1011 nullptr);
1012}
1013
1014bool cpyrt::Char16Converter::ToMemory(PyObject* value, void* address,
1015 PyObject* /* ctxt */) {
1016 if (!PyUnicode_Check(value) || PyUnicode_GET_LENGTH(value) != 1) {
1017 PyErr_SetString(PyExc_ValueError, "single char16_t character expected");
1018 return false;
1019 }
1020
1021 PyObject* bstr = PyUnicode_AsUTF16String(value);
1022 if (!bstr)
1023 return false;
1024
1025 *((char16_t*)address) =
1026 *(char16_t*)(PyBytes_AS_STRING(bstr) + sizeof(char16_t) /*BOM*/);
1027 Py_DECREF(bstr);
1028 return true;
1029}
1030
1031//----------------------------------------------------------------------------
1032bool cpyrt::Char32Converter::SetArg(PyObject* pyobject, Parameter& para,
1033 CallContext* /* ctxt */) {
1034 // convert <pyobject> to C++ <char32_t>, set arg for call
1035 if (!PyUnicode_Check(pyobject) || 2 < PyUnicode_GET_LENGTH(pyobject)) {
1036 PyErr_SetString(PyExc_ValueError, "single char32_t character expected");
1037 return false;
1038 }
1039
1040 PyObject* bstr = PyUnicode_AsUTF32String(pyobject);
1041 if (!bstr)
1042 return false;
1043
1044 char32_t val =
1045 *(char32_t*)(PyBytes_AS_STRING(bstr) + sizeof(char32_t) /*BOM*/);
1046 Py_DECREF(bstr);
1047 para.fValue.fLong = (long)val;
1048 para.fTypeCode = 'U';
1049 return true;
1050}
1051
1052PyObject* cpyrt::Char32Converter::FromMemory(void* address) {
1053 return PyUnicode_DecodeUTF32((const char*)address, sizeof(char32_t), nullptr,
1054 nullptr);
1055}
1056
1057bool cpyrt::Char32Converter::ToMemory(PyObject* value, void* address,
1058 PyObject* /* ctxt */) {
1059 if (!PyUnicode_Check(value) || 2 < PyUnicode_GET_LENGTH(value)) {
1060 PyErr_SetString(PyExc_ValueError, "single char32_t character expected");
1061 return false;
1062 }
1063
1064 PyObject* bstr = PyUnicode_AsUTF32String(value);
1065 if (!bstr)
1066 return false;
1067
1068 *((char32_t*)address) =
1069 *(char32_t*)(PyBytes_AS_STRING(bstr) + sizeof(char32_t) /*BOM*/);
1070 Py_DECREF(bstr);
1071 return true;
1072}
1073
1074//----------------------------------------------------------------------------
1075CPPJIT_IMPL_BASIC_CONVERTER_IB(Int8, int8_t, long, c_int8, PyLong_FromLong,
1077CPPJIT_IMPL_BASIC_CONVERTER_IB(UInt8, uint8_t, long, c_uint8, PyLong_FromLong,
1079CPPJIT_IMPL_BASIC_CONVERTER_IB(Int16, int16_t, long, c_int16, PyLong_FromLong,
1081CPPJIT_IMPL_BASIC_CONVERTER_IB(UInt16, uint16_t, long, c_uint16,
1082 PyLong_FromLong, cpyrt_PyLong_AsUInt16, 'l')
1083CPPJIT_IMPL_BASIC_CONVERTER_IB(Int32, int32_t, long, c_int32, PyLong_FromLong,
1085CPPJIT_IMPL_BASIC_CONVERTER_IB(UInt32, uint32_t, unsigned long, c_uint32,
1086 PyLong_FromUnsignedLong, cpyrt_PyLong_AsUInt32,
1087 'l')
1088CPPJIT_IMPL_BASIC_CONVERTER_IB(Short, short, long, c_short, PyLong_FromLong,
1090CPPJIT_IMPL_BASIC_CONVERTER_IB(UShort, unsigned short, long, c_ushort,
1091 PyLong_FromLong, cpyrt_PyLong_AsUShort, 'l')
1092CPPJIT_IMPL_BASIC_CONVERTER_IB(Int, int, long, c_uint, PyLong_FromLong,
1094
1095//----------------------------------------------------------------------------
1096bool cpyrt::ULongConverter::SetArg(PyObject* pyobject, Parameter& para,
1097 CallContext* ctxt) {
1098 // convert <pyobject> to C++ unsigned long, set arg for call
1099 if (!ImplicitBool(pyobject, ctxt))
1100 return false;
1101
1102 para.fValue.fULong = PyLongOrInt_AsULong(pyobject);
1103 if (para.fValue.fULong == (unsigned long)-1 && PyErr_Occurred())
1104 return false;
1105 para.fTypeCode = 'L';
1106 return true;
1107}
1108
1109PyObject* cpyrt::ULongConverter::FromMemory(void* address) {
1110 // construct python object from C++ unsigned long read at <address>
1111 return PyLong_FromUnsignedLong(*((unsigned long*)address));
1112}
1113
1114bool cpyrt::ULongConverter::ToMemory(PyObject* value, void* address,
1115 PyObject* /* ctxt */) {
1116 // convert <value> to C++ unsigned long, write it at <address>
1117 unsigned long u = PyLongOrInt_AsULong(value);
1118 if (u == (unsigned long)-1 && PyErr_Occurred()) {
1120 PyErr_Clear();
1121 u = (unsigned long)0;
1122 } else
1123 return false;
1124 }
1125 *((unsigned long*)address) = u;
1126 return true;
1127}
1128
1129//----------------------------------------------------------------------------
1130PyObject* cpyrt::UIntConverter::FromMemory(void* address) {
1131 // construct python object from C++ unsigned int read at <address>
1132 return PyLong_FromUnsignedLong(*((unsigned int*)address));
1133}
1134
1135bool cpyrt::UIntConverter::ToMemory(PyObject* value, void* address,
1136 PyObject* /* ctxt */) {
1137 // convert <value> to C++ unsigned int, write it at <address>
1138 unsigned long u = PyLongOrInt_AsULong(value);
1139 if (u == (unsigned long)-1 && PyErr_Occurred())
1140 return false;
1141
1142 if (u > (unsigned long)UINT_MAX) {
1143 PyErr_SetString(PyExc_OverflowError, "value too large for unsigned int");
1144 return false;
1145 }
1146
1147 *((unsigned int*)address) = (unsigned int)u;
1148 return true;
1149}
1150
1151//- floating point converters ------------------------------------------------
1152CPPJIT_IMPL_BASIC_CONVERTER_NB(Float, float, double, c_float,
1153 PyFloat_FromDouble, PyFloat_AsDouble, 'f')
1154CPPJIT_IMPL_BASIC_CONVERTER_NB(Double, double, double, c_double,
1155 PyFloat_FromDouble, PyFloat_AsDouble, 'd')
1156
1158 c_longdouble, PyFloat_FromDouble,
1159 PyFloat_AsDouble, 'g')
1160
1161cpyrt::ComplexDConverter::ComplexDConverter(bool keepControl)
1162 : InstanceConverter(interop::GetFullScope("std::complex<double>"),
1163 keepControl) {}
1164
1165// special case for std::complex<double>, maps it to/from Python's complex
1166bool cpyrt::ComplexDConverter::SetArg(PyObject* pyobject, Parameter& para,
1167 CallContext* ctxt) {
1168 const Py_complex& pc = PyComplex_AsCComplex(pyobject);
1169 if (pc.real != -1.0 || !PyErr_Occurred()) {
1170 fBuffer.real(pc.real);
1171 fBuffer.imag(pc.imag);
1172 para.fValue.fVoidp = &fBuffer;
1173 para.fTypeCode = 'V';
1174 return true;
1175 }
1176
1177 return this->InstanceConverter::SetArg(pyobject, para, ctxt);
1178}
1179
1180PyObject* cpyrt::ComplexDConverter::FromMemory(void* address) {
1181 std::complex<double>* dc = (std::complex<double>*)address;
1182 return PyComplex_FromDoubles(dc->real(), dc->imag());
1183}
1184
1185bool cpyrt::ComplexDConverter::ToMemory(PyObject* value, void* address,
1186 PyObject* ctxt) {
1187 const Py_complex& pc = PyComplex_AsCComplex(value);
1188 if (pc.real != -1.0 || !PyErr_Occurred()) {
1189 std::complex<double>* dc = (std::complex<double>*)address;
1190 dc->real(pc.real);
1191 dc->imag(pc.imag);
1192 return true;
1193 }
1194 return this->InstanceConverter::ToMemory(value, address, ctxt);
1195}
1196
1197//----------------------------------------------------------------------------
1198bool cpyrt::DoubleRefConverter::SetArg(PyObject* pyobject, Parameter& para,
1199 CallContext* /* ctxt */) {
1200 // convert <pyobject> to C++ double&, set arg for call
1201
1202 if (Py_TYPE(pyobject) == GetCTypesType(ct_c_double)) {
1203 para.fValue.fVoidp = (void*)((cpyrt_tagCDataObject*)pyobject)->b_ptr;
1204 para.fTypeCode = 'V';
1205 return true;
1206 }
1207
1208 // alternate, pass pointer from buffer
1209 Py_ssize_t buflen =
1210 Utility::GetBuffer(pyobject, 'd', sizeof(double), para.fValue.fVoidp);
1211 if (buflen && para.fValue.fVoidp) {
1212 para.fTypeCode = 'V';
1213 return true;
1214 }
1215
1216 PyErr_SetString(PyExc_TypeError,
1217 "use ctypes.c_double for pass-by-ref of doubles");
1218 return false;
1219}
1220
1221//----------------------------------------------------------------------------
1222// clang-format off
1223CPPJIT_IMPL_BASIC_CONST_REFCONVERTER(Float, float, c_float, PyFloat_AsDouble)
1224CPPJIT_IMPL_BASIC_CONST_REFCONVERTER(Double, double, c_double, PyFloat_AsDouble)
1225CPPJIT_IMPL_BASIC_CONST_REFCONVERTER(LDouble, PY_LONG_DOUBLE, c_longdouble, PyFloat_AsDouble)
1226// clang-format on
1227
1228//----------------------------------------------------------------------------
1229bool cpyrt::VoidConverter::SetArg(PyObject*, Parameter&, CallContext*) {
1230 // can't happen (unless a type is mapped wrongly), but implemented for
1231 // completeness
1232 PyErr_SetString(PyExc_SystemError, "void/unknown arguments can\'t be set");
1233 return false;
1234}
1235
1236//----------------------------------------------------------------------------
1237bool cpyrt::LLongConverter::SetArg(PyObject* pyobject, Parameter& para,
1238 CallContext* ctxt) {
1239 // convert <pyobject> to C++ long long, set arg for call
1240 if (!ImplicitBool(pyobject, ctxt))
1241 return false;
1242
1244 if (PyErr_Occurred())
1245 return false;
1246 para.fTypeCode = 'q';
1247 return true;
1248}
1249
1250PyObject* cpyrt::LLongConverter::FromMemory(void* address) {
1251 // construct python object from C++ long long read at <address>
1252 return PyLong_FromLongLong(*(PY_LONG_LONG*)address);
1253}
1254
1255bool cpyrt::LLongConverter::ToMemory(PyObject* value, void* address,
1256 PyObject* /* ctxt */) {
1257 // convert <value> to C++ long long, write it at <address>
1258 PY_LONG_LONG ll = PyLong_AsLongLong(value);
1259 if (ll == -1 && PyErr_Occurred()) {
1261 PyErr_Clear();
1262 ll = (PY_LONG_LONG)0;
1263 } else
1264 return false;
1265 }
1266 *((PY_LONG_LONG*)address) = ll;
1267 return true;
1268}
1269
1270//----------------------------------------------------------------------------
1271bool cpyrt::ULLongConverter::SetArg(PyObject* pyobject, Parameter& para,
1272 CallContext* ctxt) {
1273 // convert <pyobject> to C++ unsigned long long, set arg for call
1274 if (!ImplicitBool(pyobject, ctxt))
1275 return false;
1276
1277 para.fValue.fULLong = PyLongOrInt_AsULong64(pyobject);
1278 if (PyErr_Occurred())
1279 return false;
1280 para.fTypeCode = 'Q';
1281 return true;
1282}
1283
1284PyObject* cpyrt::ULLongConverter::FromMemory(void* address) {
1285 // construct python object from C++ unsigned long long read at <address>
1286 return PyLong_FromUnsignedLongLong(*(PY_ULONG_LONG*)address);
1287}
1288
1289bool cpyrt::ULLongConverter::ToMemory(PyObject* value, void* address,
1290 PyObject* /* ctxt */) {
1291 // convert <value> to C++ unsigned long long, write it at <address>
1293 if (PyErr_Occurred()) {
1295 PyErr_Clear();
1296 ull = (PY_ULONG_LONG)0;
1297 } else
1298 return false;
1299 }
1300 *((PY_ULONG_LONG*)address) = ull;
1301 return true;
1302}
1303
1304//----------------------------------------------------------------------------
1305cpyrt::EnumConverter::~EnumConverter() { DestroyConverter(fBase); }
1306
1307bool cpyrt::EnumConverter::SetArg(PyObject* pyobject, Parameter& para,
1308 CallContext* ctxt) {
1309 // an instance of the same enum is an exact match; anything else, plain
1310 // integers included, converts in the implicit round only, as in C++
1311 if (ctxt && !AllowImplicit(ctxt)) {
1312 bool exact = false;
1313 if (IsCPPEnumInstance(pyobject)) {
1314 PyObject* pycppname =
1315 PyObject_GetAttr((PyObject*)Py_TYPE(pyobject), PyStrings::gCppName);
1316 if (pycppname) {
1317 exact = (fEnumName == PyUnicode_AsUTF8(pycppname));
1318 Py_DECREF(pycppname);
1319 } else
1320 PyErr_Clear();
1321 }
1322 if (!exact) {
1323 if (!NoImplicit(ctxt))
1325 return false;
1326 }
1327 // the base integer converter defers enum instances, so lift the gate
1329 return fBase->SetArg(pyobject, para, ctxt);
1330 }
1331 return fBase->SetArg(pyobject, para, ctxt);
1332}
1333
1334PyObject* cpyrt::EnumConverter::FromMemory(void* address) {
1335 return fBase->FromMemory(address);
1336}
1337
1338bool cpyrt::EnumConverter::ToMemory(PyObject* value, void* address,
1339 PyObject* ctxt) {
1340 return fBase->ToMemory(value, address, ctxt);
1341}
1342
1343std::string cpyrt::EnumConverter::GetFailureMsg() {
1344 return fBase->GetFailureMsg();
1345}
1346
1347//----------------------------------------------------------------------------
1348bool cpyrt::CStringConverter::SetArg(PyObject* pyobject, Parameter& para,
1349 CallContext* ctxt) {
1350 // construct a new string and copy it in new memory
1351 Py_ssize_t len;
1352 const char* cstr = cpyrt_PyText_AsStringAndSize(pyobject, &len);
1353 if (!cstr) {
1354 // special case: allow ctypes c_char_p
1355 auto error = cpyrt::Utility::FetchPyError();
1356 if (Py_TYPE(pyobject) == GetCTypesType(ct_c_char_p)) {
1357 SetLifeLine(ctxt->fPyContext, pyobject, (intptr_t)this);
1358 para.fValue.fVoidp = (void*)((cpyrt_tagCDataObject*)pyobject)->b_ptr;
1359 para.fTypeCode = 'V';
1360 return true;
1361 }
1363 return false;
1364 }
1365
1366 // verify (too long string will cause truncation, no crash)
1367 if (fMaxSize != std::string::npos && fMaxSize < fBuffer.size())
1368 if (PyErr_WarnEx(PyExc_RuntimeWarning,
1369 (char*)"string too long for char array (truncated)",
1370 1) < 0)
1371 return false;
1372
1373 if (!ctxt->fPyContext) {
1374 // use internal buffer as workaround
1375 fBuffer = std::string(cstr, len);
1376 if (fMaxSize != std::string::npos)
1377 fBuffer.resize(fMaxSize, '\0'); // pad remainder of buffer as needed
1378 cstr = fBuffer.c_str();
1379 } else
1380 SetLifeLine(ctxt->fPyContext, pyobject, (intptr_t)this);
1381
1382 // set the value and declare success
1383 para.fValue.fVoidp = (void*)cstr;
1384 para.fTypeCode = 'p';
1385 return true;
1386}
1387
1388PyObject* cpyrt::CStringConverter::FromMemory(void* address) {
1389 // construct python object from C++ const char* read at <address>
1390 if (address && *(void**)address) {
1391 if (fMaxSize !=
1392 std::string::npos) // need to prevent reading beyond boundary
1393 return PyUnicode_FromStringAndSize(*(char**)address,
1394 (Py_ssize_t)fMaxSize);
1395
1396 if (*(void**)address ==
1397 (void*)fBuffer.data()) // if we're buffering, we know the size
1398 return PyUnicode_FromStringAndSize((char*)fBuffer.data(), fBuffer.size());
1399
1400 // no idea about lentgth: cut on \0
1401 return PyUnicode_FromString(*(char**)address);
1402 }
1403
1404 // empty string in case there's no address
1405 Py_INCREF(PyStrings::gEmptyString);
1407}
1408
1409bool cpyrt::CStringConverter::ToMemory(PyObject* value, void* address,
1410 PyObject* ctxt) {
1411 // convert <value> to C++ const char*, write it at <address>
1412 Py_ssize_t len;
1413 const char* cstr = cpyrt_PyText_AsStringAndSize(value, &len);
1414 if (!cstr)
1415 return false;
1416
1417 // verify (too long string will cause truncation, no crash)
1418 if (fMaxSize != std::string::npos && fMaxSize < (std::string::size_type)len)
1419 if (PyErr_WarnEx(PyExc_RuntimeWarning,
1420 (char*)"string too long for char array (truncated)",
1421 1) < 0)
1422 return false;
1423
1424 // if address is available, and it wasn't set by this converter, assume a
1425 // byte-wise copy; otherwise assume a pointer copy (this relies on the
1426 // converter to be used for properties, or for argument passing, but not both
1427 // at the same time; this is currently the case)
1428 void* ptrval = *(void**)address;
1429 if (ptrval == (void*)fBuffer.data()) {
1430 fBuffer = std::string(cstr, len);
1431 *(void**)address = (void*)fBuffer.data();
1432 return true;
1433 } else if (ptrval && HasLifeLine(ctxt, (intptr_t)ptrval)) {
1434 ptrval = nullptr;
1435 // fall through; ptrval is nullptr means we're managing it
1436 }
1437
1438 // the string is (going to be) managed by us: assume pointer copy
1439 if (!ptrval) {
1440 SetLifeLine(ctxt, value, (intptr_t)address);
1441 *(void**)address = (void*)cstr;
1442 return true;
1443 }
1444
1445 // the pointer value is non-zero and not ours: assume byte copy
1446 if (fMaxSize != std::string::npos)
1447 strncpy(*(char**)address, cstr, fMaxSize); // pads remainder
1448 else
1449 // coverity[secure_coding] - can't help it, it's intentional.
1450 strcpy(*(char**)address, cstr);
1451
1452 return true;
1453}
1454
1455//----------------------------------------------------------------------------
1456bool cpyrt::WCStringConverter::SetArg(PyObject* pyobject, Parameter& para,
1457 CallContext* /* ctxt */) {
1458 // construct a new string and copy it in new memory
1459 Py_ssize_t len = PyUnicode_GetLength(pyobject);
1460 if (len == (Py_ssize_t)-1 && PyErr_Occurred())
1461 return false;
1462
1463 fBuffer = (wchar_t*)realloc(fBuffer, sizeof(wchar_t) * (len + 1));
1464 Py_ssize_t res = PyUnicode_AsWideChar(pyobject, fBuffer, len);
1465 if (res == -1)
1466 return false; // could free the buffer here
1467
1468 // set the value and declare success
1469 fBuffer[len] = L'\0';
1470 para.fValue.fVoidp = (void*)fBuffer;
1471 para.fTypeCode = 'p';
1472 return true;
1473}
1474
1475PyObject* cpyrt::WCStringConverter::FromMemory(void* address) {
1476 // construct python object from C++ wchar_t* read at <address>
1477 if (address && *(wchar_t**)address) {
1478 if (fMaxSize !=
1479 std::wstring::npos) // need to prevent reading beyond boundary
1480 return PyUnicode_FromWideChar(*(wchar_t**)address, (Py_ssize_t)fMaxSize);
1481 // with unknown size
1482 return PyUnicode_FromWideChar(*(wchar_t**)address,
1483 wcslen(*(wchar_t**)address));
1484 }
1485
1486 // empty string in case there's no valid address
1487 wchar_t w = L'\0';
1488 return PyUnicode_FromWideChar(&w, 0);
1489}
1490
1491bool cpyrt::WCStringConverter::ToMemory(PyObject* value, void* address,
1492 PyObject* /* ctxt */) {
1493 // convert <value> to C++ wchar_t*, write it at <address>
1494 Py_ssize_t len = PyUnicode_GetLength(value);
1495 if (len == (Py_ssize_t)-1 && PyErr_Occurred())
1496 return false;
1497
1498 // verify (too long string will cause truncation, no crash)
1499 if (fMaxSize != std::wstring::npos && fMaxSize < (std::wstring::size_type)len)
1500 if (PyErr_WarnEx(PyExc_RuntimeWarning,
1501 (char*)"string too long for wchar_t array (truncated)",
1502 1) < 0)
1503 return false;
1504
1505 Py_ssize_t res = -1;
1506 if (fMaxSize != std::wstring::npos)
1507 res =
1508 PyUnicode_AsWideChar(value, *(wchar_t**)address, (Py_ssize_t)fMaxSize);
1509 else
1510 // coverity[secure_coding] - can't help it, it's intentional.
1511 res = PyUnicode_AsWideChar(value, *(wchar_t**)address, len);
1512
1513 if (res == -1)
1514 return false;
1515 return true;
1516}
1518//----------------------------------------------------------------------------
1519#define CPYRT_WIDESTRING_CONVERTER(name, type, encode, decode, snull) \
1520 bool cpyrt::name##Converter::SetArg(PyObject* pyobject, Parameter& para, \
1521 CallContext* /* ctxt */) { \
1522 /* change string encoding and copy into local buffer */ \
1523 PyObject* bstr = encode(pyobject); \
1524 if (!bstr) \
1525 return false; \
1526 \
1527 Py_ssize_t len = PyBytes_GET_SIZE(bstr) - sizeof(type) /*BOM*/; \
1528 fBuffer = (type*)realloc(fBuffer, len + sizeof(type)); \
1529 memcpy(fBuffer, PyBytes_AS_STRING(bstr) + sizeof(type) /*BOM*/, len); \
1530 Py_DECREF(bstr); \
1531 \
1532 fBuffer[len / sizeof(type)] = snull; \
1533 para.fValue.fVoidp = (void*)fBuffer; \
1534 para.fTypeCode = 'p'; \
1535 return true; \
1536 } \
1537 \
1538 PyObject* cpyrt::name##Converter::FromMemory(void* address) { \
1539 /* construct python object from C++ <type>* read at <address> */ \
1540 if (address && *(type**)address) { \
1541 if (fMaxSize != std::wstring::npos) \
1542 return decode(*(const char**)address, \
1543 (Py_ssize_t)fMaxSize * sizeof(type), nullptr, nullptr); \
1544 return decode(*(const char**)address, \
1545 std::char_traits<type>::length(*(type**)address) * \
1546 sizeof(type), \
1547 nullptr, nullptr); \
1548 } \
1549 \
1550 /* empty string in case there's no valid address */ \
1551 type w = snull; \
1552 return decode((const char*)&w, 0, nullptr, nullptr); \
1553 } \
1554 \
1555 bool cpyrt::name##Converter::ToMemory(PyObject* value, void* address, \
1556 PyObject* /* ctxt */) { \
1557 /* convert <value> to C++ <type>*, write it at <address> */ \
1558 PyObject* bstr = encode(value); \
1559 if (!bstr) \
1560 return false; \
1561 \
1562 Py_ssize_t len = PyBytes_GET_SIZE(bstr) - sizeof(type) /*BOM*/; \
1563 Py_ssize_t maxbytes = (Py_ssize_t)fMaxSize * sizeof(type); \
1564 \
1565 /* verify (too long string will cause truncation, no crash) */ \
1566 if (fMaxSize != std::wstring::npos && maxbytes < len) { \
1567 if (PyErr_WarnEx(PyExc_RuntimeWarning, \
1568 (char*)"string too long for " #type \
1569 " array (truncated)", \
1570 1) < 0) { \
1571 Py_DECREF(bstr); \
1572 return false; \
1573 } \
1574 len = maxbytes; \
1575 } \
1576 \
1577 memcpy(*((void**)address), PyBytes_AS_STRING(bstr) + sizeof(type) /*BOM*/, \
1578 len); \
1579 Py_DECREF(bstr); \
1580 /* debatable, but probably more convenient in most cases to null-terminate \
1581 * if enough space */ \
1582 if (len / sizeof(type) < fMaxSize) \
1583 (*(type**)address)[len / sizeof(type)] = snull; \
1584 return true; \
1585 }
1586
1587CPYRT_WIDESTRING_CONVERTER(CString16, char16_t, PyUnicode_AsUTF16String,
1588 PyUnicode_DecodeUTF16, u'\0')
1589CPYRT_WIDESTRING_CONVERTER(CString32, char32_t, PyUnicode_AsUTF32String,
1590 PyUnicode_DecodeUTF32, U'\0')
1591
1592//----------------------------------------------------------------------------
1593bool cpyrt::NonConstCStringConverter::SetArg(PyObject* pyobject,
1594 Parameter& para,
1595 CallContext* ctxt) {
1596 // attempt base class first (i.e. passing a string), but if that fails, try a
1597 // buffer
1598 if (this->CStringConverter::SetArg(pyobject, para, ctxt))
1599 return true;
1600
1601 // apparently failed, try char buffer
1602 PyErr_Clear();
1603 return CArraySetArg(pyobject, para, 'c', sizeof(char));
1604}
1605
1606//----------------------------------------------------------------------------
1607PyObject* cpyrt::NonConstCStringConverter::FromMemory(void* address) {
1608 // assume this is a buffer access if the size is known; otherwise assume
1609 // string
1610 if (fMaxSize != std::string::npos)
1611 return PyUnicode_FromStringAndSize(*(char**)address, (Py_ssize_t)fMaxSize);
1612 return this->CStringConverter::FromMemory(address);
1614
1615//----------------------------------------------------------------------------
1617 void*& address) {
1618 // (1): C++11 style "null pointer"
1619 if (pyobject == gNullPtrObject || pyobject == gDefaultObject) {
1620 address = nullptr;
1621 return true;
1622 }
1623
1624 // (2): allow integer zero to act as a null pointer (C NULL), no deriveds
1625 if (PyLong_CheckExact(pyobject)) {
1626 intptr_t val = (intptr_t)PyLong_AsLongLong(pyobject);
1627 if (val == 0l) {
1628 address = (void*)val;
1629 return true;
1630 }
1631
1632 return false;
1633 }
1634
1635 // (3): opaque PyCapsule (CObject in older pythons) from somewhere
1636 if (PyCapsule_CheckExact(pyobject)) {
1637 address = (void*)PyCapsule_GetPointer(pyobject, nullptr);
1638 return true;
1639 }
1640
1641 return false;
1643
1644//----------------------------------------------------------------------------
1646 CallContext* /*ctxt*/) {
1647 // just convert pointer if it is a C++ object
1648 CPPInstance* pyobj = GetCppInstance(pyobject);
1649 para.fValue.fVoidp = nullptr;
1650 if (pyobj) {
1651 // depending on memory policy, some objects are no longer owned when passed
1652 // to C++
1654 pyobj->CppOwns();
1655
1656 // set pointer (may be null) and declare success
1657 para.fValue.fVoidp = pyobj->GetObject();
1658 para.fTypeCode = 'p';
1659 return true;
1660 }
1661
1662 // handle special cases
1663 if (GetAddressSpecialCase(pyobject, para.fValue.fVoidp)) {
1664 para.fTypeCode = 'p';
1665 return true;
1666 }
1667
1668 // allow ctypes voidp (which if got as a buffer will return void**, not
1669 // void*); use isintance instead of an exact check, b/c c_void_p is the type
1670 // mapper for typedefs of void* (typically opaque handles)
1671 if (PyObject_IsInstance(pyobject, (PyObject*)GetCTypesType(ct_c_void_p))) {
1672 para.fValue.fVoidp = (void*)((cpyrt_tagCDataObject*)pyobject)->b_ptr;
1673 para.fTypeCode = 'V';
1674 return true;
1675 }
1676
1677 // allow any other ctypes pointer type
1678 if (IsCTypesArrayOrPointer(pyobject)) {
1679 void** payload = (void**)((cpyrt_tagCDataObject*)pyobject)->b_ptr;
1680 if (payload) {
1681 para.fValue.fVoidp = *payload;
1682 para.fTypeCode = 'p';
1683 return true;
1684 }
1685 }
1686
1687 // final try: attempt to get buffer
1688 Py_ssize_t buflen =
1689 Utility::GetBuffer(pyobject, '*', 1, para.fValue.fVoidp, false);
1690
1691 // ok if buffer exists (can't perform any useful size checks)
1692 if (para.fValue.fVoidp && buflen != 0) {
1693 para.fTypeCode = 'p';
1694 return true;
1695 }
1696
1697 // give up
1698 return false;
1700
1701//----------------------------------------------------------------------------
1703 // nothing sensible can be done, just return <address> as pylong
1704 if (!address || *(uintptr_t*)address == 0) {
1705 Py_INCREF(gNullPtrObject);
1706 return gNullPtrObject;
1707 }
1708 return CreatePointerView(*(uintptr_t**)address);
1710
1711//----------------------------------------------------------------------------
1713 PyObject* /* ctxt */) {
1714 // just convert pointer if it is a C++ object
1716 if (pyobj) {
1717 // depending on memory policy, some objects are no longer owned when passed
1718 // to C++
1720 pyobj->CppOwns();
1721
1722 // set pointer (may be null) and declare success
1723 *(void**)address = pyobj->GetObject();
1724 return true;
1725 }
1726
1727 // handle special cases
1728 void* ptr = nullptr;
1729 if (GetAddressSpecialCase(value, ptr)) {
1730 *(void**)address = ptr;
1731 return true;
1732 }
1733
1734 // final try: attempt to get buffer
1735 void* buf = nullptr;
1736 Py_ssize_t buflen = Utility::GetBuffer(value, '*', 1, buf, false);
1737 if (!buf || buflen == 0)
1738 return false;
1739
1740 *(void**)address = buf;
1741 return true;
1742}
1743
1744#if __cplusplus >= 202002L
1745
1746namespace cppjit::cpyrt {
1747
1748class StdSpanConverter : public InstanceConverter {
1749public:
1750 StdSpanConverter(std::string const& typeName, interop::TCppScope_t klass,
1751 bool keepControl = false)
1752 : InstanceConverter{klass, keepControl}, fTypeName{typeName} {}
1753
1754 ~StdSpanConverter() {
1755 if (fHasBuffer) {
1756 PyBuffer_Release(&fBufinfo);
1757 }
1758 }
1759
1760 bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override;
1761 bool HasState() override { return true; }
1762
1763private:
1764 std::string fTypeName;
1765 std::span<std::size_t> fBuffer;
1766 bool fHasBuffer = false;
1767 Py_buffer fBufinfo;
1768};
1769
1770} // namespace cppjit::cpyrt
1771
1772//----------------------------------------------------------------------------
1773bool cpyrt::StdSpanConverter::SetArg(PyObject* pyobject, Parameter& para,
1774 CallContext* ctxt) {
1775 auto typecodeFound = Utility::TypecodeMap().find(fTypeName);
1776
1777 // attempt to get buffer if the C++ type maps to a buffer type
1778 if (typecodeFound == Utility::TypecodeMap().end() ||
1779 !PyObject_CheckBuffer(pyobject)) {
1780 // Fall back to regular InstanceConverter
1781 return this->InstanceConverter::SetArg(pyobject, para, ctxt);
1782 }
1783
1784 Py_ssize_t buflen = 0;
1785 char typecode = typecodeFound->second;
1786 memset(&fBufinfo, 0, sizeof(Py_buffer));
1787
1788 if (PyObject_GetBuffer(pyobject, &fBufinfo, PyBUF_FORMAT) == 0) {
1789 if (!Utility::BufferFormatCompatible(typecode, fBufinfo.format)) {
1790 PyErr_Format(PyExc_TypeError,
1791 "buffer has incompatible type: expected '%c' for C++ type "
1792 "'%s', but got format '%s'",
1793 typecode, fTypeName.c_str(),
1794 fBufinfo.format ? fBufinfo.format : "<null>");
1795 PyBuffer_Release(&fBufinfo);
1796 return false;
1797 }
1798 buflen =
1799 Utility::GetBuffer(pyobject, typecode, 1, para.fValue.fVoidp, false);
1800 }
1801
1802 // ok if buffer exists (can't perform any useful size checks)
1803 if (para.fValue.fVoidp && buflen != 0) {
1804 // We assume the layout for any std::span<T> is the same, and just use
1805 // std::span<std::size_t> as a placeholder. Not elegant, but works.
1806 fBuffer = std::span<std::size_t>{(std::size_t*)para.fValue.fVoidp,
1807 static_cast<std::size_t>(buflen)};
1808 fHasBuffer = true;
1809 para.fValue.fVoidp = &fBuffer;
1810 para.fTypeCode = 'V';
1811 return true;
1812 }
1813
1814 return false;
1815}
1816
1817#endif // __cplusplus >= 202002L
1818
1819namespace {
1820
1821template <class type>
1822bool ToArrayFromBuffer(PyObject* owner, void* address, PyObject* ctxt,
1823 const void* buf, Py_ssize_t buflen, cpyrt::dims_t& shape,
1824 bool isFixed) {
1825 if (buflen == 0)
1826 return false;
1827
1828 Py_ssize_t oldsz = 1;
1829 for (Py_ssize_t idim = 0; idim < shape.ndim(); ++idim) {
1830 if (shape[idim] == cpyrt::UNKNOWN_SIZE) {
1831 oldsz = -1;
1832 break;
1833 }
1834 oldsz *= shape[idim];
1835 }
1836 if (shape.ndim() != cpyrt::UNKNOWN_SIZE && 0 < oldsz && oldsz < buflen) {
1837 PyErr_SetString(PyExc_ValueError, "buffer too large for value");
1838 return false;
1839 }
1840
1841 if (isFixed)
1842 memcpy(*(type**)address, buf, (0 < buflen ? buflen : 1) * sizeof(type));
1843 else {
1844 *(type**)address = (type*)buf;
1845 shape.ndim(1);
1846 shape[0] = buflen;
1847 SetLifeLine(ctxt, owner, (intptr_t)address);
1848 }
1849 return true;
1850}
1851
1852} // namespace
1853
1854//----------------------------------------------------------------------------
1855#define CPPJIT_IMPL_ARRAY_CONVERTER(name, ctype, type, code, suffix) \
1856 cpyrt::name##ArrayConverter::name##ArrayConverter(cdims_t dims) \
1857 : fShape(dims) { \
1858 fIsFixed = dims ? fShape[0] != UNKNOWN_SIZE : false; \
1859 } \
1860 \
1861 bool cpyrt::name##ArrayConverter::SetArg( \
1862 PyObject* pyobject, Parameter& para, CallContext* ctxt) { \
1863 /* filter ctypes first b/c their buffer conversion will be wrong */ \
1864 bool convOk = false; \
1865 \
1866 /* 2-dim case: ptr-ptr types */ \
1867 if (!convOk && fShape.ndim() == 2) { \
1868 if (Py_TYPE(pyobject) == GetCTypesPtrType(ct_##ctype)) { \
1869 para.fValue.fVoidp = (void*)((cpyrt_tagCDataObject*)pyobject)->b_ptr; \
1870 para.fTypeCode = 'p'; \
1871 convOk = true; \
1872 } else if (Py_TYPE(pyobject) == GetCTypesType(ct_c_void_p)) { \
1873 /* special case: pass address of c_void_p buffer to return the address \
1874 */ \
1875 para.fValue.fVoidp = (void*)((cpyrt_tagCDataObject*)pyobject)->b_ptr; \
1876 para.fTypeCode = 'p'; \
1877 convOk = true; \
1878 } else if (LowLevelView_Check(pyobject) && \
1879 ((LowLevelView*)pyobject)->fBufInfo.ndim == 2 && \
1880 Utility::BufferFormatCompatible( \
1881 code, ((LowLevelView*)pyobject)->fBufInfo.format)) { \
1882 para.fValue.fVoidp = ((LowLevelView*)pyobject)->get_buf(); \
1883 para.fTypeCode = 'p'; \
1884 convOk = true; \
1885 } \
1886 } \
1887 \
1888 /* 1-dim (accept pointer), or unknown (accept pointer as cast) */ \
1889 if (!convOk) { \
1890 PyTypeObject* ctypes_type = GetCTypesType(ct_##ctype); \
1891 if (Py_TYPE(pyobject) == ctypes_type) { \
1892 para.fValue.fVoidp = (void*)((cpyrt_tagCDataObject*)pyobject)->b_ptr; \
1893 para.fTypeCode = 'p'; \
1894 convOk = true; \
1895 } else if (Py_TYPE(pyobject) == GetCTypesPtrType(ct_##ctype)) { \
1896 para.fValue.fVoidp = (void*)((cpyrt_tagCDataObject*)pyobject)->b_ptr; \
1897 para.fTypeCode = 'V'; \
1898 convOk = true; \
1899 } else if (IsPyCArgObject(pyobject)) { \
1900 cpyrt_tagPyCArgObject* carg = (cpyrt_tagPyCArgObject*)pyobject; \
1901 if (carg->obj && Py_TYPE(carg->obj) == ctypes_type) { \
1902 para.fValue.fVoidp = \
1903 (void*)((cpyrt_tagCDataObject*)carg->obj)->b_ptr; \
1904 para.fTypeCode = 'p'; \
1905 convOk = true; \
1906 } \
1907 } else if (LowLevelView_Check(pyobject) && \
1908 Utility::BufferFormatCompatible( \
1909 code, ((LowLevelView*)pyobject)->fBufInfo.format)) { \
1910 para.fValue.fVoidp = ((LowLevelView*)pyobject)->get_buf(); \
1911 para.fTypeCode = 'p'; \
1912 convOk = true; \
1913 } \
1914 } \
1915 \
1916 /* cast pointer type */ \
1917 if (!convOk) { \
1918 bool ismulti = fShape.ndim() > 1; \
1919 convOk = CArraySetArg(pyobject, para, code, \
1920 ismulti ? sizeof(void*) : sizeof(type), true); \
1921 } \
1922 \
1923 /* memory management and offsetting */ \
1924 if (convOk) \
1925 SetLifeLine(ctxt->fPyContext, pyobject, (intptr_t)this); \
1926 \
1927 return convOk; \
1928 } \
1929 \
1930 PyObject* cpyrt::name##ArrayConverter::FromMemory(void* address) { \
1931 if (!fIsFixed) \
1932 return CreateLowLevelView##suffix((type**)address, fShape); \
1933 return CreateLowLevelView##suffix(*(type**)address, fShape); \
1934 } \
1935 \
1936 bool cpyrt::name##ArrayConverter::ToMemory(PyObject* value, void* address, \
1937 PyObject* ctxt) { \
1938 if (fShape.ndim() <= 1 || fIsFixed) { \
1939 void* buf = nullptr; \
1940 Py_ssize_t buflen = Utility::GetBuffer(value, code, sizeof(type), buf); \
1941 if (buflen == 0) \
1942 return false; \
1943 \
1944 Py_ssize_t oldsz = 1; \
1945 for (Py_ssize_t idim = 0; idim < fShape.ndim(); ++idim) { \
1946 if (fShape[idim] == UNKNOWN_SIZE) { \
1947 oldsz = -1; \
1948 break; \
1949 } \
1950 oldsz *= fShape[idim]; \
1951 } \
1952 if (fShape.ndim() != UNKNOWN_SIZE && 0 < oldsz && oldsz < buflen) { \
1953 PyErr_SetString(PyExc_ValueError, "buffer too large for value"); \
1954 return false; \
1955 } \
1956 \
1957 if (fIsFixed) \
1958 memcpy(*(type**)address, buf, \
1959 (0 < buflen ? buflen : 1) * sizeof(type)); \
1960 else { \
1961 *(type**)address = (type*)buf; \
1962 fShape.ndim(1); \
1963 fShape[0] = buflen; \
1964 SetLifeLine(ctxt, value, (intptr_t)address); \
1965 } \
1966 \
1967 } else { /* multi-dim, non-flat array; assume structure matches */ \
1968 void* buf = nullptr; /* TODO: GetBuffer() assumes flat? */ \
1969 Py_ssize_t buflen = Utility::GetBuffer(value, code, sizeof(void*), buf); \
1970 if (buflen == 0) \
1971 return false; \
1972 *(type**)address = (type*)buf; \
1973 SetLifeLine(ctxt, value, (intptr_t)address); \
1974 } \
1975 return true; \
1976 }
1977
1978//----------------------------------------------------------------------------
1979// clang-format off
1980CPPJIT_IMPL_ARRAY_CONVERTER(Bool, c_bool, bool, '?', )
1981CPPJIT_IMPL_ARRAY_CONVERTER(SChar, c_char, signed char, 'b', )
1982CPPJIT_IMPL_ARRAY_CONVERTER(UChar, c_ubyte, unsigned char, 'B', )
1983CPPJIT_IMPL_ARRAY_CONVERTER(Byte, c_ubyte, std::byte, 'B', )
1984CPPJIT_IMPL_ARRAY_CONVERTER(Int8, c_byte, int8_t, 'b', _i8)
1985CPPJIT_IMPL_ARRAY_CONVERTER(Int16, c_int16, int16_t, 'h', _i16)
1986CPPJIT_IMPL_ARRAY_CONVERTER(Int32, c_int32, int32_t, 'i', _i32)
1987CPPJIT_IMPL_ARRAY_CONVERTER(UInt8, c_ubyte, uint8_t, 'B', _i8)
1988CPPJIT_IMPL_ARRAY_CONVERTER(UInt16, c_uint16, uint16_t, 'H', _i16)
1989CPPJIT_IMPL_ARRAY_CONVERTER(UInt32, c_uint32, uint32_t, 'I', _i32)
1990CPPJIT_IMPL_ARRAY_CONVERTER(Short, c_short, short, 'h', )
1991CPPJIT_IMPL_ARRAY_CONVERTER(UShort, c_ushort, unsigned short, 'H', )
1992CPPJIT_IMPL_ARRAY_CONVERTER(Int, c_int, int, 'i', )
1993CPPJIT_IMPL_ARRAY_CONVERTER(UInt, c_uint, unsigned int, 'I', )
1994CPPJIT_IMPL_ARRAY_CONVERTER(Long, c_long, long, 'l', )
1995CPPJIT_IMPL_ARRAY_CONVERTER(ULong, c_ulong, unsigned long, 'L', )
1996CPPJIT_IMPL_ARRAY_CONVERTER(LLong, c_longlong, long long, 'q', )
1997CPPJIT_IMPL_ARRAY_CONVERTER(ULLong, c_ulonglong, unsigned long long, 'Q', )
1998CPPJIT_IMPL_ARRAY_CONVERTER(Float, c_float, float, 'f', )
1999CPPJIT_IMPL_ARRAY_CONVERTER(Double, c_double, double, 'd', )
2000CPPJIT_IMPL_ARRAY_CONVERTER(LDouble, c_longdouble, long double, 'g', )
2001CPPJIT_IMPL_ARRAY_CONVERTER(ComplexF, c_fcomplex, std::complex<float>, 'z', )
2002CPPJIT_IMPL_ARRAY_CONVERTER(ComplexD, c_complex, std::complex<double>, 'Z', )
2003// clang-format on
2004
2005//----------------------------------------------------------------------------
2006bool cpyrt::CStringArrayConverter::SetArg(PyObject* pyobject, Parameter& para,
2007 CallContext* ctxt) {
2008 if (Py_TYPE(pyobject) == GetCTypesPtrType(ct_c_char_p) ||
2009 (1 < fShape.ndim() &&
2010 PyObject_IsInstance(pyobject, (PyObject*)GetCTypesType(ct_c_pointer)))) {
2011 // 2nd predicate is ebatable: it's a catch-all for ctypes-styled
2012 // multi-dimensional objects, which at this point does not check further
2013 // dimensionality
2014 para.fValue.fVoidp = (void*)((cpyrt_tagCDataObject*)pyobject)->b_ptr;
2015 para.fTypeCode = 'V';
2016 return true;
2017
2018 } else if (PySequence_Check(pyobject) && !PyUnicode_Check(pyobject) &&
2019 !PyBytes_Check(pyobject)) {
2020 // for (auto& p : fBuffer) free(p);
2021 fBuffer.clear();
2022
2023 size_t len = (size_t)PySequence_Size(pyobject);
2024 if (len == (size_t)-1) {
2025 PyErr_SetString(PyExc_ValueError,
2026 "can not convert sequence object of unknown length");
2027 return false;
2028 }
2029
2030 fBuffer.reserve(len);
2031 for (size_t i = 0; i < len; ++i) {
2032 PyObject* item = PySequence_GetItem(pyobject, i);
2033 if (item) {
2034 Py_ssize_t sz;
2035 const char* p = cpyrt_PyText_AsStringAndSize(item, &sz);
2036 Py_DECREF(item);
2037
2038 if (p)
2039 fBuffer.push_back(p);
2040 else {
2041 PyErr_Format(PyExc_TypeError, "could not convert item %d to string",
2042 (int)i);
2043 return false;
2044 }
2045
2046 } else
2047 return false;
2048 }
2049
2050 para.fValue.fVoidp = (void*)fBuffer.data();
2051 para.fTypeCode = 'p';
2052 return true;
2053 }
2054
2055 return SCharArrayConverter::SetArg(pyobject, para, ctxt);
2056}
2057
2058//----------------------------------------------------------------------------
2059PyObject* cpyrt::CStringArrayConverter::FromMemory(void* address) {
2060 if (fIsFixed)
2061 return CreateLowLevelView(*(char**)address, fShape);
2062 else if (fShape[0] == UNKNOWN_SIZE)
2063 return CreateLowLevelViewString((const char**)address, fShape);
2064 return CreateLowLevelViewString(*(const char***)address, fShape);
2065}
2066
2067bool cpyrt::CStringArrayConverter::ToMemory(PyObject* value, void* address,
2068 PyObject* ctxt) {
2069 // Unlike the other array converters, this one also accepts a Python string.
2070 Py_ssize_t len;
2071 if (const char* cstr = cpyrt_PyText_AsStringAndSize(value, &len)) {
2072 return ToArrayFromBuffer<char>(value, address, ctxt, cstr, len, fShape,
2073 fIsFixed);
2074 }
2075 return SCharArrayConverter::ToMemory(value, address, ctxt);
2076}
2077
2078//----------------------------------------------------------------------------
2079PyObject* cpyrt::NonConstCStringArrayConverter::FromMemory(void* address) {
2080 if (fIsFixed)
2081 return CreateLowLevelView(*(char**)address, fShape);
2082 else if (fShape[0] == UNKNOWN_SIZE)
2083 return CreateLowLevelViewString((char**)address, fShape);
2084 return CreateLowLevelViewString(*(char***)address, fShape);
2085}
2086
2087//- converters for special cases ---------------------------------------------
2088bool cpyrt::NullptrConverter::SetArg(PyObject* pyobject, Parameter& para,
2089 CallContext* /* ctxt */) {
2090 // Only allow C++11 style nullptr to pass
2091 if (pyobject == gNullPtrObject || pyobject == gDefaultObject) {
2092 para.fValue.fVoidp = nullptr;
2093 para.fTypeCode = 'p';
2094 return true;
2095 }
2096 return false;
2098
2099//----------------------------------------------------------------------------
2100template <typename T>
2101static inline bool cpyrt_PyUnicodeAsBytes2Buffer(PyObject* pyobject,
2102 T& buffer) {
2103 PyObject* pybytes = nullptr;
2104 if (PyBytes_Check(pyobject)) {
2105 Py_INCREF(pyobject);
2106 pybytes = pyobject;
2107 } else if (PyUnicode_Check(pyobject)) {
2108 pybytes = PyUnicode_AsUTF8String(pyobject);
2109 }
2110
2111 if (pybytes) {
2112 Py_ssize_t len;
2113 const char* cstr = nullptr;
2114 PyBytes_AsStringAndSize(pybytes, (char**)&cstr, &len);
2115 if (cstr)
2116 buffer = T{cstr, (typename T::size_type)len};
2117 Py_DECREF(pybytes);
2118 return (bool)cstr;
2119 }
2121 return false;
2122}
2123
2124#define CPPJIT_IMPL_STRING_AS_PRIMITIVE_CONVERTER(name, type, F1, F2) \
2125 cpyrt::name##Converter::name##Converter(bool keepControl) \
2126 : InstanceConverter(interop::GetFullScope(#type), keepControl) {} \
2127 \
2128 bool cpyrt::name##Converter::SetArg(PyObject* pyobject, Parameter& para, \
2129 CallContext* ctxt) { \
2130 if (cpyrt_PyUnicodeAsBytes2Buffer(pyobject, fBuffer)) { \
2131 para.fValue.fVoidp = &fBuffer; \
2132 para.fTypeCode = 'V'; \
2133 return true; \
2134 } \
2135 \
2136 PyErr_Clear(); \
2137 if (!PyLong_Check(pyobject)) { \
2138 bool result = InstanceConverter::SetArg(pyobject, para, ctxt); \
2139 para.fTypeCode = 'V'; \
2140 return result; \
2141 } \
2142 \
2143 return false; \
2144 } \
2145 \
2146 PyObject* cpyrt::name##Converter::FromMemory(void* address) { \
2147 if (address) \
2148 return InstanceConverter::FromMemory(address); \
2149 auto* empty = new type(); \
2150 return BindCppObjectNoCast(interop::TCppObject_t((void*)empty), fClass, \
2151 CPPInstance::kIsOwner); \
2152 } \
2153 \
2154 bool cpyrt::name##Converter::ToMemory(PyObject* value, void* address, \
2155 PyObject* ctxt) { \
2156 if (cpyrt_PyUnicodeAsBytes2Buffer(value, *((type*)address))) \
2157 return true; \
2158 return InstanceConverter::ToMemory(value, address, ctxt); \
2159 }
2160
2161CPPJIT_IMPL_STRING_AS_PRIMITIVE_CONVERTER(STLString, std::string, c_str, size)
2162
2163cpyrt::STLWStringConverter::STLWStringConverter(bool keepControl)
2164 : InstanceConverter(interop::GetFullScope("std::wstring"), keepControl) {}
2165
2166bool cpyrt::STLWStringConverter::SetArg(PyObject* pyobject, Parameter& para,
2167 CallContext* ctxt) {
2168 if (PyUnicode_Check(pyobject)) {
2169 Py_ssize_t len = PyUnicode_GET_LENGTH(pyobject);
2170 fBuffer.resize(len);
2171 PyUnicode_AsWideChar(pyobject, &fBuffer[0], len);
2172 para.fValue.fVoidp = &fBuffer;
2173 para.fTypeCode = 'V';
2174 return true;
2175 }
2176
2177 if (!PyLong_Check(pyobject)) {
2178 bool result = InstancePtrConverter<false>::SetArg(pyobject, para, ctxt);
2179 para.fTypeCode = 'V';
2180 return result;
2181 }
2182
2183 return false;
2184}
2185
2186PyObject* cpyrt::STLWStringConverter::FromMemory(void* address) {
2187 if (address)
2188 return PyUnicode_FromWideChar(((std::wstring*)address)->c_str(),
2189 ((std::wstring*)address)->size());
2190 wchar_t w = L'\0';
2191 return PyUnicode_FromWideChar(&w, 0);
2192}
2193
2194bool cpyrt::STLWStringConverter::ToMemory(PyObject* value, void* address,
2195 PyObject* ctxt) {
2196 if (PyUnicode_Check(value)) {
2197 Py_ssize_t len = PyUnicode_GET_LENGTH(value);
2198 wchar_t* buf = new wchar_t[len + 1];
2199 PyUnicode_AsWideChar(value, buf, len);
2200 *((std::wstring*)address) = std::wstring(buf, len);
2201 delete[] buf;
2202 return true;
2203 }
2204 return InstanceConverter::ToMemory(value, address, ctxt);
2205}
2206
2207cpyrt::STLStringViewConverter::STLStringViewConverter(bool keepControl)
2208 : InstanceConverter(interop::GetFullScope("std::string_view"),
2209 keepControl) {}
2210
2211bool cpyrt::STLStringViewConverter::SetArg(PyObject* pyobject, Parameter& para,
2212 CallContext* ctxt) {
2213 // normal instance convertion (eg. string_view object passed)
2214 if (!PyLong_Check(pyobject)) {
2216 if (InstanceConverter::SetArg(pyobject, para, ctxt)) {
2217 para.fTypeCode = 'V';
2218 return true;
2219 } else
2220 PyErr_Clear();
2221 }
2222
2223 // passing of a Python string; buffering done Python-side b/c str is immutable
2224 Py_ssize_t len;
2225 const char* cstr = cpyrt_PyText_AsStringAndSize(pyobject, &len);
2226 if (cstr) {
2227 SetLifeLine(ctxt->fPyContext, pyobject, (intptr_t)this);
2228 fBuffer = std::string_view(cstr, (std::string_view::size_type)len);
2229 para.fValue.fVoidp = &fBuffer;
2230 para.fTypeCode = 'V';
2231 return true;
2232 }
2233
2234 if (!CPPInstance_Check(pyobject))
2235 return false;
2236
2237 // special case of a C++ std::string object; life-time management is left to
2238 // the caller to ensure any external changes propagate correctly
2239 if (CPPInstance_Check(pyobject)) {
2240 static interop::TCppScope_t sStringID =
2242 CPPInstance* pyobj = (CPPInstance*)pyobject;
2243 if (pyobj->ObjectIsA() == sStringID) {
2244 void* ptr = pyobj->GetObject();
2245 if (!ptr)
2246 return false; // leaves prior conversion error for report
2247
2248 PyErr_Clear();
2249
2250 fBuffer = *((std::string*)ptr);
2251 para.fValue.fVoidp = &fBuffer;
2252 para.fTypeCode = 'V';
2253 return true;
2254 }
2255 }
2256
2257 return false;
2258}
2259
2260PyObject* cpyrt::STLStringViewConverter::FromMemory(void* address) {
2261 if (address)
2262 return InstanceConverter::FromMemory(address);
2263 auto* empty = new std::string_view();
2265}
2266
2267bool cpyrt::STLStringViewConverter::ToMemory(PyObject* value, void* address,
2268 PyObject* ctxt) {
2269 // common case of simple object assignment
2270 if (InstanceConverter::ToMemory(value, address, ctxt))
2271 return true;
2272
2273 // assignment of a Python string; buffering done Python-side b/c str is
2274 // immutable
2275 Py_ssize_t len;
2276 const char* cstr = cpyrt_PyText_AsStringAndSize(value, &len);
2277 if (cstr) {
2278 SetLifeLine(ctxt, value, (intptr_t)this);
2279 *reinterpret_cast<std::string_view*>(address) =
2280 std::string_view(cstr, (std::string_view::size_type)len);
2281 return true;
2282 }
2283
2284 return false;
2285}
2286
2287bool cpyrt::STLStringMoveConverter::SetArg(PyObject* pyobject, Parameter& para,
2288 CallContext* ctxt) {
2289 // convert <pyobject> to C++ std::string&&, set arg for call
2290 int moveit_reason = 3; // move on temporary fBuffer
2291 if (CPPInstance_Check(pyobject)) {
2292 CPPInstance* pyobj = (CPPInstance*)pyobject;
2293 if (pyobj->fFlags & CPPInstance::kIsRValue) {
2294 pyobj->fFlags &= ~CPPInstance::kIsRValue;
2295 moveit_reason = 2;
2296 } else if (PyUnstable_Object_IsUniqueReferencedTemporary(pyobject)) {
2297 moveit_reason = 1;
2298 } else
2299 moveit_reason = 0;
2300 }
2301
2302 if (moveit_reason) {
2303 bool result = this->STLStringConverter::SetArg(pyobject, para, ctxt);
2304 if (!result && moveit_reason == 2) // restore the movability flag?
2305 ((CPPInstance*)pyobject)->fFlags |= CPPInstance::kIsRValue;
2306 return result;
2307 }
2308
2309 PyErr_SetString(PyExc_ValueError, "object is not an rvalue");
2310 return false; // not a temporary or movable object
2312
2313//----------------------------------------------------------------------------
2314template <bool ISCONST>
2316 Parameter& para,
2317 CallContext* ctxt) {
2318 // convert <pyobject> to C++ instance*, set arg for call
2319 CPPInstance* pyobj =
2320 GetCppInstance(pyobject, ISCONST ? fClass : interop::TCppScope_t{});
2321 if (!pyobj) {
2322 if (GetAddressSpecialCase(pyobject, para.fValue.fVoidp)) {
2323 para.fTypeCode = 'p'; // allow special cases such as nullptr
2324 return true;
2325 }
2326
2327 // not a cppjit object (TODO: handle SWIG etc.)
2328 return false;
2329 }
2330
2331 // smart pointers should only extract the pointer if this is NOT an implicit
2332 // conversion to another smart pointer
2333 if (pyobj->IsSmart() && IsConstructor(ctxt->fFlags) &&
2335 return false;
2336
2337 interop::TCppScope_t oisa = pyobj->ObjectIsA();
2338 if (oisa && (oisa == fClass || interop::IsSubclass(oisa, fClass))) {
2339 // depending on memory policy, some objects need releasing when passed into
2340 // functions
2341 if (!KeepControl() && !UseStrictOwnership())
2342 pyobj->CppOwns();
2343
2344 // calculate offset between formal and actual arguments
2345 para.fValue.fVoidp = pyobj->GetObject();
2346 if (oisa != fClass) {
2348 oisa, fClass, para.fValue.fVoidp, 1 /* up-cast */);
2349 }
2350
2351 // set pointer (may be null) and declare success
2352 para.fTypeCode = 'p';
2353 return true;
2354 }
2355
2356 return false;
2358
2359//----------------------------------------------------------------------------
2360template <bool ISCONST>
2362 // construct python object from C++ instance read at <address>
2363 if (ISCONST)
2364 return BindCppObject(*(void**)address, fClass); // by pointer value
2365
2366 // a writable pointer variable's proxy tracks the pointer itself, so it can
2367 // only down-cast where the actual class shares the address; abstract
2368 // declared types stay put as Python may replace the pointee (see test21)
2369 void* pobj = *(void**)address;
2371 if (pobj && !interop::IsAbstract(fClass)) {
2373 if (actual && actual != fClass &&
2374 interop::GetBaseOffset(actual, fClass, pobj, -1 /* down-cast */) == 0)
2375 klass = actual;
2376 }
2377 return BindCppObject(address, klass, CPPInstance::kIsReference); // modifiable
2379
2380//----------------------------------------------------------------------------
2381template <bool ISCONST>
2383 void* address,
2384 PyObject* /* ctxt */) {
2385 // convert <value> to C++ instance, write it at <address>
2386 CPPInstance* pyobj =
2388 if (!pyobj) {
2389 void* ptr = nullptr;
2390 if (GetAddressSpecialCase(value, ptr)) {
2391 *(void**)address = ptr; // allow special cases such as nullptr
2392 return true;
2393 }
2394
2395 // not a cppjit object (TODO: handle SWIG etc.)
2396 return false;
2397 }
2398
2399 if (interop::IsSubclass(pyobj->ObjectIsA(), fClass)) {
2400 // depending on memory policy, some objects need releasing when passed into
2401 // functions
2402 if (!KeepControl() && !UseStrictOwnership())
2403 ((CPPInstance*)value)->CppOwns();
2404
2405 *(void**)address = pyobj->GetObject();
2406 return true;
2407 }
2408
2409 return false;
2410}
2411
2412// TODO: CONSOLIDATE Instance, InstanceRef, InstancePtr ...
2413
2414//----------------------------------------------------------------------------
2415bool cpyrt::InstanceConverter::SetArg(PyObject* pyobject, Parameter& para,
2416 CallContext* ctxt) {
2417 // convert <pyobject> to C++ instance, set arg for call
2418 CPPInstance* pyobj = GetCppInstance(pyobject, fClass);
2419 if (pyobj) {
2420 auto oisa = pyobj->ObjectIsA();
2421 if (oisa && ((oisa == (interop::IsTypedefed(fClass)
2423 : fClass)) ||
2424 interop::IsSubclass(oisa, fClass))) {
2425 // calculate offset between formal and actual arguments
2426 para.fValue.fVoidp = pyobj->GetObject();
2427 if (!para.fValue.fVoidp)
2428 return false;
2429
2430 if (oisa != fClass) {
2432 pyobj->ObjectIsA(), fClass, para.fValue.fVoidp, 1 /* up-cast */);
2433 }
2434
2435 para.fTypeCode = 'V';
2436 return true;
2437 }
2438 }
2439
2440 return (bool)ConvertImplicit(fClass, pyobject, para, ctxt);
2441}
2442
2443//----------------------------------------------------------------------------
2444PyObject* cpyrt::InstanceConverter::FromMemory(void* address) {
2445 // This should not need a cast (ie. BindCppObjectNoCast), but performing the
2446 // cast here means callbacks receive down-casted object when passed by-ptr,
2447 // which is needed for object identity. The latter case is assumed to be more
2448 // common than conversion of (global) objects.
2449 return BindCppObject((interop::TCppObject_t)address, fClass);
2450}
2451
2452//----------------------------------------------------------------------------
2453bool cpyrt::InstanceConverter::ToMemory(PyObject* value, void* address,
2454 PyObject* /* ctxt */) {
2455 // assign value to C++ instance living at <address> through assignment
2456 // operator
2457 PyObject* pyobj = BindCppObjectNoCast(address, fClass);
2458 PyObject* result =
2459 PyObject_CallMethodOneArg(pyobj, PyStrings::gAssign, value);
2460 Py_DECREF(pyobj);
2461
2462 if (result) {
2463 Py_DECREF(result);
2464 return true;
2465 }
2466 return false;
2467}
2468
2469//----------------------------------------------------------------------------
2470bool cpyrt::InstanceRefConverter::SetArg(PyObject* pyobject, Parameter& para,
2471 CallContext* ctxt) {
2472 // convert <pyobject> to C++ instance&, set arg for call
2473 CPPInstance* pyobj =
2474 GetCppInstance(pyobject, fIsConst ? fClass : interop::TCppScope_t{});
2475 if (pyobj) {
2476
2477 // reject moves
2478 if (pyobj->fFlags & CPPInstance::kIsRValue)
2479 return false;
2480
2481 // smart pointers can end up here in case of a move, so preferentially match
2482 // the smart type directly
2483 bool argset = false;
2485 if (pyobj->IsSmart()) {
2486 cls = pyobj->ObjectIsA(false);
2487 if (cls && interop::IsSubclass(cls, fClass)) {
2488 para.fValue.fVoidp = pyobj->GetObjectRaw();
2489 argset = true;
2490 }
2491 }
2492
2493 if (!argset) {
2494 cls = pyobj->ObjectIsA();
2495 if (cls && interop::IsSubclass(cls, fClass)) {
2496 para.fValue.fVoidp = pyobj->GetObject();
2497 argset = true;
2498 }
2499 }
2500
2501 if (argset) {
2502 // do not allow null pointers through references
2503 if (!para.fValue.fVoidp) {
2504 PyErr_SetString(PyExc_ReferenceError,
2505 "attempt to access a null-pointer");
2506 return false;
2507 }
2508
2509 // calculate offset between formal and actual arguments
2510 if (cls != fClass) {
2512 cls, fClass, para.fValue.fVoidp, 1 /* up-cast */);
2513 }
2514
2515 para.fTypeCode = 'V';
2516 return true;
2517 }
2518 }
2519
2520 if (!fIsConst) // no implicit conversion possible
2521 return false;
2522
2523 return (bool)ConvertImplicit(fClass, pyobject, para, ctxt);
2524}
2525
2526//----------------------------------------------------------------------------
2527PyObject* cpyrt::InstanceRefConverter::FromMemory(void* address) {
2530}
2531
2532//----------------------------------------------------------------------------
2533bool cpyrt::InstanceMoveConverter::SetArg(PyObject* pyobject, Parameter& para,
2534 CallContext* ctxt) {
2535 // convert <pyobject> to C++ instance&&, set arg for call
2536 CPPInstance* pyobj =
2537 GetCppInstance(pyobject, fClass, true /* accept_rvalue */);
2538 if (!pyobj || (pyobj->fFlags & CPPInstance::kIsLValue)) {
2539 // implicit conversion is fine as the temporary by definition is moveable
2540 return (bool)ConvertImplicit(fClass, pyobject, para, ctxt);
2541 }
2542
2543 // moving is same as by-ref, but have to check that move is allowed
2544 int moveit_reason = 0;
2545 if (pyobj->fFlags & CPPInstance::kIsRValue) {
2546 pyobj->fFlags &= ~CPPInstance::kIsRValue;
2547 moveit_reason = 2;
2548 } else if (PyUnstable_Object_IsUniqueReferencedTemporary(pyobject)) {
2549 moveit_reason = 1;
2550 }
2551
2552 if (moveit_reason) {
2553 bool result = this->InstanceRefConverter::SetArg(pyobject, para, ctxt);
2554 if (!result && moveit_reason == 2) // restore the movability flag?
2555 ((CPPInstance*)pyobject)->fFlags |= CPPInstance::kIsRValue;
2556 return result;
2557 }
2558
2559 PyErr_SetString(PyExc_ValueError, "object is not an rvalue");
2560 return false; // not a temporary or movable object
2561}
2562
2563//----------------------------------------------------------------------------
2564template <bool ISREFERENCE>
2565bool cpyrt::InstancePtrPtrConverter<ISREFERENCE>::SetArg(
2566 PyObject* pyobject, Parameter& para, CallContext* /*ctxt*/) {
2567 // convert <pyobject> to C++ instance**, set arg for call
2568 CPPInstance* pyobj = GetCppInstance(pyobject);
2569 if (!pyobj) {
2570 if (!ISREFERENCE &&
2571 (pyobject == gNullPtrObject || pyobject == gDefaultObject)) {
2572 // allow nullptr as a special case
2573 para.fValue.fVoidp = nullptr;
2574 para.fTypeCode = 'p';
2575 return true;
2576 }
2577 return false; // not a cppjit object (TODO: handle SWIG etc.)
2578 }
2579
2580 if (interop::IsSubclass(pyobj->ObjectIsA(), fClass)) {
2581 // depending on memory policy, some objects need releasing when passed into
2582 // functions
2583 if (!KeepControl() && !UseStrictOwnership())
2584 pyobj->CppOwns();
2585
2586 // set pointer (may be null) and declare success
2587 if (pyobj->fFlags & CPPInstance::kIsReference) // already a ptr to object?
2588 para.fValue.fVoidp = pyobj->GetObjectRaw();
2589 else
2590 para.fValue.fVoidp = &pyobj->GetObjectRaw();
2591 para.fTypeCode = ISREFERENCE ? 'V' : 'p';
2592 return true;
2593 }
2594
2595 return false;
2596}
2597
2598//----------------------------------------------------------------------------
2599template <bool ISREFERENCE>
2600PyObject*
2601cpyrt::InstancePtrPtrConverter<ISREFERENCE>::FromMemory(void* address) {
2602 // construct python object from C++ instance* read at <address>
2603 return BindCppObject(*(void**)address, fClass,
2605}
2606
2607//----------------------------------------------------------------------------
2608template <bool ISREFERENCE>
2609bool cpyrt::InstancePtrPtrConverter<ISREFERENCE>::ToMemory(
2610 PyObject* value, void* address, PyObject* /* ctxt */) {
2611 // convert <value> to C++ instance*, write it at <address>
2613 if (!pyobj) {
2615 // allow nullptr as a special case
2616 *(void**)address = nullptr;
2617 return true;
2618 }
2619 return false; // not a cppjit object (TODO: handle SWIG etc.)
2620 }
2621
2622 if (interop::IsSubclass(pyobj->ObjectIsA(), fClass)) {
2623 // depending on memory policy, some objects need releasing when passed into
2624 // functions
2625 if (!KeepControl() && !UseStrictOwnership())
2626 pyobj->CppOwns();
2627
2628 // register the value for potential recycling
2630
2631 // set pointer (may be null) and declare success
2632 *(void**)address = pyobj->GetObject();
2633 return true;
2634 }
2635
2636 return false;
2637}
2638
2639namespace cppjit::cpyrt {
2640// Instantiate the templates
2643template class cpyrt::InstancePtrPtrConverter<true>;
2644template class cpyrt::InstancePtrPtrConverter<false>;
2645} // namespace cppjit::cpyrt
2646
2647//----------------------------------------------------------------------------
2648bool cpyrt::InstanceArrayConverter::SetArg(PyObject* pyobject, Parameter& para,
2649 CallContext* /* txt */) {
2650 // convert <pyobject> to C++ instance**, set arg for call
2651 while (PyTuple_Check(pyobject) && !TupleOfInstances_CheckExact(pyobject)) {
2652 if (PyTuple_Size(pyobject) > 0)
2653 pyobject = PyTuple_GetItem(pyobject, 0);
2654 else
2655 return false;
2656 }
2657 if (!TupleOfInstances_CheckExact(pyobject))
2658 return false; // no guarantee that the tuple is okay
2659
2660 // treat the first instance of the tuple as the start of the array, and pass
2661 // it by pointer (TODO: store and check sizes)
2662 if (PyTuple_Size(pyobject) < 1)
2663 return false;
2664
2665 PyObject* first = PyTuple_GetItem(pyobject, 0);
2666 if (!CPPInstance_Check(first))
2667 return false; // should not happen
2668
2669 if (interop::IsSubclass(((CPPInstance*)first)->ObjectIsA(), fClass)) {
2670 // no memory policies supported; set pointer (may be null) and declare
2671 // success
2672 para.fValue.fVoidp = ((CPPInstance*)first)->GetObject();
2673 para.fTypeCode = 'p';
2674 return true;
2675 }
2676
2677 return false;
2678}
2679
2680//----------------------------------------------------------------------------
2681PyObject* cpyrt::InstanceArrayConverter::FromMemory(void* address) {
2682 // construct python tuple of instances from C++ array read at <address>
2683 return BindCppObjectArray(*(char**)address, fClass, fShape);
2684}
2685
2686//----------------------------------------------------------------------------
2687bool cpyrt::InstanceArrayConverter::ToMemory(PyObject* /* value */,
2688 void* /* address */,
2689 PyObject* /* ctxt */) {
2690 // convert <value> to C++ array of instances, write it at <address>
2691
2692 // TODO: need to have size both for the array and from the input
2693 PyErr_SetString(PyExc_NotImplementedError,
2694 "access to C-arrays of objects not yet implemented!");
2695 return false;
2696}
2697
2698//___________________________________________________________________________
2699// Cling WORKAROUND -- classes for STL iterators are completely undefined in
2700// that they come in a bazillion different guises, so just do whatever
2701bool cpyrt::STLIteratorConverter::SetArg(PyObject* pyobject, Parameter& para,
2702 CallContext* /* ctxt */) {
2703 if (!CPPInstance_Check(pyobject))
2704 return false;
2705
2706 // just set the pointer value, no check
2707 CPPInstance* pyobj = (CPPInstance*)pyobject;
2708 para.fValue.fVoidp = pyobj->GetObject();
2709 para.fTypeCode = 'V';
2710 return true;
2711}
2712// -- END Cling WORKAROUND
2713
2714//----------------------------------------------------------------------------
2715bool cpyrt::VoidPtrRefConverter::SetArg(PyObject* pyobject, Parameter& para,
2716 CallContext* /* ctxt */) {
2717 // convert <pyobject> to C++ void*&, set arg for call
2718 CPPInstance* pyobj = GetCppInstance(pyobject);
2719 if (pyobj) {
2720 para.fValue.fVoidp = &pyobj->GetObjectRaw();
2721 para.fTypeCode = 'V';
2722 return true;
2723 }
2724
2725 return false;
2726}
2727
2728//----------------------------------------------------------------------------
2729cpyrt::VoidPtrPtrConverter::VoidPtrPtrConverter(cdims_t dims,
2730 const std::string& failureMsg)
2731 : fShape(dims), fFailureMsg(failureMsg) {
2732 fIsFixed = dims ? fShape[0] != UNKNOWN_SIZE : false;
2733}
2734
2735//----------------------------------------------------------------------------
2736bool cpyrt::VoidPtrPtrConverter::SetArg(PyObject* pyobject, Parameter& para,
2737 CallContext* /* ctxt */) {
2738 // convert <pyobject> to C++ void**, set arg for call
2739 CPPInstance* pyobj = GetCppInstance(pyobject);
2740 if (pyobj) {
2741 // this is a C++ object, take and set its address
2742 para.fValue.fVoidp = &pyobj->GetObjectRaw();
2743 para.fTypeCode = 'p';
2744 return true;
2745 } else if (IsPyCArgObject(pyobject)) {
2747 if (carg->obj) {
2748 para.fValue.fVoidp = (void*)((cpyrt_tagCDataObject*)carg->obj)->b_ptr;
2749 para.fTypeCode = 'p';
2750 return true;
2751 }
2752 }
2753
2754 // buffer objects are allowed under "user knows best" (this includes the
2755 // buffer interface to ctypes.c_void_p, which results in a void**)
2756 Py_ssize_t buflen =
2757 Utility::GetBuffer(pyobject, '*', 1, para.fValue.fVoidp, false);
2758
2759 // ok if buffer exists (can't perform any useful size checks)
2760 if (para.fValue.fVoidp && buflen != 0) {
2761 para.fTypeCode = 'p';
2762 return true;
2763 }
2764
2765 return false;
2766}
2767
2768//----------------------------------------------------------------------------
2769PyObject* cpyrt::VoidPtrPtrConverter::FromMemory(void* address) {
2770 // read a void** from address; since this is unknown, uintptr_t is used (user
2771 // can cast)
2772 if (!address || *(ptrdiff_t*)address == 0) {
2773 Py_INCREF(gNullPtrObject);
2774 return gNullPtrObject;
2775 }
2776 if (!fIsFixed)
2777 return CreatePointerView((uintptr_t**)address, fShape);
2778 return CreatePointerView(*(uintptr_t**)address, fShape);
2779}
2780
2781//----------------------------------------------------------------------------
2782bool cpyrt::PyObjectConverter::SetArg(PyObject* pyobject, Parameter& para,
2783 CallContext* /* ctxt */) {
2784 // by definition: set and declare success
2785 para.fValue.fVoidp = pyobject;
2786 para.fTypeCode = 'p';
2787 return true;
2788}
2789
2790PyObject* cpyrt::PyObjectConverter::FromMemory(void* address) {
2791 // construct python object from C++ PyObject* read at <address>
2792 PyObject* pyobject = *((PyObject**)address);
2793
2794 if (!pyobject) {
2796 }
2797
2798 Py_INCREF(pyobject);
2799 return pyobject;
2800}
2801
2802bool cpyrt::PyObjectConverter::ToMemory(PyObject* value, void* address,
2803 PyObject* /* ctxt */) {
2804 // no conversion needed, write <value> at <address>
2805 Py_INCREF(value);
2806 Py_XDECREF(*((PyObject**)address));
2807 *((PyObject**)address) = value;
2808 return true;
2809}
2810
2811//- function pointer converter -----------------------------------------------
2812static unsigned int sWrapperCounter = 0;
2813// cache mapping signature/return type to python callable and corresponding
2814// wrapper
2815typedef std::string RetSigKey_t;
2816static std::unordered_map<RetSigKey_t, std::vector<void*>> sWrapperFree;
2817static std::unordered_map<RetSigKey_t, std::unordered_map<PyObject*, void*>>
2819static std::unordered_map<PyObject*, std::pair<void*, RetSigKey_t>>
2821static std::unordered_map<void*, PyObject**> sWrapperReference;
2822
2824 std::string& ret, std::string& sig) {
2825 std::vector<interop::TCppType_t> types;
2826 interop::GetFnTypeSig(fn_type, types);
2827 assert(types.size() >= 1);
2828 ret = interop::GetTypeAsString(types[0]);
2829 sig = "(";
2830 bool f = false;
2831 for (size_t i = 1; i < types.size(); i++) {
2832 if (f)
2833 sig += ", ";
2834 else
2835 f = true;
2836 sig += interop::GetTypeAsString(types[i]);
2838 sig += ")";
2839}
2840
2841static PyObject* WrapperCacheEraser(PyObject*, PyObject* pyref) {
2842 auto ipos = sWrapperWeakRefs.find(pyref);
2843 if (ipos != sWrapperWeakRefs.end()) {
2844 auto key = ipos->second.second;
2845
2846 // disable this callback and store on free list for possible re-use
2847 void* wpraddress = ipos->second.first;
2848 PyObject** oldref = sWrapperReference[wpraddress];
2849 const auto& lookup = sWrapperLookup.find(key);
2850 if (lookup != sWrapperLookup.end())
2851 lookup->second.erase(*oldref);
2852 *oldref = nullptr; // to detect deletions
2853 sWrapperFree[ipos->second.second].push_back(wpraddress);
2854
2855 // clean up and remove weak reference from admin
2856 Py_DECREF(ipos->first);
2857 sWrapperWeakRefs.erase(ipos);
2859
2861}
2862static PyMethodDef gWrapperCacheEraserMethodDef = {
2863 const_cast<char*>("internal_WrapperCacheEraser"),
2864 (PyCFunction)WrapperCacheEraser, METH_O, nullptr};
2865
2866static void* PyFunction_AsCPointer(PyObject* pyobject,
2867 const interop::TCppType_t fn_type,
2868 bool allowCppInstance) {
2869 // Convert a bound C++ function pointer or callable python object to a C-style
2870 // function pointer. The former is direct, the latter involves a JIT-ed
2871 // wrapper.
2872 static PyObject* sWrapperCacheEraser =
2873 PyCFunction_New(&gWrapperCacheEraserMethodDef, nullptr);
2874
2875 using namespace cppjit::cpyrt;
2876
2877 if (CPPOverload_Check(pyobject)) {
2878 CPPOverload* ol = (CPPOverload*)pyobject;
2879 if (!ol->fMethodInfo || ol->fMethodInfo->fMethods.empty())
2880 return nullptr;
2881
2882 // find the overload with matching signature
2883 for (auto& m : ol->fMethodInfo->fMethods) {
2884 if (m->IsSimilarFnType(fn_type)) {
2885 void* fptr = (void*)m->GetFunctionAddress();
2886 if (fptr)
2887 return fptr;
2888 }
2889 }
2890 return nullptr;
2891 }
2892
2893 if (TemplateProxy_Check(pyobject)) {
2894 // get the actual underlying template matching the signature
2895 TemplateProxy* pytmpl = (TemplateProxy*)pyobject;
2896 std::string fullname = pytmpl->fTI->fCppName;
2897 if (pytmpl->fTemplateArgs)
2898 fullname += PyUnicode_AsUTF8(pytmpl->fTemplateArgs);
2899 interop::TCppScope_t scope = ((CPPClass*)pytmpl->fTI->fPyClass)->fCppType;
2900 std::string ret{}, sig{};
2901 GetSignatureFromFnType(fn_type, ret, sig);
2903 scope, fullname, sig.substr(1, sig.size() - 2));
2904 if (cppmeth) {
2905 void* fptr = (void*)interop::GetFunctionAddress(cppmeth, false);
2906 if (fptr)
2907 return fptr;
2908 }
2909 return nullptr;
2910 }
2911
2912 if (PyObject_IsInstance(pyobject, (PyObject*)GetCTypesType(ct_c_funcptr))) {
2913 // ctypes function pointer
2914 void* fptr = *(void**)((cpyrt_tagCDataObject*)pyobject)->b_ptr;
2915 return fptr;
2916 }
2917
2918 if (PyCallable_Check(pyobject) &&
2919 (allowCppInstance || !CPPInstance_Check(pyobject))) {
2920 // generic python callable: create a C++ wrapper function
2921 // Sometimes we don't want to take this branch if the object is a C++
2922 // instance, because C++ doesn't allow converting functor objects to
2923 // function pointers, but only to std::function.
2924 void* wpraddress = nullptr;
2925
2926 std::string ret{}, sig{};
2927 GetSignatureFromFnType(fn_type, ret, sig);
2928 sig = "(void)" == sig ? "()" : sig;
2929
2930 // re-use existing wrapper if possible
2931 auto key = ret + sig;
2932 const auto& lookup = sWrapperLookup.find(key);
2933 if (lookup != sWrapperLookup.end()) {
2934 const auto& existing = lookup->second.find(pyobject);
2935 if (existing != lookup->second.end() &&
2936 *sWrapperReference[existing->second] == pyobject)
2937 wpraddress = existing->second;
2938 }
2939
2940 // check for a pre-existing, unused, wrapper if not found
2941 if (!wpraddress) {
2942 const auto& freewrap = sWrapperFree.find(key);
2943 if (freewrap != sWrapperFree.end() && !freewrap->second.empty()) {
2944 wpraddress = freewrap->second.back();
2945 freewrap->second.pop_back();
2946 *(sWrapperReference[wpraddress]) = pyobject;
2947 sWrapperLookup[key][pyobject] = wpraddress;
2948 PyObject* wref = PyWeakref_NewRef(pyobject, sWrapperCacheEraser);
2949 if (wref)
2950 sWrapperWeakRefs[wref] = std::make_pair(wpraddress, key);
2951 else
2952 PyErr_Clear(); // happens for builtins which don't need this
2953 }
2954 }
2955
2956 // create wrapper if no re-use possible
2957 if (!wpraddress) {
2959 return nullptr;
2960
2961 // extract argument types
2962 const std::vector<std::string>& argtypes =
2964 int nArgs = (int)argtypes.size();
2965
2966 // wrapper name
2967 std::ostringstream wname;
2968 wname << "fptr_wrap" << ++sWrapperCounter;
2969
2970 // build wrapper function code
2971 std::ostringstream code;
2972 code << "namespace __cppjit_internal {\n " << ret << " " << wname.str()
2973 << "(";
2974 for (int i = 0; i < nArgs; ++i) {
2975 code << argtypes[i] << " arg" << i;
2976 if (i != nArgs - 1)
2977 code << ", ";
2978 }
2979 code << ") {\n"
2980 << " cppjit::cpyrt::PythonGILRAII python_gil_raii;\n";
2981
2982 // start function body
2983 Utility::ConstructCallbackPreamble(ret, argtypes, code);
2984
2985 // create a referenceable pointer
2986 PyObject** ref = new PyObject* {pyobject};
2987
2988 // function call itself and cleanup
2989 code << " PyObject** ref = (PyObject**)" << (intptr_t)ref
2990 << ";\n"
2991 " PyObject* pyresult = nullptr;\n"
2992 " if (*ref) pyresult = PyObject_CallFunctionObjArgs(*ref";
2993 for (int i = 0; i < nArgs; ++i)
2994 code << ", pyargs[" << i << "]";
2995 code << ", NULL);\n"
2996 " else PyErr_SetString(PyExc_TypeError, \"callable was "
2997 "deleted\");\n";
2998
2999 // close
3000 Utility::ConstructCallbackReturn(ret, nArgs, code);
3001
3002 // end of namespace
3003 code << "}";
3004
3005 // finally, compile the code
3006 if (!interop::Compile(code.str()))
3007 return nullptr;
3008
3009 // TODO: is there no easier way?
3010 static interop::TCppScope_t scope =
3011 interop::GetScope("__cppjit_internal");
3012 const auto& methods = interop::GetMethodsFromName(scope, wname.str());
3013 wpraddress = interop::GetFunctionAddress(methods[0], false);
3014 sWrapperReference[wpraddress] = ref;
3015
3016 // cache the new wrapper
3017 sWrapperLookup[key][pyobject] = wpraddress;
3018 PyObject* wref = PyWeakref_NewRef(pyobject, sWrapperCacheEraser);
3019 if (wref)
3020 sWrapperWeakRefs[wref] = std::make_pair(wpraddress, key);
3021 else
3022 PyErr_Clear(); // happens for builtins which don't need this
3023 }
3024
3025 // now pass the pointer to the wrapper function (may be null)
3026 return wpraddress;
3027 }
3028
3029 return nullptr;
3030}
3031
3032bool cpyrt::FunctionPointerConverter::SetArg(PyObject* pyobject,
3033 Parameter& para,
3034 CallContext* ctxt) {
3035 // special case: allow nullptr singleton:
3036 if (pyobject == gNullPtrObject || pyobject == gDefaultObject) {
3037 para.fValue.fVoidp = nullptr;
3038 para.fTypeCode = 'p';
3039 return true;
3040 }
3041
3042 // normal case, get a function pointer
3043 void* fptr = PyFunction_AsCPointer(pyobject, fFnType, fAllowCppInstance);
3044 if (fptr) {
3045 SetLifeLine(ctxt->fPyContext, pyobject, (intptr_t)this);
3046 para.fValue.fVoidp = fptr;
3047 para.fTypeCode = 'p';
3048 return true;
3049 }
3050
3051 return false;
3052}
3053
3054PyObject* cpyrt::FunctionPointerConverter::FromMemory(void* address) {
3055 // A function pointer in clang is represented by a Type, not a FunctionDecl
3056 // and it's not possible to get the latter from the former: the backend will
3057 // need to support both. Since that is far in the future, we'll use a
3058 // std::function instead.
3059 if (address) {
3060 std::string ret{}, sig{};
3062 return Utility::FuncPtr2StdFunction(ret, sig, *(void**)address);
3063 }
3064 PyErr_SetString(PyExc_TypeError, "can not convert null function pointer");
3065 return nullptr;
3066}
3067
3068bool cpyrt::FunctionPointerConverter::ToMemory(PyObject* pyobject,
3069 void* address, PyObject* ctxt) {
3070 // special case: allow nullptr singleton:
3071 if (pyobject == gNullPtrObject || pyobject == gDefaultObject) {
3072 *((void**)address) = nullptr;
3073 return true;
3074 }
3075
3076 // normal case, get a function pointer
3077 void* fptr = PyFunction_AsCPointer(pyobject, fFnType, fAllowCppInstance);
3078 if (fptr) {
3079 SetLifeLine(ctxt, pyobject, (intptr_t)address);
3080 *((void**)address) = fptr;
3081 return true;
3082 }
3083
3084 return false;
3085}
3086
3087//- std::function converter --------------------------------------------------
3088bool cpyrt::StdFunctionConverter::SetArg(PyObject* pyobject, Parameter& para,
3089 CallContext* ctxt) {
3090 // prefer normal "object" conversion
3092 if (fConverter->SetArg(pyobject, para, ctxt))
3093 return true;
3094
3095 PyErr_Clear();
3096
3097 // else create a wrapper function
3098 if (this->FunctionPointerConverter::SetArg(pyobject, para, ctxt)) {
3099 // retrieve the wrapper pointer and capture it in a temporary std::function,
3100 // then try normal conversion a second time
3101 PyObject* func =
3102 this->FunctionPointerConverter::FromMemory(&para.fValue.fVoidp);
3103 if (func) {
3104 SetLifeLine(ctxt->fPyContext, func, (intptr_t)this);
3105 bool result = fConverter->SetArg(func, para, ctxt);
3106 if (result)
3107 ctxt->AddTemporary(func);
3108 else
3109 Py_DECREF(func);
3110 return result;
3111 }
3112 }
3113
3114 return false;
3115}
3116
3117PyObject* cpyrt::StdFunctionConverter::FromMemory(void* address) {
3118 return fConverter->FromMemory(address);
3119}
3120
3121bool cpyrt::StdFunctionConverter::ToMemory(PyObject* value, void* address,
3122 PyObject* ctxt) {
3123 // if the value is not an std::function<> but a generic Python callable, the
3124 // conversion is done through the assignment, which may involve a temporary
3125 if (address)
3126 SetLifeLine(ctxt, value, (intptr_t)address);
3127 return fConverter->ToMemory(value, address, ctxt);
3128}
3129
3130//- smart pointer converters -------------------------------------------------
3131bool cpyrt::SmartPtrConverter::SetArg(PyObject* pyobject, Parameter& para,
3132 CallContext* ctxt) {
3133 char typeCode = fIsRef ? 'p' : 'V';
3134
3135 if (!CPPInstance_Check(pyobject)) {
3136 // TODO: not sure how this is correct for pass-by-ref nor does it help with
3137 // implicit conversions for pass-by-value
3138 if (fIsRef && GetAddressSpecialCase(pyobject, para.fValue.fVoidp)) {
3139 para.fTypeCode = typeCode; // allow special cases such as nullptr
3140 return true;
3141 }
3142
3143 return false;
3144 }
3145
3146 CPPInstance* pyobj = (CPPInstance*)pyobject;
3147 interop::TCppScope_t oisa = pyobj->ObjectIsA();
3148
3149 // for the case where we have a 'hidden' smart pointer:
3150 if (interop::TCppScope_t tsmart = pyobj->GetSmartIsA()) {
3151 if (interop::IsSubclass(tsmart, fSmartPtrType)) {
3152 // depending on memory policy, some objects need releasing when passed
3153 // into functions
3155 ((CPPInstance*)pyobject)->CppOwns();
3156
3157 // calculate offset between formal and actual arguments
3158 para.fValue.fVoidp = pyobj->GetSmartObject();
3159 if (tsmart != fSmartPtrType) {
3161 tsmart, fSmartPtrType, para.fValue.fVoidp, 1 /* up-cast */);
3162 }
3163
3164 // set pointer (may be null) and declare success
3165 para.fTypeCode = typeCode;
3166 return true;
3167 }
3168 }
3169
3170 // for the case where we have an 'exposed' smart pointer:
3171 if (!pyobj->IsSmart() && interop::IsSubclass(oisa, fSmartPtrType)) {
3172 // calculate offset between formal and actual arguments
3173 para.fValue.fVoidp = pyobj->GetObject();
3174 if (oisa != fSmartPtrType) {
3176 oisa, fSmartPtrType, para.fValue.fVoidp, 1 /* up-cast */);
3177 }
3178
3179 // set pointer (may be null) and declare success
3180 para.fTypeCode = typeCode;
3181 return true;
3182 }
3183
3184 // for the case where we have an ordinary object to convert
3185 if (AllowImplicitSmartPtrConversion(ctxt) && !pyobj->IsSmart() &&
3187 // create the relevant smart pointer and make the pyobject "smart"
3189 fSmartPtrType, pyobject, para, ctxt, false);
3190 if (!CPPInstance_Check(pysmart)) {
3191 Py_XDECREF(pysmart);
3192 return false;
3193 }
3194
3195 // copy internals from the fresh smart object to the original, making it
3196 // smart
3197 pyobj->GetObjectRaw() = pysmart->GetSmartObject();
3198 pyobj->SetSmart(
3199 CreateScopeProxy(fSmartPtrType)); //(PyObject*)Py_TYPE(pysmart));
3200 pyobj->PythonOwns();
3201 pysmart->CppOwns();
3202 Py_DECREF(pysmart);
3203
3204 return true;
3205 }
3206
3207 // final option, try mapping pointer types held (TODO: do not allow for
3208 // non-const ref) Match on the declared underlying type, not the (possibly
3209 // downcast) dereferenced object: a unique_ptr<Base> holding a Derived is
3210 // still a unique_ptr<Base> and must not pass where a unique_ptr<Derived> is
3211 // expected.
3212 if (pyobj->IsSmart() &&
3214 para.fValue.fVoidp = ((CPPInstance*)pyobject)->GetSmartObject();
3215 para.fTypeCode = 'V';
3216 return true;
3217 }
3218
3219 return false;
3220}
3221
3222PyObject* cpyrt::SmartPtrConverter::FromMemory(void* address) {
3223 if (!address || !fSmartPtrType)
3224 return nullptr;
3225
3226 return BindCppObjectNoCast(address, fSmartPtrType);
3227}
3228
3229bool cpyrt::SmartPtrConverter::ToMemory(PyObject* value, void* address,
3230 PyObject*) {
3231 // assign value to C++ instance living at <address> through assignment
3232 // operator (this is similar to InstanceConverter::ToMemory, but prevents
3233 // wrapping the smart ptr)
3234 PyObject* pyobj =
3236 PyObject* result =
3237 PyObject_CallMethodOneArg(pyobj, PyStrings::gAssign, value);
3238 Py_DECREF(pyobj);
3239
3240 if (result) {
3241 Py_DECREF(result);
3242 return true;
3243 }
3244 return false;
3245}
3246
3247//----------------------------------------------------------------------------
3248namespace {
3249
3250// clang libcpp and gcc use the same structure (ptr, size)
3251#if defined(_LIBCPP_INITIALIZER_LIST) || defined(__GNUC__)
3252struct faux_initlist {
3253 typedef size_t size_type;
3254 typedef void* iterator;
3255 iterator _M_array;
3256 size_type _M_len;
3257};
3258#elif defined(_MSC_VER)
3259struct faux_initlist {
3260 typedef char* iterator;
3261 iterator _M_array; // ie. _First;
3262 iterator _Last;
3263};
3264#else
3265#define NO_KNOWN_INITIALIZER_LIST 1
3266#endif
3267
3268} // unnamed namespace
3269
3270cpyrt::InitializerListConverter::InitializerListConverter(
3271 interop::TCppScope_t klass, std::string const& value_type)
3272
3273 : InstanceConverter{klass}, fValueTypeName{value_type},
3274 fValueType{interop::GetScope(value_type)},
3275 fValueSize{interop::SizeOfType(interop::GetType(value_type, true))} {}
3276
3277cpyrt::InitializerListConverter::~InitializerListConverter() {
3278 for (Converter* converter : fConverters) {
3279 if (converter && converter->HasState())
3280 delete converter;
3281 }
3282 if (fBuffer)
3283 Clear();
3284}
3285
3286void cpyrt::InitializerListConverter::Clear() {
3287 if (fValueType) {
3288 faux_initlist* fake = (faux_initlist*)fBuffer;
3289#if defined(_LIBCPP_INITIALIZER_LIST) || defined(__GNUC__)
3290 for (faux_initlist::size_type i = 0; i < fake->_M_len; ++i) {
3291#elif defined(_MSC_VER)
3292 for (size_t i = 0; (fake->_M_array + i * fValueSize) != fake->_Last; ++i) {
3293#endif
3294 void* memloc = (char*)fake->_M_array + i * fValueSize;
3297 }
3298
3299 free(fBuffer);
3300 fBuffer = nullptr;
3301}
3302
3303bool cpyrt::InitializerListConverter::SetArg(PyObject* pyobject,
3304 Parameter& para,
3305 CallContext* ctxt) {
3306#ifdef NO_KNOWN_INITIALIZER_LIST
3307 return false;
3308#else
3309 if (fBuffer)
3310 Clear();
3311
3312 // convert the given argument to an initializer list temporary; this is purely
3313 // meant to be a syntactic thing, so only _python_ sequences are allowed;
3314 // bound C++ proxies (likely explicitly created std::initializer_list, go
3315 // through an instance converter
3316 if (!PySequence_Check(pyobject) || PyUnicode_Check(pyobject) ||
3317 PyBytes_Check(pyobject))
3318 return false;
3319
3320 if (CPPInstance_Check(pyobject))
3321 return this->InstanceConverter::SetArg(pyobject, para, ctxt);
3322
3323 void* buf = nullptr;
3324 Py_ssize_t buflen =
3325 Utility::GetBuffer(pyobject, '*', (int)fValueSize, buf, true);
3326 faux_initlist* fake = nullptr;
3327 size_t entries = 0;
3328 if (buf && buflen) {
3329 // dealing with an array here, pass on whole-sale
3330 fake = (faux_initlist*)malloc(sizeof(faux_initlist));
3331 fBuffer = (void*)fake;
3332 fake->_M_array = (faux_initlist::iterator)buf;
3333#if defined(_LIBCPP_INITIALIZER_LIST) || defined(__GNUC__)
3334 fake->_M_len = (faux_initlist::size_type)buflen;
3335#elif defined(_MSC_VER)
3336 fake->_Last = fake->_M_array + buflen * fValueSize;
3337#endif
3338 } else if (fValueSize) {
3339 // Remove any errors set by GetBuffer(); note that if the argument was an
3340 // array that failed to extract because of a type mismatch, the following
3341 // will perform a (rather inefficient) copy. No warning is issued b/c e.g.
3342 // numpy doesn't do so either.
3343 PyErr_Clear();
3344
3345 // Can only construct empty lists, so use a fake initializer list. For that
3346 // we need to construct default objects. Fail early if that cannot work.
3348 PyErr_SetString(
3349 PyExc_TypeError,
3350 "default constructor needed for initializer list of objects");
3351 return false;
3352 }
3353
3354 size_t len = (size_t)PySequence_Size(pyobject);
3355 fake = (faux_initlist*)malloc(sizeof(faux_initlist) + fValueSize * len);
3356 fBuffer = (void*)fake;
3357 fake->_M_array =
3358 (faux_initlist::iterator)((char*)fake + sizeof(faux_initlist));
3359#if defined(_LIBCPP_INITIALIZER_LIST) || defined(__GNUC__)
3360 fake->_M_len = (faux_initlist::size_type)len;
3361 for (faux_initlist::size_type i = 0; i < fake->_M_len; ++i) {
3362#elif defined(_MSC_VER)
3363 fake->_Last = fake->_M_array + len * fValueSize;
3364 for (size_t i = 0; (fake->_M_array + i * fValueSize) != fake->_Last; ++i) {
3365#endif
3366 PyObject* item = PySequence_GetItem(pyobject, i);
3367 bool convert_ok = false;
3368 if (item) {
3369 if (i >= fConverters.size())
3371 Converter* converter = fConverters[i];
3372 if (!converter) {
3373 if (CPPInstance_Check(item)) {
3374 // by convention, use byte copy
3375 memcpy((char*)fake->_M_array + i * fValueSize,
3376 ((CPPInstance*)item)->GetObject(), fValueSize);
3377 convert_ok = true;
3378 }
3379 } else {
3380 void* memloc = (char*)fake->_M_array + i * fValueSize;
3381 if (fValueType) {
3382 // we need to construct a default object for the constructor to
3383 // assign into; this is clunky, but the use of a copy constructor
3384 // isn't much better as the Python object need not be a C++ object
3385 memloc = (void*)interop::Construct(fValueType, memloc).data;
3386 // We checked above that we are able to construct default objects of
3387 // fValueType.
3388 assert(memloc && ("failed to default construct object for type " +
3390 .c_str());
3391 entries += 1;
3392 }
3393 if (memloc)
3394 convert_ok = converter->ToMemory(item, memloc);
3395 }
3396
3397 Py_DECREF(item);
3398 } else
3399 PyErr_Format(PyExc_TypeError, "failed to get item %d from sequence",
3400 (int)i);
3401
3402 if (!convert_ok) {
3403#if defined(_LIBCPP_INITIALIZER_LIST) || defined(__GNUC__)
3404 fake->_M_len = (faux_initlist::size_type)entries;
3405#elif defined(_MSC_VER)
3406 fake->_Last = fake->_M_array + entries * fValueSize;
3407#endif
3408 Clear();
3409 return false;
3410 }
3411 }
3412 }
3413
3414 if (!fake) // no buffer and value size indeterminate
3415 return false;
3416
3417 para.fValue.fVoidp = (void*)fake;
3418 para.fTypeCode = 'V'; // means ptr that backend has to free after call
3419 return true;
3420#endif
3421}
3423namespace cppjit::cpyrt {
3425// raising converter to take out overloads
3426class NotImplementedConverter : public Converter {
3427public:
3428 NotImplementedConverter(PyObject* errorType, std::string const& message)
3429 : fErrorType{errorType}, fMessage{message} {}
3430 bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override;
3431
3432private:
3433 PyObject* fErrorType;
3434 std::string fMessage;
3435};
3437} // namespace cppjit::cpyrt
3438
3439//----------------------------------------------------------------------------
3441 CallContext*) {
3442 // raise a NotImplemented exception to take a method out of overload
3443 // resolution
3444 PyErr_SetString(fErrorType, fMessage.c_str());
3445 return false;
3447
3448//- helper to refactor some code from CreateConverter ------------------------
3449static inline cppjit::cpyrt::Converter*
3450selectInstanceCnv(interop::TCppScope_t klass, const std::string& cpd,
3451 cpyrt::cdims_t dims, bool isConst, bool control) {
3452 using namespace cppjit::cpyrt;
3453 Converter* result = nullptr;
3454
3455 if (cpd == "**" || cpd == "*[]" || cpd == "&*")
3456 result = new InstancePtrPtrConverter<false>(klass, control);
3457 else if (cpd == "*&")
3458 result = new InstancePtrPtrConverter<true>(klass, control);
3459 else if (cpd == "*" && dims.ndim() == UNKNOWN_SIZE) {
3460 if (isConst)
3461 result = new InstancePtrConverter<true>(klass, control);
3462 else
3463 result = new InstancePtrConverter<false>(klass, control);
3464 } else if (cpd == "&")
3465 result = new InstanceRefConverter(klass, isConst);
3466 else if (cpd == "&&")
3467 result = new InstanceMoveConverter(klass);
3468 else if (cpd == "[]" || dims)
3469 result = new InstanceArrayConverter(klass, dims, false);
3470 else if (cpd == "") // by value
3471 result = new InstanceConverter(klass, true);
3472
3473 return result;
3474}
3476//- factories ----------------------------------------------------------------
3479cppjit::cpyrt::CreateConverter(const std::string& fullType, cdims_t dims) {
3480 // The matching of the fulltype to a converter factory goes through up to five
3481 // levels:
3482 // 1) full, exact match
3483 // 2) match of decorated, unqualified type
3484 // 3) accept const ref as by value
3485 // 4) accept ref as pointer
3486 // 5) generalized cases (covers basically all C++ classes)
3487 //
3488 // If all fails, void is used, which will generate a run-time warning when
3489 // used.
3490
3491 // an exactly matching converter is best
3492 ConvFactories_t::iterator h = gConvFactories.find(fullType);
3493 if (h != gConvFactories.end()) {
3494 return (h->second)(dims);
3495 }
3496
3497 // resolve typedefs etc. (the type itself is kept for function pointers)
3499 interop::GetType(fullType, /*enable_slow_lookup=*/true);
3500 const std::string resolvedType =
3502
3503 // a full, qualified matching converter is preferred
3504 if (resolvedType != fullType) {
3505 h = gConvFactories.find(resolvedType);
3506 if (h != gConvFactories.end())
3507 return (h->second)(dims);
3508 }
3509
3510 //-- nothing? ok, collect information about the type and possible
3511 // qualifiers/decorators
3512 bool isConst = strncmp(resolvedType.c_str(), "const", 5) == 0;
3513 const std::string& cpd = TypeManip::compound(resolvedType);
3514 std::string realType = TypeManip::clean_type(resolvedType, false, true);
3515
3516 // accept unqualified type (as python does not know about qualifiers)
3517 h = gConvFactories.find((isConst ? "const " : "") + realType + cpd);
3518 if (h != gConvFactories.end())
3519 return (h->second)(dims);
3520
3521 // mutable pointer references (T*&) are incompatible with Python's object
3522 // model; a string that resolves to no type keeps the ban
3523 if (!isConst && cpd == "*&") {
3524 interop::TCppType_t refType =
3525 interop::GetType(resolvedType, /* enable_slow_lookup */ true);
3526 if (!refType || interop::IsMutablePtrRefType(refType)) {
3527 return new NotImplementedConverter{
3528 PyExc_TypeError,
3529 "argument type '" + resolvedType +
3530 "' is not supported: non-const references to pointers (T*&) "
3531 "allow a function to replace the pointer itself. Python cannot "
3532 "represent this safely. Consider changing the"
3533 " C++ API to return the new pointer or use a wrapper"};
3534 }
3535 }
3536
3537 // drop const, as that is mostly meaningless to python (with the exception
3538 // of c-strings, but those are specialized in the converter map)
3539 if (isConst) {
3540 h = gConvFactories.find(realType + cpd);
3541 if (h != gConvFactories.end())
3542 return (h->second)(dims);
3543 }
3544
3545 //-- still nothing? try pointer instead of array (for builtins)
3546 if (cpd.compare(0, 3, "*[]") == 0) {
3547 // special case, array of pointers
3548 h = gConvFactories.find(realType + " ptr");
3549 if (h != gConvFactories.end()) {
3550 // upstream treats the pointer type as the array element type, but that
3551 // pointer is treated as a low-level view as well, unless it's a
3552 // void*/char* so adjust the dims
3553 if (realType != "void" && realType != "char") {
3554 dim_t newdim = dims.ndim() == UNKNOWN_SIZE ? 2 : dims.ndim() + 1;
3555 dims_t newdims = dims_t(newdim);
3556 // TODO: sometimes the array size is known and can thus be verified;
3557 // however, currently the meta layer does not provide this information
3558 newdims[0] = dims ? dims[0] : UNKNOWN_SIZE; // the array
3559 newdims[1] = UNKNOWN_SIZE; // the pointer
3560 if (2 < newdim) {
3561 for (int i = 2; i < (newdim - 1); ++i)
3562 newdims[i] = dims[i - 1];
3563 }
3564
3565 return (h->second)(newdims);
3566 }
3567 return (h->second)(dims);
3568 }
3569
3570 } else if (!cpd.empty() && (std::string::size_type)std::count(
3571 cpd.begin(), cpd.end(), '*') == cpd.size()) {
3572 // simple array; set or resize as necessary
3573 h = gConvFactories.find(realType + " ptr");
3574 if (h != gConvFactories.end())
3575 return (h->second)((!dims && 1 < cpd.size()) ? dims_t(cpd.size()) : dims);
3576
3577 } else if (2 <= cpd.size() &&
3578 (std::string::size_type)std::count(cpd.begin(), cpd.end(), '[') ==
3579 cpd.size() / 2) {
3580 // fixed array, dims will have size if available
3581 h = gConvFactories.find(realType + " ptr");
3582 if (h != gConvFactories.end())
3583 return (h->second)(dims);
3584 }
3585
3586 //-- special case: initializer list
3587 if (realType.compare(0, 21, "std::initializer_list") == 0) {
3588 // get the type of the list and create a converter (TODO: get hold of
3589 // value_type?)
3590 auto pos = realType.find('<');
3591 std::string value_type =
3592 realType.substr(pos + 1, realType.size() - pos - 2);
3593 return new InitializerListConverter(interop::GetScope(realType),
3594 value_type);
3595 }
3596
3597 //-- still nothing? use a generalized converter
3598 bool control = cpd == "&" || isConst;
3599
3600 //-- special case: std::function
3601 auto pos = resolvedType.find("std::function<");
3602 if (pos == 0 /* std:: */ || pos == 6 /* const std:: */) {
3603
3604 // get actual converter for normal passing
3605 Converter* cnv = selectInstanceCnv(interop::GetScope(realType), cpd, dims,
3606 isConst, control);
3607
3608 if (cnv) {
3609 // get the type of the underlying (TODO: use target_type?)
3610 auto pos1 = resolvedType.find("(", pos + 14);
3611 auto pos2 = resolvedType.rfind(")");
3612 if (pos1 != std::string::npos && pos2 != std::string::npos) {
3613 auto sz1 = pos1 - pos - 14;
3614 if (resolvedType[pos + 14 + sz1 - 1] == ' ')
3615 sz1 -= 1;
3616
3617 return new StdFunctionConverter(
3618 cnv, interop::GetType(fullType), resolvedType.substr(pos + 14, sz1),
3619 resolvedType.substr(pos1, pos2 - pos1 + 1));
3620 } else if (cnv->HasState())
3621 delete cnv;
3622 }
3623 }
3624
3625#if __cplusplus >= 202002L
3626 //-- special case: std::span
3627 pos = resolvedType.find("span<");
3628 if (pos == 0 /* no std:: */ || pos == 5 /* with std:: */ ||
3629 pos == 6 /* const no std:: */ || pos == 11 /* const with std:: */) {
3630
3631 // std::span<T>::value_type == std::remove_cv_t<T>: look it up
3632 interop::TCppScope_t span_scope = interop::GetScope(realType);
3634 interop::GetTypeFromScope(interop::GetNamed("value_type", span_scope)));
3635 if (span_scope && vtype) {
3636 std::string value_type = interop::GetTypeAsString(vtype);
3637 return new StdSpanConverter{value_type, span_scope};
3638 }
3639 }
3640#endif
3641
3642 // converters for known C++ classes and default (void*)
3643 Converter* result = nullptr;
3644 if (interop::TCppScope_t klass = interop::GetFullScope(realType)) {
3646 if (interop::GetSmartPtrInfo(klass, &raw, nullptr)) {
3647 if (cpd == "") {
3648 result = new SmartPtrConverter(klass, raw, control);
3649 } else if (cpd == "&") {
3650 result = new SmartPtrConverter(klass, raw);
3651 } else if (cpd == "*" && dims.ndim() == UNKNOWN_SIZE) {
3652 result = new SmartPtrConverter(klass, raw, control, true);
3653 }
3654 }
3655
3656 if (!result) {
3657 // Cling WORKAROUND -- special case for STL iterators
3658 if (Utility::IsSTLIterator(realType)) {
3659 static STLIteratorConverter c;
3660 result = &c;
3661 } else
3662 // -- Cling WORKAROUND
3663 result = selectInstanceCnv(klass, cpd, dims, isConst, control);
3664 }
3665 } else if (interop::IsFunctionType(type)) {
3666 // this is a function pointer or reference
3667 result = new FunctionPointerConverter(type);
3668 }
3669 const std::string failure_msg("Failed to convert type: " + fullType +
3670 "; resolved: " + resolvedType +
3671 "; real: " + realType + "; cpd: " + cpd);
3672
3673 if (!result && cpd == "&&") {
3674 // for builtin, can use const-ref for r-ref
3675 h = gConvFactories.find("const " + realType + "&");
3676 if (h != gConvFactories.end())
3677 return (h->second)(dims);
3678 // else, unhandled moves
3679 result = new NotImplementedConverter{PyExc_NotImplementedError,
3680 "this method cannot (yet) be called"};
3681 }
3682
3683 if (!result && h != gConvFactories.end())
3684 // converter factory available, use it to create converter
3685 result = (h->second)(dims);
3686 else if (!result) {
3687 // default to something reasonable, assuming "user knows best"
3688 if (cpd.size() == 2 && cpd != "&&") // "**", "*[]", "*&"
3689 result = new VoidPtrPtrConverter(dims.ndim(), failure_msg);
3690 else if (!cpd.empty())
3691 result = new VoidArrayConverter(/* keepControl= */ true,
3692 failure_msg); // "user knows best"
3693 else
3694 // fails on use
3696 PyExc_NotImplementedError, "this method cannot (yet) be called"};
3697 }
3698
3699 return result;
3701
3705 // The matching of the fulltype to a converter factory goes through up to five
3706 // levels:
3707 // 1) full, exact match
3708 // 2) match of decorated, unqualified type
3709 // 3) accept const ref as by value
3710 // 4) accept ref as pointer
3711 // 5) generalized cases (covers basically all C++ classes)
3712 //
3713 // If all fails, void is used, which will generate a run-time warning when
3714 // used.
3715
3716 // an exactly matching converter is best
3717 std::string fullType = interop::GetTypeAsString(type);
3718 ConvFactories_t::iterator h = gConvFactories.find(fullType);
3719 if (h != gConvFactories.end()) {
3720 return (h->second)(dims);
3721 }
3722
3723 // resolve typedefs etc.
3725 const std::string& resolvedTypeStr = interop::GetTypeAsString(resolvedType);
3726
3727 // a by-value enum parameter exact-matches instances of the same enum; the
3728 // resolved type is its underlying integer, which carries the conversion
3729 if (interop::IsEnumType(type) && fullType != "std::byte") {
3730 h = gConvFactories.find(resolvedTypeStr);
3731 // the underlying integer may be spelled through a typedef (e.g.
3732 // `enum E : std::int32_t`), so fall back to its canonical spelling
3733 if (h == gConvFactories.end())
3734 h = gConvFactories.find(
3736 if (h != gConvFactories.end())
3737 return new EnumConverter(
3738 (h->second)(dims),
3740 }
3741
3742 // a full, qualified matching converter is preferred
3743 if (resolvedTypeStr != fullType) {
3744 h = gConvFactories.find(resolvedTypeStr);
3745 if (h != gConvFactories.end()) {
3746 return (h->second)(dims);
3747 }
3748 }
3749
3750 //-- nothing? ok, collect information about the type and possible
3751 // qualifiers/decorators
3752 bool isConst = strncmp(resolvedTypeStr.c_str(), "const", 5) == 0;
3753 const std::string& cpd = TypeManip::compound(resolvedTypeStr);
3754 interop::TCppType_t realType =
3756 std::string realTypeStr = interop::GetTypeAsString(realType);
3757 std::string realUnresolvedTypeStr =
3758 TypeManip::clean_type(fullType, false, true);
3759
3760 // accept unqualified type (as python does not know about qualifiers)
3761 h = gConvFactories.find((isConst ? "const " : "") + realTypeStr + cpd);
3762 if (h != gConvFactories.end())
3763 return (h->second)(dims);
3764
3765 // mutable pointer references (T*&) are incompatible with Python's object
3766 // model
3767 if (!isConst && cpd == "*&" && interop::IsMutablePtrRefType(resolvedType)) {
3768 return new NotImplementedConverter{
3769 PyExc_TypeError,
3770 "argument type '" + resolvedTypeStr +
3771 "' is not supported: non-const references to pointers (T*&) allow a"
3772 " function to replace the pointer itself. Python cannot represent "
3773 "this safely. Consider changing the"
3774 " C++ API to return the new pointer or use a wrapper"};
3775 }
3776
3777 // drop const, as that is mostly meaningless to python (with the exception
3778 // of c-strings, but those are specialized in the converter map)
3779 if (isConst) {
3780 h = gConvFactories.find(realTypeStr + cpd);
3781 if (h != gConvFactories.end()) {
3782 return (h->second)(dims);
3783 }
3784 }
3785
3786 //-- still nothing? try pointer instead of array (for builtins)
3787 if (cpd.compare(0, 3, "*[]") == 0) {
3788 // special case, array of pointers
3789 h = gConvFactories.find(realTypeStr + " ptr");
3790 if (h != gConvFactories.end()) {
3791 // upstream treats the pointer type as the array element type, but that
3792 // pointer is treated as a low-level view as well, unless it's a
3793 // void*/char* so adjust the dims
3794 if (realTypeStr != "void" && realTypeStr != "char") {
3795 dim_t newdim = dims.ndim() == UNKNOWN_SIZE ? 2 : dims.ndim() + 1;
3796 dims_t newdims = dims_t(newdim);
3797 // TODO: sometimes the array size is known and can thus be verified;
3798 // however, currently the meta layer does not provide this information
3799 newdims[0] = dims ? dims[0] : UNKNOWN_SIZE; // the array
3800 newdims[1] = UNKNOWN_SIZE; // the pointer
3801 if (2 < newdim) {
3802 for (int i = 2; i < (newdim - 1); ++i)
3803 newdims[i] = dims[i - 1];
3804 }
3805
3806 return (h->second)(newdims);
3807 }
3808 return (h->second)(dims);
3809 }
3810
3811 } else if (!cpd.empty() && (std::string::size_type)std::count(
3812 cpd.begin(), cpd.end(), '*') == cpd.size()) {
3813 // simple array; set or resize as necessary
3814 h = gConvFactories.find(realTypeStr + " ptr");
3815 if (h != gConvFactories.end())
3816 return (h->second)((!dims && 1 < cpd.size()) ? dims_t(cpd.size()) : dims);
3817
3818 } else if (2 <= cpd.size() &&
3819 (std::string::size_type)std::count(cpd.begin(), cpd.end(), '[') ==
3820 cpd.size() / 2) {
3821 // fixed array, dims will have size if available
3822 h = gConvFactories.find(realTypeStr + " ptr");
3823 if (h != gConvFactories.end())
3824 return (h->second)(dims);
3825 }
3826
3827 //-- special case: initializer list
3828 if (realTypeStr.compare(0, 21, "std::initializer_list") == 0) {
3829 // get the type of the list and create a converter (TODO: get hold of
3830 // value_type?)
3831 auto pos = realTypeStr.find('<');
3832 std::string value_type =
3833 realTypeStr.substr(pos + 1, realTypeStr.size() - pos - 2);
3834 Converter* cnv = nullptr;
3835 bool use_byte_cnv = false;
3836 if (cpd == "" && interop::GetScope(value_type)) {
3837 // initializer list of object values does not work as the target is raw
3838 // memory; simply use byte copies
3839
3840 // by convention, leave cnv as nullptr
3841 use_byte_cnv = true;
3842 } else
3843 cnv = CreateConverter(value_type);
3844 if (cnv || use_byte_cnv)
3845 return new InitializerListConverter(interop::GetScopeFromType(realType),
3846 value_type);
3847 }
3848
3849 //-- still nothing? use a generalized converter
3850 bool control = cpd == "&" || isConst;
3851
3852 //-- special case: std::function
3853 auto pos = resolvedTypeStr.find("std::function<");
3854 if (pos == 0 /* std:: */ || pos == 6 /* const std:: */) {
3855
3856 // get actual converter for normal passing
3858 dims, isConst, control);
3859
3860 if (cnv) {
3861 // get the type of the underlying (TODO: use target_type?)
3862 auto pos1 = resolvedTypeStr.find("(", pos + 14);
3863 auto pos2 = resolvedTypeStr.rfind(")");
3864 if (pos1 != std::string::npos && pos2 != std::string::npos) {
3865 auto sz1 = pos1 - pos - 14;
3866 if (resolvedTypeStr[pos + 14 + sz1 - 1] == ' ')
3867 sz1 -= 1;
3868
3869 const std::string& argsStr =
3870 resolvedTypeStr.substr(pos1, pos2 - pos1 + 1).c_str();
3871 return new StdFunctionConverter(cnv, resolvedType,
3872 resolvedTypeStr.substr(pos + 14, sz1),
3873 argsStr == "(void)" ? "()" : argsStr);
3874 } else if (cnv->HasState())
3875 delete cnv;
3876 }
3877 }
3878
3879#if __cplusplus >= 202002L
3880 //-- special case: std::span
3881 pos = resolvedTypeStr.find("span<");
3882 if (pos == 0 /* no std:: */ || pos == 5 /* with std:: */ ||
3883 pos == 6 /* const no std:: */ || pos == 11 /* const with std:: */) {
3884
3885 // std::span<T>::value_type == std::remove_cv_t<T>: look it up
3886 interop::TCppScope_t span_scope = interop::GetScopeFromType(realType);
3888 interop::GetTypeFromScope(interop::GetNamed("value_type", span_scope)));
3889 if (span_scope && vtype) {
3890 std::string value_type = interop::GetTypeAsString(vtype);
3891 return new StdSpanConverter{value_type, span_scope};
3892 }
3893 }
3894#endif
3895
3896 // converters for known C++ classes and default (void*)
3897 Converter* result = nullptr;
3899 if (interop::IsFunctionType(resolvedType)) {
3900 // this is a function function pointer or reference
3901 result = new FunctionPointerConverter(resolvedType);
3902 } else if ((realTypeStr != "std::byte") &&
3903 (klass || (klass = interop::GetFullScope(realTypeStr)))) {
3904 // std::byte is a special enum class used to access raw memory
3906 if (interop::GetSmartPtrInfo(klass, &raw, nullptr)) {
3907 if (cpd == "") {
3908 result = new SmartPtrConverter(klass, raw, control);
3909 } else if (cpd == "&") {
3910 result = new SmartPtrConverter(klass, raw);
3911 } else if (cpd == "*" && dims.ndim() == UNKNOWN_SIZE) {
3912 result = new SmartPtrConverter(klass, raw, control, true);
3913 }
3914 }
3915
3916 if (!result) {
3917 // Cling WORKAROUND -- special case for STL iterators
3918 if (realTypeStr.rfind("__gnu_cxx::__normal_iterator", 0) /* vector */ == 0
3919#ifdef __APPLE__
3920 || realTypeStr.rfind("__wrap_iter", 0) == 0
3921#endif
3922 // TODO: Windows?
3923 ) {
3924 static STLIteratorConverter c;
3925 result = &c;
3926 } else if (realTypeStr != "int8_t" and realTypeStr != "uint8_t") {
3927 // -- Cling WORKAROUND
3928 result = selectInstanceCnv(klass, cpd, dims, isConst, control);
3929 }
3930 }
3931 }
3932 const std::string failure_msg(
3933 "Failed to convert type: " + fullType + "; resolved: " + resolvedTypeStr +
3934 "; real: " + realTypeStr +
3935 "; realUnresolvedType: " + realUnresolvedTypeStr + "; cpd: " + cpd);
3936
3937 if (!result && cpd == "&&") {
3938 // for builtin, can use const-ref for r-ref
3939 h = gConvFactories.find("const " + realTypeStr + "&");
3940 if (h != gConvFactories.end())
3941 return (h->second)(dims);
3942 h = gConvFactories.find("const " + realUnresolvedTypeStr + "&");
3943 if (h != gConvFactories.end())
3944 return (h->second)(dims);
3945 // else, unhandled moves
3946 result = new NotImplementedConverter{PyExc_NotImplementedError,
3947 "this method cannot (yet) be called"};
3948 }
3949
3950 if (!result && h != gConvFactories.end()) {
3951 // converter factory available, use it to create converter
3952 result = (h->second)(dims);
3953 } else if (!result) {
3954 // default to something reasonable, assuming "user knows best"
3955 if (cpd.size() == 2 && cpd != "&&") { // "**", "*[]", "*&"
3956 result = new VoidPtrPtrConverter(dims.ndim(), failure_msg);
3957 } else if (!cpd.empty()) {
3958 result = new VoidArrayConverter(/* keepControl= */ true,
3959 failure_msg); // "user knows best"
3960 } else {
3961 // fails on use
3963 PyExc_NotImplementedError, "this method cannot (yet) be called"};
3964 }
3965 }
3966
3967 return result;
3969
3970//----------------------------------------------------------------------------
3973 if (p && p->HasState())
3974 delete p; // state-less converters are always shared
3976
3977//----------------------------------------------------------------------------
3979bool cpyrt::RegisterConverter(const std::string& name, cf_t fac) {
3980 // register a custom converter
3981 auto f = gConvFactories.find(name);
3982 if (f != gConvFactories.end())
3983 return false;
3984
3985 gConvFactories[name] = fac;
3986 return true;
3988
3989//----------------------------------------------------------------------------
3991bool cpyrt::UnregisterConverter(const std::string& name) {
3992 // remove a custom converter
3993 auto f = gConvFactories.find(name);
3994 if (f != gConvFactories.end()) {
3995 gConvFactories.erase(f);
3996 return true;
3997 }
3998 return false;
3999}
4000
4001//----------------------------------------------------------------------------
4002namespace {
4003
4004using namespace cppjit::cpyrt;
4005
4006inline static std::string::size_type dims2stringsz(cdims_t d) {
4007 return (d && d.ndim() != UNKNOWN_SIZE) ? d[0] : std::string::npos;
4009
4010#define STRINGVIEW "std::basic_string_view<char>"
4011#define WSTRING1 "std::basic_string<wchar_t>"
4012#define WSTRING2 \
4013 "std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_" \
4014 "t>>"
4016//-- aliasing special case: C complex (is binary compatible with C++
4017// std::complex)
4018#ifndef _WIN32
4019#define CCOMPLEX_D "_Complex double"
4020#define CCOMPLEX_F "_Complex float"
4021#else
4022#define CCOMPLEX_D "_C_double_complex"
4023#define CCOMPLEX_F "_C_float_complex"
4024#endif
4025
4026static struct InitConvFactories_t {
4027public:
4028 InitConvFactories_t() {
4029 // load all converter factories in the global map 'gConvFactories'
4031
4032 // factories for built-ins
4033 gf["bool"] = (cf_t) + [](cdims_t) {
4034 static BoolConverter c{};
4035 return &c;
4036 };
4037 gf["const bool&"] = (cf_t) + [](cdims_t) {
4038 static ConstBoolRefConverter c{};
4039 return &c;
4040 };
4041 gf["bool&"] = (cf_t) + [](cdims_t) {
4042 static BoolRefConverter c{};
4043 return &c;
4044 };
4045 gf["char"] = (cf_t) + [](cdims_t) {
4046 static CharConverter c{};
4047 return &c;
4048 };
4049 gf["const char&"] = (cf_t) + [](cdims_t) {
4050 static ConstCharRefConverter c{};
4051 return &c;
4052 };
4053 gf["char&"] = (cf_t) + [](cdims_t) {
4054 static CharRefConverter c{};
4055 return &c;
4056 };
4057 gf["signed char&"] = (cf_t) + [](cdims_t) {
4058 static SCharRefConverter c{};
4059 return &c;
4060 };
4061 gf["unsigned char"] = (cf_t) + [](cdims_t) {
4062 static UCharConverter c{};
4063 return &c;
4064 };
4065 gf["const unsigned char&"] = (cf_t) + [](cdims_t) {
4066 static ConstUCharRefConverter c{};
4067 return &c;
4068 };
4069 gf["unsigned char&"] = (cf_t) + [](cdims_t) {
4070 static UCharRefConverter c{};
4071 return &c;
4072 };
4073 gf["SCharAsInt"] = (cf_t) + [](cdims_t) {
4074 static SCharAsIntConverter c{};
4075 return &c;
4076 };
4077 gf["UCharAsInt"] = (cf_t) + [](cdims_t) {
4078 static UCharAsIntConverter c{};
4079 return &c;
4080 };
4081 gf["wchar_t"] = (cf_t) + [](cdims_t) {
4082 static WCharConverter c{};
4083 return &c;
4084 };
4085 gf["char16_t"] = (cf_t) + [](cdims_t) {
4086 static Char16Converter c{};
4087 return &c;
4088 };
4089 gf["char32_t"] = (cf_t) + [](cdims_t) {
4090 static Char32Converter c{};
4091 return &c;
4092 };
4093 gf["wchar_t&"] = (cf_t) + [](cdims_t) {
4094 static WCharRefConverter c{};
4095 return &c;
4096 };
4097 gf["char16_t&"] = (cf_t) + [](cdims_t) {
4098 static Char16RefConverter c{};
4099 return &c;
4100 };
4101 gf["char32_t&"] = (cf_t) + [](cdims_t) {
4102 static Char32RefConverter c{};
4103 return &c;
4104 };
4105 gf["int8_t"] = (cf_t) + [](cdims_t) {
4106 static Int8Converter c{};
4107 return &c;
4108 };
4109 gf["const int8_t&"] = (cf_t) + [](cdims_t) {
4110 static ConstInt8RefConverter c{};
4111 return &c;
4112 };
4113 gf["int8_t&"] = (cf_t) + [](cdims_t) {
4114 static Int8RefConverter c{};
4115 return &c;
4116 };
4117 gf["uint8_t"] = (cf_t) + [](cdims_t) {
4118 static UInt8Converter c{};
4119 return &c;
4120 };
4121 gf["const uint8_t&"] = (cf_t) + [](cdims_t) {
4122 static ConstUInt8RefConverter c{};
4123 return &c;
4124 };
4125 gf["uint8_t&"] = (cf_t) + [](cdims_t) {
4126 static UInt8RefConverter c{};
4127 return &c;
4128 };
4129 gf["int16_t"] = (cf_t) + [](cdims_t) {
4130 static Int16Converter c{};
4131 return &c;
4132 };
4133 gf["const int16_t&"] = (cf_t) + [](cdims_t) {
4134 static ConstInt16RefConverter c{};
4135 return &c;
4136 };
4137 gf["int16_t&"] = (cf_t) + [](cdims_t) {
4138 static Int16RefConverter c{};
4139 return &c;
4140 };
4141 gf["int32_t"] = (cf_t) + [](cdims_t) {
4142 static Int32Converter c{};
4143 return &c;
4144 };
4145 gf["const int32_t&"] = (cf_t) + [](cdims_t) {
4146 static ConstInt32RefConverter c{};
4147 return &c;
4148 };
4149 gf["int32_t&"] = (cf_t) + [](cdims_t) {
4150 static Int32RefConverter c{};
4151 return &c;
4152 };
4153 gf["uint16_t"] = (cf_t) + [](cdims_t) {
4154 static UInt16Converter c{};
4155 return &c;
4156 };
4157 gf["const uint16_t&"] = (cf_t) + [](cdims_t) {
4158 static ConstUInt16RefConverter c{};
4159 return &c;
4160 };
4161 gf["uint16_t&"] = (cf_t) + [](cdims_t) {
4162 static UInt16RefConverter c{};
4163 return &c;
4164 };
4165 gf["uint32_t"] = (cf_t) + [](cdims_t) {
4166 static UInt32Converter c{};
4167 return &c;
4168 };
4169 gf["const uint32_t&"] = (cf_t) + [](cdims_t) {
4170 static ConstUInt32RefConverter c{};
4171 return &c;
4172 };
4173 gf["uint32_t&"] = (cf_t) + [](cdims_t) {
4174 static UInt32RefConverter c{};
4175 return &c;
4176 };
4177 gf["short"] = (cf_t) + [](cdims_t) {
4178 static ShortConverter c{};
4179 return &c;
4180 };
4181 gf["const short&"] = (cf_t) + [](cdims_t) {
4182 static ConstShortRefConverter c{};
4183 return &c;
4184 };
4185 gf["short&"] = (cf_t) + [](cdims_t) {
4186 static ShortRefConverter c{};
4187 return &c;
4188 };
4189 gf["unsigned short"] = (cf_t) + [](cdims_t) {
4190 static UShortConverter c{};
4191 return &c;
4192 };
4193 gf["const unsigned short&"] = (cf_t) + [](cdims_t) {
4194 static ConstUShortRefConverter c{};
4195 return &c;
4196 };
4197 gf["unsigned short&"] = (cf_t) + [](cdims_t) {
4198 static UShortRefConverter c{};
4199 return &c;
4200 };
4201 gf["int"] = (cf_t) + [](cdims_t) {
4202 static IntConverter c{};
4203 return &c;
4204 };
4205 gf["int&"] = (cf_t) + [](cdims_t) {
4206 static IntRefConverter c{};
4207 return &c;
4208 };
4209 gf["const int&"] = (cf_t) + [](cdims_t) {
4210 static ConstIntRefConverter c{};
4211 return &c;
4212 };
4213 gf["unsigned int"] = (cf_t) + [](cdims_t) {
4214 static UIntConverter c{};
4215 return &c;
4216 };
4217 gf["const unsigned int&"] = (cf_t) + [](cdims_t) {
4218 static ConstUIntRefConverter c{};
4219 return &c;
4220 };
4221 gf["unsigned int&"] = (cf_t) + [](cdims_t) {
4222 static UIntRefConverter c{};
4223 return &c;
4224 };
4225 gf["long"] = (cf_t) + [](cdims_t) {
4226 static LongConverter c{};
4227 return &c;
4228 };
4229 gf["long&"] = (cf_t) + [](cdims_t) {
4230 static LongRefConverter c{};
4231 return &c;
4232 };
4233 gf["const long&"] = (cf_t) + [](cdims_t) {
4234 static ConstLongRefConverter c{};
4235 return &c;
4236 };
4237 gf["unsigned long"] = (cf_t) + [](cdims_t) {
4238 static ULongConverter c{};
4239 return &c;
4240 };
4241 gf["const unsigned long&"] = (cf_t) + [](cdims_t) {
4242 static ConstULongRefConverter c{};
4243 return &c;
4244 };
4245 gf["unsigned long&"] = (cf_t) + [](cdims_t) {
4246 static ULongRefConverter c{};
4247 return &c;
4248 };
4249 gf["long long"] = (cf_t) + [](cdims_t) {
4250 static LLongConverter c{};
4251 return &c;
4252 };
4253 gf["const long long&"] = (cf_t) + [](cdims_t) {
4254 static ConstLLongRefConverter c{};
4255 return &c;
4256 };
4257 gf["long long&"] = (cf_t) + [](cdims_t) {
4258 static LLongRefConverter c{};
4259 return &c;
4260 };
4261 gf["unsigned long long"] = (cf_t) + [](cdims_t) {
4262 static ULLongConverter c{};
4263 return &c;
4264 };
4265 gf["const unsigned long long&"] = (cf_t) + [](cdims_t) {
4266 static ConstULLongRefConverter c{};
4267 return &c;
4268 };
4269 gf["unsigned long long&"] = (cf_t) + [](cdims_t) {
4270 static ULLongRefConverter c{};
4271 return &c;
4272 };
4273
4274 gf["float"] = (cf_t) + [](cdims_t) {
4275 static FloatConverter c{};
4276 return &c;
4277 };
4278 gf["const float&"] = (cf_t) + [](cdims_t) {
4279 static ConstFloatRefConverter c{};
4280 return &c;
4281 };
4282 gf["float&"] = (cf_t) + [](cdims_t) {
4283 static FloatRefConverter c{};
4284 return &c;
4285 };
4286 gf["double"] = (cf_t) + [](cdims_t) {
4287 static DoubleConverter c{};
4288 return &c;
4289 };
4290 gf["double&"] = (cf_t) + [](cdims_t) {
4291 static DoubleRefConverter c{};
4292 return &c;
4293 };
4294 gf["const double&"] = (cf_t) + [](cdims_t) {
4295 static ConstDoubleRefConverter c{};
4296 return &c;
4297 };
4298 gf["long double"] = (cf_t) + [](cdims_t) {
4299 static LDoubleConverter c{};
4300 return &c;
4301 };
4302 gf["const long double&"] = (cf_t) + [](cdims_t) {
4303 static ConstLDoubleRefConverter c{};
4304 return &c;
4305 };
4306 gf["long double&"] = (cf_t) + [](cdims_t) {
4307 static LDoubleRefConverter c{};
4308 return &c;
4309 };
4310 gf["std::complex<double>"] =
4311 (cf_t) + [](cdims_t) { return new ComplexDConverter{}; };
4312 gf["const std::complex<double>&"] =
4313 (cf_t) + [](cdims_t) { return new ComplexDConverter{}; };
4314 gf["void"] = (cf_t) + [](cdims_t) {
4315 static VoidConverter c{};
4316 return &c;
4317 };
4318
4319 // pointer/array factories
4320 gf["bool ptr"] =
4321 (cf_t) + [](cdims_t d) { return new BoolArrayConverter{d}; };
4322 gf["signed char ptr"] =
4323 (cf_t) + [](cdims_t d) { return new SCharArrayConverter{d}; };
4324 gf["signed char**"] = (cf_t) + [](cdims_t) {
4325 return new SCharArrayConverter{{UNKNOWN_SIZE, UNKNOWN_SIZE}};
4326 };
4327 gf["const unsigned char*"] =
4328 (cf_t) + [](cdims_t d) { return new UCharArrayConverter{d}; };
4329 gf["unsigned char ptr"] =
4330 (cf_t) + [](cdims_t d) { return new UCharArrayConverter{d}; };
4331 gf["SCharAsInt*"] = gf["signed char ptr"];
4332 gf["SCharAsInt[]"] = gf["signed char ptr"];
4333 gf["UCharAsInt*"] = gf["unsigned char ptr"];
4334 gf["UCharAsInt[]"] = gf["unsigned char ptr"];
4335 gf["std::byte ptr"] =
4336 (cf_t) + [](cdims_t d) { return new ByteArrayConverter{d}; };
4337 gf["int8_t ptr"] =
4338 (cf_t) + [](cdims_t d) { return new Int8ArrayConverter{d}; };
4339 gf["int16_t ptr"] =
4340 (cf_t) + [](cdims_t d) { return new Int16ArrayConverter{d}; };
4341 gf["int32_t ptr"] =
4342 (cf_t) + [](cdims_t d) { return new Int32ArrayConverter{d}; };
4343 gf["uint8_t ptr"] =
4344 (cf_t) + [](cdims_t d) { return new UInt8ArrayConverter{d}; };
4345 gf["uint16_t ptr"] =
4346 (cf_t) + [](cdims_t d) { return new UInt16ArrayConverter{d}; };
4347 gf["uint32_t ptr"] =
4348 (cf_t) + [](cdims_t d) { return new UInt32ArrayConverter{d}; };
4349 gf["short ptr"] =
4350 (cf_t) + [](cdims_t d) { return new ShortArrayConverter{d}; };
4351 gf["unsigned short ptr"] =
4352 (cf_t) + [](cdims_t d) { return new UShortArrayConverter{d}; };
4353 gf["int ptr"] = (cf_t) + [](cdims_t d) { return new IntArrayConverter{d}; };
4354 gf["unsigned int ptr"] =
4355 (cf_t) + [](cdims_t d) { return new UIntArrayConverter{d}; };
4356 gf["long ptr"] =
4357 (cf_t) + [](cdims_t d) { return new LongArrayConverter{d}; };
4358 gf["unsigned long ptr"] =
4359 (cf_t) + [](cdims_t d) { return new ULongArrayConverter{d}; };
4360 gf["long long ptr"] =
4361 (cf_t) + [](cdims_t d) { return new LLongArrayConverter{d}; };
4362 gf["unsigned long long ptr"] =
4363 (cf_t) + [](cdims_t d) { return new ULLongArrayConverter{d}; };
4364 gf["float ptr"] =
4365 (cf_t) + [](cdims_t d) { return new FloatArrayConverter{d}; };
4366 gf["double ptr"] =
4367 (cf_t) + [](cdims_t d) { return new DoubleArrayConverter{d}; };
4368 gf["long double ptr"] =
4369 (cf_t) + [](cdims_t d) { return new LDoubleArrayConverter{d}; };
4370 gf["std::complex<float> ptr"] =
4371 (cf_t) + [](cdims_t d) { return new ComplexFArrayConverter{d}; };
4372 gf["std::complex<double> ptr"] =
4373 (cf_t) + [](cdims_t d) { return new ComplexDArrayConverter{d}; };
4374 gf["void*"] =
4375 (cf_t) + [](cdims_t d) { return new VoidArrayConverter{(bool)d}; };
4376
4377 // aliases
4378 gf["signed char"] = gf["char"];
4379 gf["const signed char&"] = gf["const char&"];
4380 gf["std::byte"] = gf["uint8_t"];
4381 gf["const std::byte&"] = gf["const uint8_t&"];
4382 gf["std::byte&"] = gf["uint8_t&"];
4383 gf["std::int8_t"] = gf["int8_t"];
4384 gf["const std::int8_t&"] = gf["const int8_t&"];
4385 gf["std::int8_t&"] = gf["int8_t&"];
4386 gf["std::uint8_t"] = gf["uint8_t"];
4387 gf["const std::uint8_t&"] = gf["const uint8_t&"];
4388 gf["std::uint8_t&"] = gf["uint8_t&"];
4389 gf["internal_enum_type_t"] = gf["int"];
4390 gf["internal_enum_type_t&"] = gf["int&"];
4391 gf["const internal_enum_type_t&"] = gf["const int&"];
4392 gf["internal_enum_type_t ptr"] = gf["int ptr"];
4393#ifdef _WIN32
4394 gf["__int64"] = gf["long long"];
4395 gf["const __int64&"] = gf["const long long&"];
4396 gf["__int64&"] = gf["long long&"];
4397 gf["__int64 ptr"] = gf["long long ptr"];
4398 gf["unsigned __int64"] = gf["unsigned long long"];
4399 gf["const unsigned __int64&"] = gf["const unsigned long long&"];
4400 gf["unsigned __int64&"] = gf["unsigned long long&"];
4401 gf["unsigned __int64 ptr"] = gf["unsigned long long ptr"];
4402#endif
4403 gf[CCOMPLEX_D] = gf["std::complex<double>"];
4404 gf["const " CCOMPLEX_D "&"] = gf["const std::complex<double>&"];
4405 gf[CCOMPLEX_F " ptr"] = gf["std::complex<float> ptr"];
4406 gf[CCOMPLEX_D " ptr"] = gf["std::complex<double> ptr"];
4407
4408 // We always need these converters when cppjit is based on an unpatched
4409 // ROOT, because the "long long" types are always converted to Long64_t
4410 // and ULong64_t already at the ROOT Meta level.
4411 // See
4412 // https://github.com/root-project/root/issues/15872#issuecomment-2174092763
4413 gf["Long64_t"] = gf["long long"];
4414 gf["Long64_t ptr"] = gf["long long ptr"];
4415 gf["Long64_t&"] = gf["long long&"];
4416 gf["const Long64_t&"] = gf["const long long&"];
4417 gf["ULong64_t"] = gf["unsigned long long"];
4418 gf["ULong64_t ptr"] = gf["unsigned long long ptr"];
4419 gf["ULong64_t&"] = gf["unsigned long long&"];
4420 gf["const ULong64_t&"] = gf["const unsigned long long&"];
4421
4422 // factories for special cases
4423 gf["nullptr_t"] = (cf_t) + [](cdims_t) {
4424 static NullptrConverter c{};
4425 return &c;
4426 };
4427 gf["const char*"] = (cf_t) + [](cdims_t) { return new CStringConverter{}; };
4428 gf["const signed char*"] = gf["const char*"];
4429 gf["const char*&&"] = gf["const char*"];
4430 gf["const char[]"] =
4431 (cf_t) + [](cdims_t) { return new CStringConverter{}; };
4432 gf["char*"] = (cf_t) + [](cdims_t d) {
4433 return new NonConstCStringConverter{dims2stringsz(d)};
4434 };
4435 gf["char[]"] = (cf_t) + [](cdims_t d) {
4436 return new NonConstCStringArrayConverter{d, true};
4437 };
4438 gf["signed char*"] = gf["char*"];
4439 gf["wchar_t*"] = (cf_t) + [](cdims_t) { return new WCStringConverter{}; };
4440 // TODO: The libc++ library that is used most prominently by Apple for
4441 // macOS is using the _Nonnull Clang attribute for some of its string
4442 // constructors (_LIBCPP_DIAGNOSE_NULLPTR is a preprocessor macro that
4443 // resolves to _Nullable). The cppjit backend doesn't strip away such Clang
4444 // attributes, so the wchar_t* converter fails to match. To make the
4445 // construction of std::wstring work on macOS, we are explicitly creating
4446 // a converter for the wchar_t*_Nonnull type here, but we should try to
4447 // find a more general solution for dealing with Clang attributes.
4448 // [1] https://clang.llvm.org/docs/AttributeReference.html#id656
4449 // [2]
4450 // https://github.com/llvm/llvm-project/blob/2078da43e25a4623cab2d0d60decddf709aaea28/libcxx/include/string#L1061
4451 gf["wchar_t*_Nonnull"] =
4452 (cf_t) + [](cdims_t) { return new WCStringConverter{}; };
4453 gf["char16_t*"] = (cf_t) + [](cdims_t) { return new CString16Converter{}; };
4454 gf["char16_t[]"] = (cf_t) + [](cdims_t d) {
4455 return new CString16Converter{dims2stringsz(d)};
4456 };
4457 gf["char32_t*"] = (cf_t) + [](cdims_t) { return new CString32Converter{}; };
4458 gf["char32_t[]"] = (cf_t) + [](cdims_t d) {
4459 return new CString32Converter{dims2stringsz(d)};
4460 };
4461 // TODO: the following are handled incorrectly upstream (char16_t** where
4462 // char16_t* intended)?!
4463 gf["char16_t**"] = gf["char16_t*"];
4464 gf["char32_t**"] = gf["char32_t*"];
4465 gf["const char**"] = (cf_t) + [](cdims_t) {
4466 return new CStringArrayConverter{{UNKNOWN_SIZE, UNKNOWN_SIZE}, false};
4467 };
4468 gf["char**"] = gf["const char**"];
4469 gf["const char*[]"] =
4470 (cf_t) + [](cdims_t d) { return new CStringArrayConverter{d, false}; };
4471 gf["char*[]"] = (cf_t) + [](cdims_t d) {
4472 return new NonConstCStringArrayConverter{d, false};
4473 };
4474 gf["char ptr"] = gf["char*[]"];
4475 gf["std::basic_string<char>"] =
4476 (cf_t) + [](cdims_t) { return new STLStringConverter{}; };
4477 gf["const std::basic_string<char>&"] = gf["std::basic_string<char>"];
4478 gf["std::basic_string<char>&&"] =
4479 (cf_t) + [](cdims_t) { return new STLStringMoveConverter{}; };
4480 gf["const std::basic_string<char> &"] = gf["std::basic_string<char>"];
4481 gf["std::basic_string<char> &&"] =
4482 (cf_t) + [](cdims_t) { return new STLStringMoveConverter{}; };
4483 gf["std::basic_string_view<char>"] =
4484 (cf_t) + [](cdims_t) { return new STLStringViewConverter{}; };
4485 gf[STRINGVIEW] = gf["std::basic_string_view<char>"];
4486 gf["std::basic_string_view<char>&"] = gf["std::basic_string_view<char>"];
4487 gf["const " STRINGVIEW "&"] = gf["std::basic_string_view<char>"];
4488 gf["std::basic_string_view<char> &"] = gf["std::basic_string_view<char>"];
4489 gf["const " STRINGVIEW " &"] = gf["std::basic_string_view<char>"];
4490 gf["std::wstring"] =
4491 (cf_t) + [](cdims_t) { return new STLWStringConverter{}; };
4492 gf[WSTRING1] = gf["std::wstring"];
4493 gf[WSTRING2] = gf["std::wstring"];
4494 gf["const std::wstring&"] = gf["std::wstring"];
4495 gf["const " WSTRING1 "&"] = gf["std::wstring"];
4496 gf["const " WSTRING2 "&"] = gf["std::wstring"];
4497 gf["const std::wstring &"] = gf["std::wstring"];
4498 gf["const " WSTRING1 " &"] = gf["std::wstring"];
4499 gf["const " WSTRING2 " &"] = gf["std::wstring"];
4500 // void*& is exempt from the T*& ban: the callee updates the proxy's
4501 // held pointer through the opaque handle
4502 gf["void*&"] = (cf_t) + [](cdims_t) {
4503 static VoidPtrRefConverter c{};
4504 return &c;
4505 };
4506 gf["const void*&"] = gf["void*&"];
4507 gf["void**"] =
4508 (cf_t) + [](cdims_t d) { return new VoidPtrPtrConverter{d}; };
4509 gf["void ptr"] = gf["void**"];
4510 gf["PyObject*"] = (cf_t) + [](cdims_t) {
4511 static PyObjectConverter c{};
4512 return &c;
4513 };
4514 gf["_object*"] = gf["PyObject*"];
4515 gf["FILE*"] = (cf_t) + [](cdims_t) { return new VoidArrayConverter{}; };
4516 }
4517} initConvFactories_;
4518
4519} // unnamed namespace
static int8_t cpyrt_PyLong_AsInt8(PyObject *pyobject)
#define CPPJIT_IMPL_REFCONVERTER(name, ctype, type, code)
#define ct_c_funcptr
static std::array< PyTypeObject *, 27 > gCTypesTypes
static bool IsPyCArgObject(PyObject *pyobject)
#define CPPJIT_PYLONG_AS_TYPE(name, type, limit_low, limit_high)
static PyObject * WrapperCacheEraser(PyObject *, PyObject *pyref)
#define CPPJIT_IMPL_BASIC_CONST_CHAR_REFCONVERTER(name, type, ctype, low, high)
#define ct_c_pointer
static std::array< PyTypeObject *, 27 > gCTypesPtrTypes
#define ct_c_long
static int cpyrt_PyLong_AsStrictInt(PyObject *pyobject)
static void GetSignatureFromFnType(interop::TCppType_t fn_type, std::string &ret, std::string &sig)
static bool SetLifeLine(PyObject *holder, PyObject *target, intptr_t ref)
static std::unordered_map< void *, PyObject ** > sWrapperReference
#define CPPJIT_IMPL_BASIC_CHAR_CONVERTER(name, type, low, high)
fBuffer
static int ExtractChar(PyObject *pyobject, const char *tname, int low, int high)
#define CPPJIT_IMPL_BASIC_CONVERTER_NB(name, type, stype, ctype, F1, F2, tc)
static bool cpyrt_PyLong_AsBool(PyObject *pyobject)
static bool HasLifeLine(PyObject *holder, intptr_t ref)
#define CPPJIT_IMPL_BASIC_CONST_REFCONVERTER(name, type, ctype, F1)
const Py_ssize_t MOVE_REFCOUNT_CUTOFF
static PyTypeObject * GetCTypesPtrType(int nidx)
#define ct_c_double
static cppjit::cpyrt::Converter * selectInstanceCnv(interop::TCppScope_t klass, const std::string &cpd, cpyrt::cdims_t dims, bool isConst, bool control)
static unsigned short cpyrt_PyLong_AsUShort(PyObject *pyobject)
static long cpyrt_PyLong_AsStrictLong(PyObject *pyobject)
#define ct_c_char_p
static uint32_t cpyrt_PyLong_AsUInt32(PyObject *pyobject)
std::string RetSigKey_t
#define ct_c_int
#define CPYRT_WIDESTRING_CONVERTER(name, type, encode, decode, snull)
#define CPPJIT_IMPL_BASIC_CONVERTER_NI(name, type, stype, ctype, F1, F2, tc)
#define CCOMPLEX_D
static uint16_t cpyrt_PyLong_AsUInt16(PyObject *pyobject)
#define CPPJIT_IMPL_STRING_AS_PRIMITIVE_CONVERTER(name, type, F1, F2)
static std::unordered_map< RetSigKey_t, std::vector< void * > > sWrapperFree
static PyMethodDef gWrapperCacheEraserMethodDef
static cpyrt::CPPInstance * ConvertImplicit(interop::TCppScope_t klass, PyObject *pyobject, cpyrt::Parameter &para, cpyrt::CallContext *ctxt, bool manage=true)
static bool cpyrt_PyUnicodeAsBytes2Buffer(PyObject *pyobject, T &buffer)
static PY_LONG_LONG cpyrt_PyLong_AsStrictLongLong(PyObject *pyobject)
static bool CArraySetArg(PyObject *pyobject, cpyrt::Parameter &para, char tc, int size, bool check=true)
static std::array< const char *, 27 > gCTypesNames
#define CPPJIT_IMPL_BASIC_CONVERTER_IB(name, type, stype, ctype, F1, F2, tc)
static int32_t cpyrt_PyLong_AsInt32(PyObject *pyobject)
free(fBuffer)
#define CCOMPLEX_F
static std::unordered_map< PyObject *, std::pair< void *, RetSigKey_t > > sWrapperWeakRefs
static std::unordered_map< RetSigKey_t, std::unordered_map< PyObject *, void * > > sWrapperLookup
static uint8_t cpyrt_PyLong_AsUInt8(PyObject *pyobject)
static short cpyrt_PyLong_AsShort(PyObject *pyobject)
static bool StrictBool(PyObject *pyobject, cpyrt::CallContext *ctxt)
#define CPPJIT_IMPL_REFCONVERTER_FROM_MEMORY(name, ctype)
#define WSTRING1
static void * PyFunction_AsCPointer(PyObject *pyobject, const interop::TCppType_t fn_type, bool allowCppInstance)
static bool ImplicitBool(PyObject *pyobject, cpyrt::CallContext *ctxt)
#define WSTRING2
static PyTypeObject * GetCTypesType(int nidx)
bool PyUnstable_Object_IsUniqueReferencedTemporary(PyObject *pyobject)
#define ct_c_void_p
#define CPPJIT_IMPL_ARRAY_CONVERTER(name, ctype, type, code, suffix)
static cpyrt::CPPInstance * GetCppInstance(PyObject *pyobject, interop::TCppScope_t klass=interop::TCppScope_t{}, bool accept_rvalue=false)
static bool IsCTypesArrayOrPointer(PyObject *pyobject)
static int16_t cpyrt_PyLong_AsInt16(PyObject *pyobject)
static unsigned int sWrapperCounter
static bool IsCPPEnumInstance(PyObject *pyobject)
#define STRINGVIEW
interop::TCppScope_t fValueType
bool fAllowCppInstance
interop::TCppScope_t fUnderlyingType
dims_t fShape
const std::string fFailureMsg
size_t fValueSize
cppjit::interop::TCppType_t fFnType
interop::TCppScope_t fSmartPtrType
bool fIsConst
std::string fValueTypeName
bool fIsFixed
bool fKeepControl
bool fIsRef
Converter * fBase
interop::TCppScope_t fClass
std::string fEnumName
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
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 src
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:142
float * q
#define CPYRT_EXPORT
Definition CommonDefs.h:25
interop::TCppScope_t GetSmartIsA() const
interop::TCppScope_t GetSmartUnderlyingType() const
void SetSmart(PyObject *smart_type)
interop::TCppScope_t ObjectIsA(bool check_smart=true) const
MethodInfo_t * fMethodInfo
Definition CPPOverload.h:83
virtual PyObject * FromMemory(void *address)
virtual std::string GetFailureMsg()
Definition Converters.h:32
virtual bool HasState()
Definition API.h:133
virtual bool SetArg(PyObject *, Parameter &, CallContext *=nullptr)=0
virtual bool ToMemory(PyObject *value, void *address, PyObject *ctxt=nullptr)
bool ToMemory(PyObject *value, void *address, PyObject *ctxt=nullptr) override
PyObject * FromMemory(void *address) override
bool SetArg(PyObject *, Parameter &, CallContext *=nullptr) override
static bool RegisterPyObject(CPPInstance *pyobj, interop::TCppObject_t cppobj)
bool SetArg(PyObject *, Parameter &, CallContext *=nullptr) override
bool SetArg(PyObject *, Parameter &, CallContext *=nullptr) override
bool ToMemory(PyObject *value, void *address, PyObject *ctxt=nullptr) override
PyObject * FromMemory(void *address) override
virtual bool GetAddressSpecialCase(PyObject *pyobject, void *&address)
long double PY_LONG_DOUBLE
unsigned long long PY_ULONG_LONG
long long PY_LONG_LONG
static const char * cpyrt_PyText_AsStringAndSize(PyObject *pystr, Py_ssize_t *size)
Definition cpyrt.h:44
#define PY_SSIZE_T_FORMAT
Definition cpyrt.h:65
#define Py_RETURN_NONE
Definition cpyrt.h:69
RooArgList L(Args_t &&... args)
Definition RooArgList.h:156
Type GetType(const std::string &Name)
std::vector< std::string > extract_arg_types(const std::string &sig)
std::string clean_type(const std::string &cppname, bool template_strip=true, bool const_strip=true)
std::string compound(const std::string &name)
bool BufferFormatCompatible(char tc, const char *fmt)
Definition Utility.cxx:1312
void ConstructCallbackPreamble(const std::string &retType, const std::vector< std::string > &argtypes, std::ostringstream &code)
Definition Utility.cxx:1004
void RestorePyError(PyError_t &error)
Definition Utility.cxx:1617
void ConstructCallbackReturn(const std::string &retType, int nArgs, std::ostringstream &code)
Definition Utility.cxx:1073
bool IsSTLIterator(const std::string &classname)
Definition Utility.cxx:1547
Py_ssize_t GetBuffer(PyObject *pyobject, char tc, int size, void *&buf, bool check=true)
Definition Utility.cxx:1341
std::unordered_map< std::string, char > const & TypecodeMap()
Definition Utility.cxx:1188
PyObject * FuncPtr2StdFunction(const std::string &retType, const std::string &signature, void *address)
Definition Utility.cxx:1112
PyError_t FetchPyError()
Definition Utility.cxx:1598
bool CPPOverload_Check(T *object)
Definition CPPOverload.h:94
bool TemplateProxy_Check(T *object)
PyObject * CreateLowLevelViewString(char **, cdims_t shape)
bool IsConstructor(uint64_t flags)
CPYRT_EXTERN Converter * CreateConverter(const std::string &name, cdims_t=0)
CPYRT_EXTERN bool RegisterConverter(const std::string &name, ConverterFactory_t)
PyObject * CreateLowLevelView(bool *, cdims_t shape)
bool LowLevelView_Check(T *object)
bool TupleOfInstances_CheckExact(T *object)
PyObject * gDefaultObject
PyObject * gNullPtrObject
PyObject * CreatePointerView(void *ptr, cdims_t shape=0)
PyObject * GetScopeProxy(interop::TCppScope_t)
PyObject * BindCppObject(interop::TCppObject_t object, interop::TCppScope_t klass, const unsigned flags=0)
PyObject * BindCppObjectArray(interop::TCppObject_t address, interop::TCppScope_t klass, cdims_t dims)
bool CPPExcInstance_Check(T *object)
Py_ssize_t dim_t
Definition API.h:100
CPYRT_EXTERN void DestroyConverter(Converter *p)
bool NoImplicit(CallContext *ctxt)
bool AllowImplicit(CallContext *ctxt)
bool AllowImplicitSmartPtrConversion(CallContext *ctxt)
PyObject * CreateScopeProxy(PyObject *, PyObject *args)
static const dim_t UNKNOWN_SIZE
Definition Dimensions.h:16
CPYRT_EXTERN bool UnregisterConverter(const std::string &name)
static ConvFactories_t gConvFactories
Dimensions dims_t
Definition API.h:113
PyObject * BindCppObjectNoCast(interop::TCppObject_t object, interop::TCppScope_t klass, const unsigned flags=0)
std::unordered_map< std::string, cf_t > ConvFactories_t
bool UseStrictOwnership()
unsigned long PyLongOrInt_AsULong(PyObject *pyobject)
Definition Utility.cxx:125
bool CPPScope_Check(T *object)
Definition CPPScope.h:86
bool CPPInstance_Check(T *object)
Converter *(* cf_t)(cdims_t d)
Definition Converters.h:41
PY_ULONG_LONG PyLongOrInt_AsULong64(PyObject *pyobject)
Definition Utility.cxx:152
Cpp::TypeRef TCppType_t
Definition API.h:42
TCppScope_t GetFullScope(const std::string &scope_name)
Cpp::ObjectRef TCppObject_t
Definition API.h:43
TCppType_t GetTypeFromScope(TCppScope_t klass)
std::string GetTypeAsString(TCppType_t type)
bool IsFunctionType(TCppType_t typ)
void CallDestructor(TCppScope_t type, TCppObject_t self)
TCppMethod_t GetMethodTemplate(TCppScope_t scope, const std::string &name, const std::string &proto)
TCppScope_t GetActualClass(TCppScope_t klass, TCppObject_t obj)
bool IsAbstract(TCppScope_t scope)
bool IsSubclass(TCppScope_t derived, TCppScope_t base)
TCppScope_t GetNamed(const std::string &scope_name, TCppScope_t parent_scope=TCppScope_t{})
size_t SizeOfType(TCppType_t type)
Cpp::FuncRef TCppMethod_t
Definition API.h:44
bool IsTypedefed(TCppScope_t scope)
bool GetSmartPtrInfo(TCppScope_t klass, TCppScope_t *raw, TCppMethod_t *deref)
void GetFnTypeSig(TCppType_t fn_type, std::vector< TCppType_t > &arg_types)
TCppFuncAddr_t GetFunctionAddress(TCppMethod_t method, bool check_enabled=true)
TCppType_t ResolveType(TCppType_t cppitem_name)
TCppScope_t GetUnderlyingScope(TCppScope_t scope)
bool Compile(const std::string &code, bool silent=false)
std::vector< TCppMethod_t > GetMethodsFromName(TCppScope_t scope, const std::string &name)
bool IsDefaultConstructable(TCppScope_t scope)
TCppScope_t GetScopeFromType(TCppType_t type)
TCppType_t GetRealType(TCppType_t type)
TCppScope_t GetScope(const std::string &scope_name, TCppScope_t parent_scope=TCppScope_t{})
ptrdiff_t GetBaseOffset(TCppScope_t derived, TCppScope_t base, TCppObject_t address, int direction, bool rerror=false)
TCppObject_t Construct(TCppScope_t scope, void *arena=nullptr)
bool IsMutablePtrRefType(TCppType_t type)
bool IsSmartPtr(TCppScope_t klass)
TCppType_t GetType(const std::string &name, bool enable_slow_lookup=false)
std::string GetScopedFinalName(TCppScope_t type)
Cpp::DeclRef TCppScope_t
Definition API.h:41
bool IsEnumType(TCppType_t type)
Definition API.cxx:34
void AddTemporary(PyObject *pyobj)
interop::TCppScope_t fCurScope
Definition CallContext.h:86
union cppjit::cpyrt::Parameter::Value fValue
PyObject_HEAD char * b_ptr
PyObject_HEAD void * pffi_type
union cpyrt_tagPyCArgObject::@182 value
TMarker m
Definition textangle.C:8
TLine l
Definition textangle.C:4
unsigned long long fULLong
Definition API.h:81