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