Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
Utility.cxx
Go to the documentation of this file.
1// Bindings
2#include "cpyrt.h"
3
4using namespace cppjit;
5#include "CPPFunction.h"
6#include "CPPInstance.h"
7#include "CPPOverload.h"
8#include "CustomPyTypes.h"
9#include "ProxyWrappers.h"
10#include "PyCallable.h"
11#include "PyStrings.h"
12#include "TemplateProxy.h"
13#include "TypeManip.h"
14#include "Utility.h"
15#include "cpyrt/DispatchPtr.h"
16
17// Standard
18#include <algorithm>
19#include <limits.h>
20#include <list>
21#include <mutex>
22#include <set>
23#include <sstream>
24#include <string.h>
25#include <utility>
26
27//- data _____________________________________________________________________
28typedef std::unordered_map<std::string, std::string> TC2POperatorMapping_t;
30static std::set<std::string> gOpSkip;
31static std::set<std::string> gOpRemove;
32
33namespace cppjit::cpyrt {
34// special objects
37} // namespace cppjit::cpyrt
38
39namespace {
40
41using namespace cpyrt::Utility;
42
43struct InitOperatorMapping_t {
44public:
45 InitOperatorMapping_t() {
46 // Initialize the global map of operator names C++ -> python.
47
48 gOpSkip.insert("[]"); // __s/getitem__, depends on return type
49 gOpSkip.insert("+"); // __add__, depends on # of args (see __pos__)
50 gOpSkip.insert("-"); // __sub__, id. (eq. __neg__)
51 gOpSkip.insert("*"); // __mul__ or __deref__
52 gOpSkip.insert("++"); // __postinc__ or __preinc__
53 gOpSkip.insert("--"); // __postdec__ or __predec__
54
55 gOpRemove.insert("new"); // this and the following not handled at all
56 gOpRemove.insert("new[]");
57 gOpRemove.insert("delete");
58 gOpRemove.insert("delete[]");
59
60 gC2POperatorMapping["[]"] = "__getitem__";
61 gC2POperatorMapping["()"] = "__call__";
62 gC2POperatorMapping["%"] = "__mod__";
63 gC2POperatorMapping["**"] = "__pow__";
64 gC2POperatorMapping["<<"] = "__lshift__";
65 gC2POperatorMapping[">>"] = "__rshift__";
66 gC2POperatorMapping["&"] = "__and__";
67 gC2POperatorMapping["&&"] = "__dand__";
68 gC2POperatorMapping["|"] = "__or__";
69 gC2POperatorMapping["||"] = "__dor__";
70 gC2POperatorMapping["^"] = "__xor__";
71 gC2POperatorMapping["~"] = "__invert__";
72 gC2POperatorMapping[","] = "__comma__";
73 gC2POperatorMapping["+="] = "__iadd__";
74 gC2POperatorMapping["-="] = "__isub__";
75 gC2POperatorMapping["*="] = "__imul__";
76 gC2POperatorMapping["/="] = "__itruediv__";
77 gC2POperatorMapping["%="] = "__imod__";
78 gC2POperatorMapping["**="] = "__ipow__";
79 gC2POperatorMapping["<<="] = "__ilshift__";
80 gC2POperatorMapping[">>="] = "__irshift__";
81 gC2POperatorMapping["&="] = "__iand__";
82 gC2POperatorMapping["|="] = "__ior__";
83 gC2POperatorMapping["^="] = "__ixor__";
84 gC2POperatorMapping["=="] = "__eq__";
85 gC2POperatorMapping["!="] = "__ne__";
86 gC2POperatorMapping[">"] = "__gt__";
87 gC2POperatorMapping["<"] = "__lt__";
88 gC2POperatorMapping[">="] = "__ge__";
89 gC2POperatorMapping["<="] = "__le__";
90
91 // the following type mappings are "exact"
92 gC2POperatorMapping["const char*"] = "__str__";
93 gC2POperatorMapping["char*"] = "__str__";
94 gC2POperatorMapping["const char *"] = gC2POperatorMapping["const char*"];
95 gC2POperatorMapping["char *"] = gC2POperatorMapping["char*"];
96 gC2POperatorMapping["int"] = "__int__";
97 gC2POperatorMapping["long"] = "__int__";
98 gC2POperatorMapping["double"] = "__float__";
99
100 // the following type mappings are "okay"; the assumption is that they
101 // are not mixed up with the ones above or between themselves (and if
102 // they are, that it is done consistently)
103 gC2POperatorMapping["short"] = "__int__";
104 gC2POperatorMapping["unsigned short"] = "__int__";
105 gC2POperatorMapping["unsigned int"] = "__int__";
106 gC2POperatorMapping["unsigned long"] = "__int__";
107 gC2POperatorMapping["long long"] = "__int__";
108 gC2POperatorMapping["unsigned long long"] = "__int__";
109 gC2POperatorMapping["float"] = "__float__";
110
111 gC2POperatorMapping["->"] = "__follow__"; // not an actual python operator
112 gC2POperatorMapping["="] = "__assign__"; // id.
113
114 gC2POperatorMapping["bool"] = "__cpp_bool__";
115 }
116} initOperatorMapping_;
117
118inline std::string full_scope(const std::string& tpname) {
119 return tpname[0] == ':' ? tpname : "::" + tpname;
120}
121
122} // unnamed namespace
123
124//- public functions ---------------------------------------------------------
125unsigned long cpyrt::PyLongOrInt_AsULong(PyObject* pyobject) {
126 // Convert <pybject> to C++ unsigned long, with bounds checking, allow int ->
127 // ulong.
128 if (PyFloat_Check(pyobject)) {
129 PyErr_SetString(PyExc_TypeError, "can\'t convert float to unsigned long");
130 return (unsigned long)-1;
131 } else if (pyobject == cpyrt::gDefaultObject) {
132 return (unsigned long)0;
133 }
134
135 unsigned long ul = PyLong_AsUnsignedLong(pyobject);
136 if (ul == (unsigned long)-1 && PyErr_Occurred() && PyLong_Check(pyobject)) {
137 PyErr_Clear();
138 long i = PyLong_AsLong(pyobject);
139 if (0 <= i) {
140 ul = (unsigned long)i;
141 } else {
142 PyErr_SetString(PyExc_ValueError,
143 "can\'t convert negative value to unsigned long");
144 return (unsigned long)-1;
145 }
146 }
147
148 return ul;
149}
150
151//----------------------------------------------------------------------------
153 // Convert <pyobject> to C++ unsigned long long, with bounds checking.
154 if (PyFloat_Check(pyobject)) {
155 PyErr_SetString(PyExc_TypeError,
156 "can\'t convert float to unsigned long long");
157 return -1;
158 } else if (pyobject == cpyrt::gDefaultObject) {
159 return (unsigned long)0;
160 }
161
162 PY_ULONG_LONG ull = PyLong_AsUnsignedLongLong(pyobject);
163 if (PyErr_Occurred() && PyLong_Check(pyobject)) {
164 PyErr_Clear();
165 long i = PyLong_AsLong(pyobject);
166 if (0 <= i) {
167 ull = (PY_ULONG_LONG)i;
168 } else {
169 PyErr_SetString(PyExc_ValueError,
170 "can\'t convert negative value to unsigned long long");
171 }
172 }
173
174 return ull;
175}
176
177//----------------------------------------------------------------------------
178bool cpyrt::Utility::AddToClass(PyObject* pyclass, const char* label,
179 PyCFunction cfunc, int flags) {
180 // Add the given function to the class under name 'label'.
181
182 // use list for clean-up (.so's are unloaded only at interpreter shutdown)
183 static std::list<PyMethodDef> s_pymeths;
184
185 s_pymeths.push_back(PyMethodDef());
186 PyMethodDef* pdef = &s_pymeths.back();
187 pdef->ml_name = const_cast<char*>(label);
188 pdef->ml_meth = cfunc;
189 pdef->ml_flags = flags;
190 pdef->ml_doc = nullptr;
191
192 PyObject* func = PyCFunction_New(pdef, nullptr);
193 PyObject* name = PyUnicode_InternFromString(pdef->ml_name);
194 PyObject* method = CustomInstanceMethod_New(func, nullptr, pyclass);
195 PyObject *pytype = 0, *pyvalue = 0, *pytrace = 0;
196 PyErr_Fetch(&pytype, &pyvalue, &pytrace);
197 bool isOk = PyType_Type.tp_setattro(pyclass, name, method) == 0;
198 PyErr_Restore(pytype, pyvalue, pytrace);
199 Py_DECREF(method);
200 Py_DECREF(name);
201 Py_DECREF(func);
202
203 if (PyErr_Occurred())
204 return false;
205
206 if (!isOk) {
207 PyErr_Format(PyExc_TypeError, "could not add method %s", label);
208 return false;
209 }
210
211 return true;
212}
213
214//----------------------------------------------------------------------------
215bool cpyrt::Utility::AddToClass(PyObject* pyclass, const char* label,
216 const char* func) {
217 // Add the given function to the class under name 'label'.
218 PyObject* pyfunc = PyObject_GetAttrString(pyclass, const_cast<char*>(func));
219 if (!pyfunc)
220 return false;
221
222 PyObject* pylabel = PyUnicode_InternFromString(const_cast<char*>(label));
223 bool isOk = PyType_Type.tp_setattro(pyclass, pylabel, pyfunc) == 0;
224 Py_DECREF(pylabel);
225
226 Py_DECREF(pyfunc);
227 return isOk;
228}
229
230//----------------------------------------------------------------------------
231bool cpyrt::Utility::AddToClass(PyObject* pyclass, const char* label,
232 PyCallable* pyfunc) {
233 // Add the given function to the class under name 'label'.
234 CPPOverload* method =
235 (CPPOverload*)PyObject_GetAttrString(pyclass, const_cast<char*>(label));
236
237 if (!method || !CPPOverload_Check(method)) {
238 // not adding to existing CPPOverload; add callable directly to the class
239 if (PyErr_Occurred())
240 PyErr_Clear();
241 Py_XDECREF((PyObject*)method);
242 method = CPPOverload_New(label, pyfunc);
243 PyObject* pylabel = PyUnicode_InternFromString(const_cast<char*>(label));
244 bool isOk =
245 PyType_Type.tp_setattro(pyclass, pylabel, (PyObject*)method) == 0;
246 Py_DECREF(pylabel);
247 Py_DECREF(method);
248 return isOk;
249 }
250
251 method->AdoptMethod(pyfunc);
252
253 Py_DECREF(method);
254 return true;
255}
256
257//----------------------------------------------------------------------------
258static inline cpyrt::PyCallable* BuildOperator(const std::string& lcname,
259 const std::string& rcname,
260 const char* op,
262 bool reverse = false) {
263 // Helper to find a function with matching signature in 'funcs'.
264
266 interop::GetGlobalOperator(scope, lcname, rcname, op);
267 if (!meth)
268 return nullptr;
269
270 if (!reverse)
271 return new cpyrt::CPPFunction(scope, meth);
272 return new cpyrt::CPPReverseBinary(scope, meth);
273}
274
275//----------------------------------------------------------------------------
277 const char* op) {
278 // Find a callable matching named operator (op) and klass arguments in the
279 // global namespace or the klass' namespace.
280
281 if (!CPPScope_Check(pyclass))
282 return nullptr;
283
284 CPPClass* klass = (CPPClass*)pyclass;
285 const std::string& lcname = interop::GetScopedFinalName(klass->fCppType);
287 interop::GetScope(TypeManip::extract_namespace(lcname));
288 return FindBinaryOperator(lcname, "", op, scope, false);
289}
290
291//----------------------------------------------------------------------------
294 const char* op, interop::TCppScope_t scope) {
295 // Find a callable matching the named operator (op) and the (left, right)
296 // arguments in the global or these objects' namespaces.
297
298 bool reverse = false;
299 if (!CPPInstance_Check(left)) {
300 if (CPPInstance_Check(right))
301 reverse = true;
302 else
303 return nullptr;
304 }
305
306 // retrieve the class names to match the signature of any found global
307 // functions
308 const std::string& lcname = ClassName(left);
309 const std::string& rcname = ClassName(right);
310 return FindBinaryOperator(lcname, rcname, op, scope, reverse);
311}
312
313//----------------------------------------------------------------------------
315cpyrt::Utility::FindBinaryOperator(const std::string& lcname,
316 const std::string& rcname, const char* op,
317 interop::TCppScope_t scope, bool reverse) {
318 // Find a global function with a matching signature; search __gnu_cxx,
319 // std::__1, and __cppjit_internal pro-actively (as there's AFAICS no way to
320 // unearth 'using' information).
321
322 if (rcname == "<unknown>" || lcname == "<unknown>")
323 return nullptr;
324
325 PyCallable* pyfunc = 0;
326
327 if (!scope) {
328 // TODO: the following should remain sync with what clingwrapper does in its
329 // type remapper; there must be a better way?
330 if (lcname == "str" || lcname == "unicode" || lcname == "complex" ||
331 lcname.find("std::") == 0)
332 scope = interop::GetScope("std");
333 }
334 if (scope)
335 pyfunc = BuildOperator(lcname, rcname, op, scope, reverse);
336
337 // search the global scope before the namespace of the left operand: a user
338 // operator for these exact types defined at global scope is what C++ overload
339 // resolution would select over a generic template found through ADL. MSVC's
340 // std::ostream rvalue-stream-inserter template (ostream&&, const _Ty&) is
341 // greedy enough to match any (ostream, T) pair, and searching std first made
342 // it shadow global-scope operator<< overloads (advancedcpp test25).
343 if (!pyfunc && scope != interop::GetGlobalScope())
344 pyfunc =
345 BuildOperator(lcname, rcname, op, interop::GetGlobalScope(), reverse);
346
347 if (!pyfunc) {
348 interop::TCppScope_t lcscope =
349 interop::GetScope(TypeManip::extract_namespace(lcname));
350 if (lcscope && lcscope != scope && lcscope != interop::GetGlobalScope())
351 pyfunc = BuildOperator(lcname, rcname, op, lcscope, reverse);
352 }
353
354 if (!pyfunc) {
355 // For GNU on clang, search the internal __gnu_cxx namespace for binary
356 // operators (is typically the case for STL iterators operator==/!=.
357 // TODO: only look in __gnu_cxx for iterators (and more generally: do
358 // lookups in the
359 // namespace where the class is defined
360 static interop::TCppScope_t gnucxx = interop::GetScope("__gnu_cxx");
361 if (gnucxx)
362 pyfunc = BuildOperator(lcname, rcname, op, gnucxx, reverse);
363 }
364
365 if (!pyfunc) {
366 // Same for clang (on Mac only?). TODO: find proper pre-processor magic to
367 // only use those specific namespaces that are actually around; although to
368 // be sure, this isn't expensive.
369 static interop::TCppScope_t std__1 = interop::GetFullScope("std::__1");
370
371 if (std__1
372#ifdef __APPLE__
373 && lcname.find("__wrap_iter") ==
374 std::string::npos // wrapper call does not compile
375#endif
376 ) {
377 pyfunc = BuildOperator(lcname, rcname, op, std__1, reverse);
378 }
379 }
380
381 if (!pyfunc) {
382 // One more, mostly for Mac, but again not sure whether this is not a
383 // general issue. Some operators are declared as friends only in classes, so
384 // then they're not found in the global namespace, so this helper let's the
385 // compiler resolve the operator.
386 static interop::TCppScope_t s_intern =
387 interop::GetScope("__cppjit_internal");
388 if (s_intern) {
389 std::stringstream fname, proto;
390 if (strncmp(op, "==", 2) == 0) {
391 fname << "is_equal<";
392 } else if (strncmp(op, "!=", 2) == 0) {
393 fname << "is_not_equal<";
394 } else {
395 fname << "not_implemented<";
396 }
397 fname << lcname << ", " << rcname << ">";
398 proto << "const " << lcname << "&, const " << rcname;
399 interop::TCppMethod_t method =
400 interop::GetMethodTemplate(s_intern, fname.str(), proto.str());
401 if (method)
402 pyfunc = new CPPFunction(s_intern, method);
403 }
404 }
405
406 return pyfunc;
407}
408
409//----------------------------------------------------------------------------
410static inline std::string AnnotationAsText(PyObject* pyobj) {
411 if (!PyUnicode_Check(pyobj)) {
412 PyObject* pystr = PyObject_GetAttr(pyobj, cpyrt::PyStrings::gName);
413 if (!pystr) {
414 PyErr_Clear();
415 pystr = PyObject_Str(pyobj);
416 }
417
418 std::string str = PyUnicode_AsUTF8(pystr);
419 Py_DECREF(pystr);
420 return str;
421 }
422 return PyUnicode_AsUTF8(pyobj);
423}
424
425static bool AddTypeName(std::string& tmpl_name, PyObject* tn, PyObject* arg,
427 int* pcnt = nullptr) {
428 // Determine the appropriate C++ type for a given Python type; this is a
429 // helper because it can recurse if the type is list or tuple and needs
430 // matching on std::vector.
431 using namespace cppjit::cpyrt;
432 using namespace cpyrt::Utility;
433
434 if (tn == (PyObject*)&PyLong_Type) {
435 if (arg) {
436 PY_LONG_LONG ll = PyLong_AsLongLong(arg);
437 if (ll == (PY_LONG_LONG)-1 && PyErr_Occurred()) {
438 PyErr_Clear();
439 PY_ULONG_LONG ull = PyLong_AsUnsignedLongLong(arg);
440 if (ull == (PY_ULONG_LONG)-1 && PyErr_Occurred()) {
441 PyErr_Clear();
442 tmpl_name.append("int"); // still out of range, will fail later
443 } else
444 tmpl_name.append(
445 "unsigned long long"); // since already failed long long
446 } else
447 tmpl_name.append(
448 (ll < INT_MIN || INT_MAX < ll)
449 ? ((ll < LONG_MIN || LONG_MAX < ll) ? "long long" : "long")
450 : "int");
451 } else
452 tmpl_name.append("int");
453
454 return true;
455 }
456
457 if (tn == (PyObject*)&PyFloat_Type) {
458 // special case for floats (Python-speak for double) if from argument (only)
459 tmpl_name.append(arg ? "double" : "float");
460 return true;
461 }
462
463 if (tn == (PyObject*)&PyComplex_Type) {
464 tmpl_name.append("std::complex<double>");
465 return true;
466 }
467
468 if (tn == (PyObject*)&PyUnicode_Type) {
469 tmpl_name.append("std::string");
470 return true;
471 }
472
473 if (tn == (PyObject*)&PyList_Type || tn == (PyObject*)&PyTuple_Type) {
474 if (arg && PySequence_Size(arg)) {
475 std::string subtype{"std::initializer_list<"};
476 PyObject* item = PySequence_GetItem(arg, 0);
477 ArgPreference subpref = pref == kValue ? kValue : kPointer;
478 bool ret = AddTypeName(subtype, (PyObject*)Py_TYPE(item), item, subpref);
479 if (ret) {
480 tmpl_name.append(subtype);
481 tmpl_name.append(">");
482 }
483 Py_DECREF(item);
484 // Error occurred in inner call to AddTypeName, which means it has
485 // also set a TypeError. We return and let the error propagate.
486 if (!ret) {
487 return false;
488 }
489 }
490
491 return true;
492 }
493
494 if (CPPScope_Check(tn)) {
495 auto cpp_type = interop::GetScopedFinalName(((CPPClass*)tn)->fCppType);
496 tmpl_name.append(full_scope(cpp_type));
497 if (arg) {
498 // try to specialize the type match for the given object
499 CPPInstance* pyobj = (CPPInstance*)arg;
500 if (CPPInstance_Check(pyobj)) {
501 if (pyobj->fFlags & CPPInstance::kIsRValue)
502 tmpl_name.append("&&");
503 else {
504 if (pcnt)
505 *pcnt += 1;
506 if ((pyobj->fFlags & CPPInstance::kIsReference) || pref == kPointer)
507 tmpl_name.push_back('*');
508 else if (pref != kValue)
509 tmpl_name.push_back('&');
510 }
511 }
512 }
513
514 return true;
515 }
516
517 if (tn == (PyObject*)&CPPOverload_Type) {
518 PyObject* tpName = arg ? PyObject_GetAttr(arg, PyStrings::gCppName)
519 : PyUnicode_FromString("void* (*)(...)");
520 tmpl_name.append(PyUnicode_AsUTF8(tpName));
521 Py_DECREF(tpName);
522
523 return true;
524 }
525
526 if (arg && PyCallable_Check(arg)) {
527 // annotated/typed Python function
528 PyObject* annot = PyObject_GetAttr(arg, PyStrings::gAnnotations);
529 if (annot) {
530 if (PyDict_Check(annot) && 1 < PyDict_Size(annot)) {
531 PyObject* ret = PyDict_GetItemString(annot, "return");
532 if (ret) {
533 // dict is ordered, with the last value being the return type
534 std::ostringstream tpn;
535 tpn << (CPPScope_Check(ret) ? ClassName(ret) : AnnotationAsText(ret))
536 << " (*)(";
537
538 PyObject* values = PyDict_Values(annot);
539 for (Py_ssize_t i = 0; i < (PyList_GET_SIZE(values) - 1); ++i) {
540 if (i)
541 tpn << ", ";
542 PyObject* item = PyList_GET_ITEM(values, i);
543 tpn << (CPPScope_Check(item) ? full_scope(ClassName(item))
544 : AnnotationAsText(item));
545 }
546 Py_DECREF(values);
547
548 tpn << ')';
549 tmpl_name.append(tpn.str());
550
551 Py_DECREF(annot);
552 return true;
553
554 } else
555 PyErr_Clear();
556 }
557 Py_DECREF(annot);
558 } else
559 PyErr_Clear();
560
561 // ctypes function pointer
562 PyObject* argtypes = nullptr;
563 PyObject* ret = nullptr;
564 if ((argtypes = PyObject_GetAttrString(arg, "argtypes")) &&
565 (ret = PyObject_GetAttrString(arg, "restype"))) {
566 std::ostringstream tpn;
567 PyObject* pytc = PyObject_GetAttr(ret, PyStrings::gCTypesType);
568 tpn << CT2CppNameS(pytc, false) << " (*)(";
569 Py_DECREF(pytc);
570
571 for (Py_ssize_t i = 0; i < PySequence_Length(argtypes); ++i) {
572 if (i)
573 tpn << ", ";
574 PyObject* item = PySequence_GetItem(argtypes, i);
575 pytc = PyObject_GetAttr(item, PyStrings::gCTypesType);
576 tpn << CT2CppNameS(pytc, false);
577 Py_DECREF(pytc);
578 Py_DECREF(item);
579 }
580
581 tpn << ')';
582 tmpl_name.append(tpn.str());
583
584 Py_DECREF(ret);
585 Py_DECREF(argtypes);
586
587 return true;
588
589 } else {
590 PyErr_Clear();
591 Py_XDECREF(ret);
592 Py_XDECREF(argtypes);
593 }
594
595 // callable C++ type (e.g. std::function)
596 PyObject* tpName = PyObject_GetAttr(arg, PyStrings::gCppName);
597 if (tpName) {
598 const char* cname = PyUnicode_AsUTF8(tpName);
599 tmpl_name.append(CPPScope_Check(arg) ? full_scope(cname) : cname);
600 Py_DECREF(tpName);
601 return true;
602 }
603 PyErr_Clear();
604 }
605
606 for (auto nn : {PyStrings::gCppName, PyStrings::gName}) {
607 PyObject* tpName = PyObject_GetAttr(tn, nn);
608 if (tpName) {
609 tmpl_name.append(PyUnicode_AsUTF8(tpName));
610 Py_DECREF(tpName);
611 return true;
612 }
613 PyErr_Clear();
614 }
615
616 if (PyLong_Check(tn) || PyFloat_Check(tn)) {
617 // last ditch attempt, works for things like int values; since this is a
618 // source of errors otherwise, it is limited to specific types and not
619 // generally used (str(obj) can print anything ...)
620 PyObject* pystr = PyObject_Str(tn);
621 tmpl_name.append(PyUnicode_AsUTF8(pystr));
622 Py_DECREF(pystr);
623 return true;
624 }
625
626 // Give up with a TypeError
627
628 // Try to get a readable representation of the argument
629 PyObject* repr = PyObject_Repr(tn);
630 const char* repr_cstr = repr ? PyUnicode_AsUTF8(repr) : "<unprintable>";
631
632 PyErr_Format(PyExc_TypeError,
633 "could not construct C++ name from template argument %s",
634 repr_cstr);
635
636 Py_XDECREF(repr);
637 return false;
638}
639
641 PyObject* tpArgs,
642 PyObject* args,
643 ArgPreference pref,
644 int argoff, int* pcnt) {
645 // Helper to construct the "<type, type, ...>" part of a templated name
646 // (either for a class or method lookup
647 bool justOne = !PyTuple_CheckExact(tpArgs);
648
649 // Note: directly appending to string is a lot faster than stringstream
650 std::string tmpl_name;
651 tmpl_name.reserve(128);
652 if (pyname)
653 tmpl_name.append(PyUnicode_AsUTF8(pyname));
654 tmpl_name.push_back('<');
655
656 if (pcnt)
657 *pcnt = 0; // count number of times 'pref' is used
658
659 Py_ssize_t nArgs = justOne ? 1 : PyTuple_GET_SIZE(tpArgs);
660 for (int i = argoff; i < nArgs; ++i) {
661 // add type as string to name
662 PyObject* tn = justOne ? tpArgs : PyTuple_GET_ITEM(tpArgs, i);
663 if (PyUnicode_Check(tn)) {
664 tmpl_name.append(PyUnicode_AsUTF8(tn));
665 // some common numeric types (separated out for performance: checking for
666 // __cpp_name__ and/or __name__ is rather expensive)
667 } else {
668 if (!AddTypeName(tmpl_name, tn,
669 (args ? PyTuple_GET_ITEM(args, i) : nullptr), pref,
670 pcnt)) {
671 PyErr_SetString(
672 PyExc_TypeError,
673 "could not construct C++ name from provided template argument.");
674 return "";
675 }
676 }
677
678 // add a comma, as needed (no space as internally, final names don't have
679 // them)
680 if (i != nArgs - 1)
681 tmpl_name.push_back(',');
682 }
683
684 // close template name
685 tmpl_name.push_back('>');
686
687 return tmpl_name;
688}
689
690//----------------------------------------------------------------------------
691static bool AddTypeName(std::vector<Cpp::TemplateArgInfo>& types, PyObject* tn,
693 int* pcnt = nullptr) {
694 // Determine the appropriate C++ type for a given Python type; this is a
695 // helper because it can recurse if the type is list or tuple and needs
696 // matching on std::vector.
697 using namespace cppjit::cpyrt;
698 using namespace cpyrt::Utility;
699
700 if (tn == (PyObject*)&PyLong_Type) {
701 if (arg) {
702 PY_LONG_LONG ll = PyLong_AsLongLong(arg);
703 if (ll == (PY_LONG_LONG)-1 && PyErr_Occurred()) {
704 PyErr_Clear();
705 PY_ULONG_LONG ull = PyLong_AsUnsignedLongLong(arg);
706 if (ull == (PY_ULONG_LONG)-1 && PyErr_Occurred()) {
707 PyErr_Clear();
708 types.push_back(interop::GetType("int")
709 .data); // still out of range, will fail later
710 } else
711 types.push_back(interop::GetType("unsigned long long")
712 .data); // since already failed long long
713 } else
714 types.push_back(
716 (ll < INT_MIN || INT_MAX < ll)
717 ? ((ll < LONG_MIN || LONG_MAX < ll) ? "long long" : "long")
718 : "int")
719 .data);
720 } else {
721 types.push_back(interop::GetType("int").data);
722 }
723
724 return true;
725 }
726
727 if (tn == (PyObject*)&PyFloat_Type) {
728 // special case for floats (Python-speak for double) if from argument (only)
729 types.push_back(interop::GetType(arg ? "double" : "float").data);
730 return true;
731 }
732
733 if (tn == (PyObject*)&PyComplex_Type) {
734 types.push_back(
735 interop::GetType("std::complex<double>", /* enable_slow_lookup */ true)
736 .data);
737 return true;
738 }
739
740 if (tn == (PyObject*)&PyUnicode_Type) {
741 types.push_back(
742 interop::GetType("std::string", /* enable_slow_lookup */ true).data);
743 return true;
744 }
745
746 if (tn == (PyObject*)&PyList_Type || tn == (PyObject*)&PyTuple_Type) {
747 if (arg && PySequence_Size(arg)) {
748 std::string subtype{"std::initializer_list<"};
749 PyObject* item = PySequence_GetItem(arg, 0);
750 ArgPreference subpref = pref == kValue ? kValue : kPointer;
751 if (AddTypeName(subtype, (PyObject*)Py_TYPE(item), item, subpref)) {
752 subtype.append(">");
753 types.push_back(interop::GetType(subtype).data);
754 }
755 Py_DECREF(item);
756 }
757
758 return true;
759 }
760
761 if (CPPScope_Check(tn)) {
762 auto cpp_type = interop::GetTypeFromScope(((CPPClass*)tn)->fCppType);
763 if (arg) {
764 // try to specialize the type match for the given object
765 CPPInstance* pyobj = (CPPInstance*)arg;
766 if (CPPInstance_Check(pyobj)) {
767 if (pyobj->fFlags & CPPInstance::kIsRValue)
768 cpp_type = interop::GetReferencedType(cpp_type, /*rvalue=*/true);
769 else {
770 if (pcnt)
771 *pcnt += 1;
772 if ((pyobj->fFlags & CPPInstance::kIsReference) || pref == kPointer)
773 cpp_type = interop::GetPointerType(cpp_type);
774 else if (pref != kValue)
775 cpp_type = interop::GetReferencedType(cpp_type, /*rvalue=*/false);
776 }
777 }
778 }
779 types.push_back(cpp_type.data);
780 return true;
781 }
782
783 if (tn == (PyObject*)&CPPOverload_Type) {
784 PyObject* tpName = arg ? PyObject_GetAttr(arg, PyStrings::gCppName)
785 : PyUnicode_FromString("void* (*)(...)");
786 types.push_back(interop::GetType(PyUnicode_AsUTF8(tpName),
787 /* enable_slow_lookup */ true)
788 .data);
789 Py_DECREF(tpName);
790
791 return true;
792 }
793
794 if (arg && PyCallable_Check(arg)) {
795 PyObject* annot = PyObject_GetAttr(arg, PyStrings::gAnnotations);
796 if (annot) {
797 if (PyDict_Check(annot) && 1 < PyDict_Size(annot)) {
798 PyObject* ret = PyDict_GetItemString(annot, "return");
799 if (ret) {
800 // dict is ordered, with the last value being the return type
801 std::ostringstream tpn;
802 tpn << (CPPScope_Check(ret) ? ClassName(ret) : PyUnicode_AsUTF8(ret))
803 << " (*)(";
804
805 PyObject* values = PyDict_Values(annot);
806 for (Py_ssize_t i = 0; i < (PyList_GET_SIZE(values) - 1); ++i) {
807 if (i)
808 tpn << ", ";
809 PyObject* item = PyList_GET_ITEM(values, i);
810 tpn << (CPPScope_Check(item) ? ClassName(item)
811 : PyUnicode_AsUTF8(item));
812 }
813 Py_DECREF(values);
814
815 tpn << ')';
816 // tmpl_name.append(tpn.str());
817 // FIXME: find a way to add it to types
818 throw std::runtime_error(
819 "This path is not yet implemented (AddTypeName) \n");
820
821 return true;
822
823 } else
824 PyErr_Clear();
825 }
826 Py_DECREF(annot);
827 } else
828 PyErr_Clear();
829
830 PyObject* tpName = PyObject_GetAttr(arg, PyStrings::gCppName);
831 if (tpName) {
832 types.push_back(interop::GetType(PyUnicode_AsUTF8(tpName),
833 /* enable_slow_lookup */ true)
834 .data);
835 Py_DECREF(tpName);
836 return true;
837 }
838 PyErr_Clear();
839 }
840
841 for (auto nn : {PyStrings::gCppName, PyStrings::gName}) {
842 PyObject* tpName = PyObject_GetAttr(tn, nn);
843 if (tpName) {
845 PyUnicode_AsUTF8(tpName), /* enable_slow_lookup */ true);
846 if (!type) {
847 // any Python object has a __name__; one that does not name a C++
848 // type contributes no argument
849 Py_DECREF(tpName);
850 continue;
851 }
853 PyObject* value_int = PyNumber_Index(tn);
854 if (!value_int) {
855 types.push_back(type.data);
856 PyErr_Clear();
857 } else {
858 PyObject* pystr = PyObject_Str(tn);
859 std::string num = PyUnicode_AsUTF8(pystr);
860 types.push_back({type.data, strdup(num.c_str())});
861 Py_DECREF(pystr);
862 Py_DECREF(value_int);
863 }
864 } else {
865 types.push_back(type.data);
866 }
867 Py_DECREF(tpName);
868 return true;
869 }
870 PyErr_Clear();
871 }
872
873 if (PyLong_Check(tn) || PyFloat_Check(tn)) {
874 // last ditch attempt, works for things like int values; since this is a
875 // source of errors otherwise, it is limited to specific types and not
876 // generally used (str(obj) can print anything ...)
877 PyObject* pystr = PyObject_Str(tn);
878 std::string num = PyUnicode_AsUTF8(pystr);
879 if (num == "True")
880 num = "1";
881 else if (num == "False")
882 num = "0";
883 types.push_back({interop::GetType("int").data, strdup(num.c_str())});
884 Py_DECREF(pystr);
885 return true;
886 }
887
888 return false;
889}
890
891std::vector<Cpp::TemplateArgInfo>
893 PyObject* args, ArgPreference pref,
894 int argoff, int* pcnt) {
895 // Helper to construct the "<type, type, ...>" part of a templated name
896 // (either for a class or method lookup
897 bool justOne = !PyTuple_CheckExact(tpArgs);
898
899 // Note: directly appending to string is a lot faster than stringstream
900 std::vector<Cpp::TemplateArgInfo> types;
901 types.reserve(8);
902
903 if (pcnt)
904 *pcnt = 0; // count number of times 'pref' is used
905
906 Py_ssize_t nArgs = justOne ? 1 : PyTuple_GET_SIZE(tpArgs);
907 for (int i = argoff; i < nArgs; ++i) {
908 // add type as string to name
909 PyObject* tn = justOne ? tpArgs : PyTuple_GET_ITEM(tpArgs, i);
910 if (PyUnicode_Check(tn)) {
911 const char* tn_string = PyUnicode_AsUTF8(tn);
912
913 if (interop::AppendTypesSlow(tn_string, types)) {
914 PyErr_Format(PyExc_TypeError, "Cannot find Templated Arg: %s",
915 tn_string);
916 return {};
917 }
918
919 // some commmon numeric types (separated out for performance: checking for
920 // __cpp_name__ and/or __name__ is rather expensive)
921 } else {
922 if (!AddTypeName(types, tn, (args ? PyTuple_GET_ITEM(args, i) : nullptr),
923 pref, pcnt)) {
924 PyErr_SetString(
925 PyExc_TypeError,
926 "could not construct C++ name from provided template argument.");
927 return {};
928 }
929 }
930 }
931
932 return types;
933}
934
935std::string cpyrt::Utility::CT2CppNameS(PyObject* pytc, bool allow_voidp) {
936 // helper to convert ctypes' `_type_` info to the equivalent C++ name
937 const char* name = "";
938 if (PyUnicode_Check(pytc)) {
939 char tc = ((char*)PyUnicode_AsUTF8(pytc))[0];
940 switch (tc) {
941 case '?':
942 name = "bool";
943 break;
944 case 'c':
945 name = "char";
946 break;
947 case 'b':
948 name = "char";
949 break;
950 case 'B':
951 name = "unsigned char";
952 break;
953 case 'h':
954 name = "short";
955 break;
956 case 'H':
957 name = "unsigned short";
958 break;
959 case 'i':
960 name = "int";
961 break;
962 case 'I':
963 name = "unsigned int";
964 break;
965 case 'l':
966 name = "long";
967 break;
968 case 'L':
969 name = "unsigned long";
970 break;
971 case 'q':
972 name = "long long";
973 break;
974 case 'Q':
975 name = "unsigned long long";
976 break;
977 case 'f':
978 name = "float";
979 break;
980 case 'd':
981 name = "double";
982 break;
983 case 'g':
984 name = "long double";
985 break;
986 case 'z':
987 name = "const char*";
988 break;
989 default:
990 if (allow_voidp)
991 name = "void*";
992 break;
993 }
994 }
995
996 return name;
997}
998
999//----------------------------------------------------------------------------
1000static inline bool check_scope(const std::string& name) {
1002}
1003
1005 const std::string& retType, const std::vector<std::string>& argtypes,
1006 std::ostringstream& code) {
1007 // Generate function setup to be used in callbacks (wrappers and overrides).
1008 int nArgs = (int)argtypes.size();
1009
1010 // return value and argument type converters
1011 bool isVoid = retType.find("void") == 0; // might contain trailing space
1012 if (!isVoid)
1013 code << " CPYRT_STATIC std::unique_ptr<cppjit::cpyrt::Converter, "
1014 "std::function<void(cppjit::cpyrt::Converter*)>> "
1015 "retconv{cppjit::cpyrt::CreateConverter(\""
1016 << retType << "\"), cppjit::cpyrt::DestroyConverter};\n";
1017 std::vector<bool> arg_is_ptr;
1018 if (nArgs) {
1019 arg_is_ptr.resize(nArgs);
1020 code << " CPYRT_STATIC "
1021 "std::vector<std::unique_ptr<cppjit::cpyrt::Converter, "
1022 "std::function<void(cppjit::cpyrt::Converter*)>>> argcvs;\n"
1023 << " if (argcvs.empty()) {\n"
1024 << " argcvs.reserve(" << nArgs << ");\n";
1025 for (int i = 0; i < nArgs; ++i) {
1026 arg_is_ptr[i] = false;
1027 code << " argcvs.emplace_back(cppjit::cpyrt::CreateConverter(\"";
1028 const std::string& at = argtypes[i];
1029 const std::string& res_at = interop::ResolveName(at);
1030 const std::string& cpd = TypeManip::compound(res_at);
1031 if (!cpd.empty() && check_scope(res_at)) {
1032 // in case of a pointer, the original argument needs to be used to
1033 // ensure the pointer-value remains comparable
1034 //
1035 // in case of a reference, there is no extra indirection on the C++ side
1036 // as would be when converting a data member, so adjust the converter
1037 arg_is_ptr[i] = cpd.back() == '*';
1038 if (arg_is_ptr[i] || cpd.back() == '&') {
1039 code << res_at.substr(0, res_at.size() - 1);
1040 } else
1041 code << at;
1042 } else
1043 code << at;
1044 code << "\"), cppjit::cpyrt::DestroyConverter);\n";
1045 }
1046 code << " }\n";
1047 }
1048
1049 // declare return value (TODO: this does not work for most non-builtin values)
1050 if (!isVoid)
1051 code << " " << retType << " ret{};\n";
1052
1053 // build argument tuple if needed
1054 if (nArgs) {
1055 code << " std::vector<PyObject*> pyargs;\n";
1056 code << " pyargs.reserve(" << nArgs << ");\n"
1057 << " try {\n";
1058 for (int i = 0; i < nArgs; ++i) {
1059 code << " pyargs.emplace_back(argcvs[" << i
1060 << "]->FromMemory((void*)";
1061 if (!arg_is_ptr[i])
1062 code << '&';
1063 code << "arg" << i << "));\n"
1064 << " if (!pyargs.back()) throw " << i << ";\n";
1065 }
1066 code << " } catch(int) {\n"
1067 << " for (auto pyarg : pyargs) Py_XDECREF(pyarg);\n"
1068 << " cppjit::cpyrt::PyException pyexc; throw pyexc;\n"
1069 << " }\n";
1070 }
1071}
1072
1073void cpyrt::Utility::ConstructCallbackReturn(const std::string& retType,
1074 int nArgs,
1075 std::ostringstream& code) {
1076 // Generate code for return value conversion and error handling.
1077 bool isVoid = retType.find("void") == 0; // might contain trailing space
1078 bool isPtr = interop::ResolveName(retType).back() == '*';
1079
1080 if (nArgs)
1081 code << " for (auto pyarg : pyargs) Py_DECREF(pyarg);\n";
1082 code << " bool cOk = (bool)pyresult;\n"
1083 " if (pyresult) {\n";
1084 if (isPtr) {
1085 // If the return type is a CPPInstance, owned by Python, and the ref-count
1086 // down to 1, the return will hold a dangling pointer, so set it to nullptr
1087 // instead.
1088 code << " if (!cppjit::cpyrt::Instance_IsLively(pyresult))\n"
1089 " ret = nullptr;\n"
1090 " else {\n";
1091 }
1092 code << (isVoid ? ""
1093 : " cOk = retconv->ToMemory(pyresult, (void*)&ret);\n")
1094 << " Py_DECREF(pyresult);\n }\n";
1095 if (isPtr)
1096 code << " }\n";
1097 code << " if (!cOk) {" // assume error set when converter failed
1098// TODO: On Windows, throwing a C++ exception here makes the code hang; leave
1099// the error be which allows at least one layer of propagation
1100#ifdef _WIN32
1101 " /* do nothing */ }\n"
1102#else
1103 " cppjit::cpyrt::PyException pyexc; throw pyexc; }\n"
1104#endif
1105 " return";
1106 code << (isVoid ? ";\n }\n" : " ret;\n }\n");
1107}
1108
1109//----------------------------------------------------------------------------
1110static std::unordered_map<void*, PyObject*> sStdFuncLookup;
1111static std::unordered_map<std::string, PyObject*> sStdFuncMakerLookup;
1113 const std::string& signature,
1114 void* address) {
1115 // Convert a function pointer to an equivalent std::function<> object.
1116 static int maker_count = 0;
1117
1118 auto pf = sStdFuncLookup.find(address);
1119 if (pf != sStdFuncLookup.end()) {
1120 Py_INCREF(pf->second);
1121 return pf->second;
1122 }
1123
1124 PyObject* maker = nullptr;
1125
1126 auto pm = sStdFuncMakerLookup.find(retType + signature);
1127 if (pm == sStdFuncMakerLookup.end()) {
1128 std::ostringstream fname;
1129 fname << "ptr2func" << ++maker_count;
1130
1131 std::ostringstream code;
1132 code << "namespace __cppjit_internal { std::function<" << retType
1133 << signature << "> " << fname.str() << "(intptr_t faddr) { return ("
1134 << retType << "(*)" << signature << ")faddr;} }";
1135
1136 if (!interop::Compile(code.str())) {
1137 PyErr_SetString(PyExc_TypeError, "conversion to std::function failed");
1138 return nullptr;
1139 }
1140
1141 PyObject* pyscope = CreateScopeProxy("__cppjit_internal");
1142 maker = PyObject_GetAttrString(pyscope, fname.str().c_str());
1143 Py_DECREF(pyscope);
1144 if (!maker)
1145 return nullptr;
1146
1147 // cache the new maker (TODO: does it make sense to use weakrefs?)
1148 sStdFuncMakerLookup[retType + signature] = maker;
1149 } else
1150 maker = pm->second;
1151
1152 PyObject* args = PyTuple_New(1);
1153 PyTuple_SET_ITEM(args, 0, PyLong_FromLongLong((intptr_t)address));
1154 PyObject* func = PyObject_Call(maker, args, NULL);
1155 Py_DECREF(args);
1156
1157 if (func) { // prevent moving this func object, since then it can not be
1158 // reused
1159 ((CPPInstance*)func)->fFlags |= CPPInstance::kIsLValue;
1160 Py_INCREF(func); // TODO: use weak? The C++ maker doesn't go away either
1161 sStdFuncLookup[address] = func;
1162 }
1163
1164 return func;
1165}
1166
1167//----------------------------------------------------------------------------
1168bool cpyrt::Utility::InitProxy(PyObject* module, PyTypeObject* pytype,
1169 const char* name) {
1170 // Initialize a proxy class for use by python, and add it to the module.
1171
1172 // finalize proxy type
1173 if (PyType_Ready(pytype) < 0)
1174 return false;
1175
1176 // add proxy type to the given module
1177 Py_INCREF(pytype); // PyModule_AddObject steals reference
1178 if (PyModule_AddObject(module, (char*)name, (PyObject*)pytype) < 0) {
1179 Py_DECREF(pytype);
1180 return false;
1181 }
1182
1183 // declare success
1184 return true;
1185}
1186
1187//----------------------------------------------------------------------------
1188std::unordered_map<std::string, char> const& cpyrt::Utility::TypecodeMap() {
1189 // See https://docs.python.org/3/library/array.html#array.array
1190 static std::unordered_map<std::string, char> typecodeMap{
1191 {"char", 'b'}, {"unsigned char", 'B'},
1192#if PY_VERSION_HEX >= 0x030d0000
1193 {"Py_UCS4", 'w'},
1194#endif
1195 {"short", 'h'}, {"unsigned short", 'H'},
1196 {"int", 'i'}, {"unsigned int", 'I'},
1197 {"long", 'l'}, {"unsigned long", 'L'},
1198 {"long long", 'q'}, {"unsigned long long", 'Q'},
1199 {"float", 'f'}, {"double", 'd'}};
1200 return typecodeMap;
1201}
1202
1203namespace {
1204
1205// classification of PEP 3118 format characters
1206enum class EBufferKind {
1207 kSInt,
1208 kUInt,
1209 kFloat,
1210 kComplex,
1211 kChar,
1212 kWChar,
1213 kBool,
1214 kString,
1215 kVoidPtr,
1216 kUnknown
1217};
1218
1219struct BufferElem {
1220 EBufferKind kind;
1221 std::size_t size;
1222 char code; // the underlying format character
1223};
1224
1225BufferElem ClassifyFormatChar(char c) {
1226 switch (c) {
1227 case 'b':
1228 return {EBufferKind::kSInt, sizeof(char), c};
1229 case 'h':
1230 return {EBufferKind::kSInt, sizeof(short), c};
1231 case 'i':
1232 return {EBufferKind::kSInt, sizeof(int), c};
1233 case 'l':
1234 return {EBufferKind::kSInt, sizeof(long), c};
1235 case 'q':
1236 return {EBufferKind::kSInt, sizeof(long long), c};
1237 case 'n':
1238 return {EBufferKind::kSInt, sizeof(Py_ssize_t), c};
1239 case 'B':
1240 return {EBufferKind::kUInt, sizeof(unsigned char), c};
1241 case 'H':
1242 return {EBufferKind::kUInt, sizeof(unsigned short), c};
1243 case 'I':
1244 return {EBufferKind::kUInt, sizeof(unsigned int), c};
1245 case 'L':
1246 return {EBufferKind::kUInt, sizeof(unsigned long), c};
1247 case 'Q':
1248 return {EBufferKind::kUInt, sizeof(unsigned long long), c};
1249 case 'N':
1250 return {EBufferKind::kUInt, sizeof(std::size_t), c};
1251 case 'e':
1252 return {EBufferKind::kFloat, 2, c}; // IEEE 754 half
1253 case 'f':
1254 return {EBufferKind::kFloat, sizeof(float), c};
1255 case 'd':
1256 return {EBufferKind::kFloat, sizeof(double), c};
1257 case 'g':
1258 return {EBufferKind::kFloat, sizeof(long double), c};
1259 case '?':
1260 return {EBufferKind::kBool, sizeof(bool), c};
1261 case 'c':
1262 return {EBufferKind::kChar, sizeof(char), c};
1263 case 'u':
1264 return {EBufferKind::kWChar, sizeof(wchar_t), c};
1265 case 'w':
1266 return {EBufferKind::kWChar, sizeof(char32_t), c}; // UCS-4
1267 case 's':
1268 case 'p':
1269 return {EBufferKind::kString, 0, c};
1270 case 'P':
1271 return {EBufferKind::kVoidPtr, sizeof(void*), c};
1272 default:
1273 return {EBufferKind::kUnknown, 0, c};
1274 }
1275}
1276
1277// reduce a PEP 3118 format string to its element type, skipping an optional
1278// byte-order/alignment prefix ("<q") and repeat count ("10q"); fail for
1279// formats without a single scalar element (e.g. structured "T{...}" ones)
1280bool ParseBufferFormat(const char* fmt, BufferElem& elemOut) {
1281 if (!fmt)
1282 return false;
1283 // ctypes prefixes pointer-to-item formats with '&' ("&<b"); the item
1284 // format follows, and the item-size check in GetBuffer guards the size
1285 if (*fmt == '&')
1286 ++fmt;
1287 // strchr also matches on the terminating NUL, so must check for the empty
1288 // string first
1289 if (*fmt && strchr("@=<>!^", *fmt))
1290 ++fmt;
1291 while ('0' <= *fmt && *fmt <= '9')
1292 ++fmt;
1293 bool isComplex = *fmt == 'Z';
1294 if (isComplex)
1295 ++fmt;
1296 if (!*fmt || fmt[1] != '\0')
1297 return false;
1298
1299 BufferElem elem = ClassifyFormatChar(*fmt);
1300 if (isComplex) {
1301 if (elem.kind != EBufferKind::kFloat)
1302 return false;
1303 elem = {EBufferKind::kComplex, 2 * elem.size, *fmt};
1304 }
1305 elemOut = elem;
1306 return true;
1307}
1308
1309} // namespace
1310
1311//----------------------------------------------------------------------------
1312bool cpyrt::Utility::BufferFormatCompatible(char tc, const char* fmt) {
1313 // the requested type code: a PEP 3118 character, or a cppjit-internal
1314 // code ('z' = std::complex<float>, 'Z' = std::complex<double>)
1315 BufferElem want;
1316 if (tc == 'z')
1317 want = {EBufferKind::kComplex, 2 * sizeof(float), tc};
1318 else if (tc == 'Z')
1319 want = {EBufferKind::kComplex, 2 * sizeof(double), tc};
1320 else
1321 want = ClassifyFormatChar(tc);
1322 if (want.kind == EBufferKind::kUnknown)
1323 return false;
1324
1325 BufferElem got;
1326 if (!ParseBufferFormat(fmt, got) || got.kind == EBufferKind::kUnknown)
1327 return false;
1328
1329 // the struct/array modules express bool-ish values as 'signed char' ('b')
1330 if (want.kind == EBufferKind::kBool && got.code == 'b')
1331 return true;
1332
1333 // 's' and 'p' are both strings but not interchangeable
1334 if (want.kind == EBufferKind::kString)
1335 return got.code == want.code;
1336
1337 return want.kind == got.kind && want.size == got.size;
1338}
1339
1340//----------------------------------------------------------------------------
1341Py_ssize_t cpyrt::Utility::GetBuffer(PyObject* pyobject, char tc, int size,
1342 void*& buf, bool check) {
1343 // Retrieve a linear buffer pointer from the given pyobject.
1344
1345 // special case: don't handle character strings here (yes, they're buffers,
1346 // but not quite)
1347 if (PyBytes_Check(pyobject) || PyUnicode_Check(pyobject))
1348 return 0;
1349
1350 // special case: bytes array
1351 if ((!check || tc == '*' || tc == 'B') && PyByteArray_CheckExact(pyobject)) {
1352 buf = PyByteArray_AS_STRING(pyobject);
1353 return PyByteArray_GET_SIZE(pyobject);
1354 }
1355
1356 // new-style buffer interface
1357 if (PyObject_CheckBuffer(pyobject)) {
1358 if (PySequence_Check(pyobject) && !PySequence_Size(pyobject))
1359 return 0; // PyObject_GetBuffer() crashes on some platforms for some
1360 // zero-sized seqeunces
1361 if (PyErr_Occurred()) {
1362 // PySequence_Size errored with
1363 // TypeError: object of type 'LP_c_type' has no len()
1364 PyErr_Clear();
1365 }
1366
1367 Py_buffer bufinfo;
1368 memset(&bufinfo, 0, sizeof(Py_buffer));
1369 if (PyObject_GetBuffer(pyobject, &bufinfo, PyBUF_FORMAT) == 0) {
1370 if (tc == '*' || BufferFormatCompatible(tc, bufinfo.format)) {
1371 buf = bufinfo.buf;
1372
1373 if (check && bufinfo.itemsize != size) {
1374 PyErr_Format(
1375 PyExc_TypeError,
1376 "buffer itemsize (%ld) does not match expected size (%d)",
1377 bufinfo.itemsize, size);
1378 PyBuffer_Release(&bufinfo);
1379 return 0;
1380 }
1381
1382 Py_ssize_t buflen = 0;
1383 if (buf && bufinfo.ndim == 0)
1384 buflen = bufinfo.len / bufinfo.itemsize;
1385 else if (buf && bufinfo.ndim == 1)
1386 buflen =
1387 bufinfo.shape ? bufinfo.shape[0] : bufinfo.len / bufinfo.itemsize;
1388 PyBuffer_Release(&bufinfo);
1389 if (buflen)
1390 return buflen;
1391 } else {
1392 // have buf, but format mismatch: bail out now, otherwise the old
1393 // code will return based on itemsize match
1394 PyBuffer_Release(&bufinfo);
1395 return 0;
1396 }
1397 } else if (bufinfo.obj)
1398 PyBuffer_Release(&bufinfo);
1399 PyErr_Clear();
1400 }
1401
1402 // attempt to retrieve pointer through old-style buffer interface
1403 PyBufferProcs* bufprocs = Py_TYPE(pyobject)->tp_as_buffer;
1404
1405 PySequenceMethods* seqmeths = Py_TYPE(pyobject)->tp_as_sequence;
1406 if (seqmeths != 0 && bufprocs != 0 && bufprocs->bf_getbuffer != 0) {
1407
1408 // get the buffer
1409 Py_buffer bufinfo;
1410 (*(bufprocs->bf_getbuffer))(pyobject, &bufinfo, PyBUF_WRITABLE);
1411 buf = (char*)bufinfo.buf;
1412 Py_ssize_t buflen = bufinfo.len;
1413 PyBuffer_Release(&bufinfo);
1414
1415 if (buf && check == true) {
1416 // determine buffer compatibility (use "buf" as a status flag)
1417 PyObject* pytc = tc != '*'
1418 ? PyObject_GetAttr(pyobject, PyStrings::gTypeCode)
1419 : nullptr;
1420 if (pytc != 0) { // for array objects
1421 char cpytc = PyUnicode_AsUTF8(pytc)[0];
1422 if (!(cpytc == tc || (tc == '?' && cpytc == 'b')))
1423 buf = 0; // no match
1424 Py_DECREF(pytc);
1425 } else if (seqmeths->sq_length &&
1426 (int)(buflen / (*(seqmeths->sq_length))(pyobject)) == size) {
1427 // this is a gamble ... may or may not be ok, but that's for the user
1428 PyErr_Clear();
1429 } else if (buflen == size) {
1430 // also a gamble, but at least 1 item will fit into the buffer, so very
1431 // likely ok ...
1432 PyErr_Clear();
1433 } else {
1434 buf = 0; // not compatible
1435
1436 // clarify error message
1437 auto error = FetchPyError();
1438 PyObject* pyvalue2 = PyUnicode_FromFormat(
1439 (char*)"%s and given element size (%ld) do not match needed (%d)",
1440 PyUnicode_AsUTF8(error.fValue.get()),
1441 seqmeths->sq_length
1442 ? (long)(buflen / (*(seqmeths->sq_length))(pyobject))
1443 : (long)buflen,
1444 size);
1445 error.fValue.reset(pyvalue2);
1446 RestorePyError(error);
1447 }
1448 }
1449
1450 if (!buf)
1451 return 0;
1452 return buflen / (size ? size : 1);
1453 }
1454
1455 return 0;
1456}
1457
1458//----------------------------------------------------------------------------
1459std::string cpyrt::Utility::MapOperatorName(const std::string& name,
1460 bool bTakesParams, bool* stubbed) {
1461 // Map the given C++ operator name on the python equivalent.
1462 if (8 < name.size() && name.substr(0, 8) == "operator") {
1463 std::string op = name.substr(8, std::string::npos);
1464
1465 // stripping ...
1466 std::string::size_type start = 0, end = op.size();
1467 while (start < end && isspace(op[start]))
1468 ++start;
1469 while (start < end && isspace(op[end - 1]))
1470 --end;
1471 op = op.substr(start, end - start);
1472
1473 // certain operators should be removed completely (e.g. operator delete &
1474 // friends)
1475 if (gOpRemove.find(op) != gOpRemove.end())
1476 return "";
1477
1478 TC2POperatorMapping_t::iterator pop = gC2POperatorMapping.find(op);
1479 // map C++ operator to python equivalent, or made up name if no equivalent
1480 // exists
1481 if (pop != gC2POperatorMapping.end()) {
1482 return pop->second;
1483
1484 } else if (op == "*") {
1485 // dereference v.s. multiplication of two instances
1486 if (!bTakesParams)
1487 return "__deref__";
1488 if (stubbed)
1489 *stubbed = true;
1490 return "__mul__";
1491
1492 } else if (op == "/") {
1493 // no unary, but is stubbed
1494 return "__truediv__";
1495
1496 } else if (op == "+") {
1497 // unary positive v.s. addition of two instances
1498 if (!bTakesParams)
1499 return "__pos__";
1500 if (stubbed)
1501 *stubbed = true;
1502 return "__add__";
1503
1504 } else if (op == "-") {
1505 // unary negative v.s. subtraction of two instances
1506 if (!bTakesParams)
1507 return "__neg__";
1508 if (stubbed)
1509 *stubbed = true;
1510 return "__sub__";
1511
1512 } else if (op == "++") {
1513 // prefix v.s. postfix increment
1514 return bTakesParams ? "__postinc__" : "__preinc__";
1515
1516 } else if (op == "--") {
1517 // prefix v.s. postfix decrement
1518 return bTakesParams ? "__postdec__" : "__predec__";
1519 }
1520 }
1521
1522 // might get here, as not all operator methods are handled (new, delete, etc.)
1523 return name;
1524}
1525
1526//----------------------------------------------------------------------------
1528 // Retrieve the class name from the given Python instance.
1529 std::string clname = "<unknown>";
1530 PyObject* pyclass = (PyObject*)Py_TYPE(pyobj);
1531 PyObject* pyname = PyObject_GetAttr(pyclass, PyStrings::gCppName);
1532 if (!pyname) {
1533 PyErr_Clear();
1534 pyname = PyObject_GetAttr(pyclass, PyStrings::gName);
1535 }
1536
1537 if (pyname) {
1538 clname = PyUnicode_AsUTF8(pyname);
1539 Py_DECREF(pyname);
1540 } else
1541 PyErr_Clear();
1542 return clname;
1543}
1544
1545//----------------------------------------------------------------------------
1546static std::set<std::string> sIteratorTypes;
1547bool cpyrt::Utility::IsSTLIterator(const std::string& classname) {
1548 // attempt to recognize STL iterators (TODO: probably belongs in the backend),
1549 // using a couple of common container classes with different iterator
1550 // protocols (note that mapping iterators are handled separately in the
1551 // pythonizations) as exemplars (the actual, resolved, names will be
1552 // compiler-specific) that are picked b/c they are baked into the CoreLegacy
1553 // dictionary
1554 if (sIteratorTypes.empty()) {
1555 std::string tt = "<int>::";
1556 for (auto c : {"std::vector", "std::list", "std::deque"}) {
1557 for (auto i : {"iterator", "const_iterator"}) {
1558 const std::string& itname = interop::ResolveName(c + tt + i);
1559 auto pos = itname.find('<');
1560 if (pos != std::string::npos)
1561 sIteratorTypes.insert(itname.substr(0, pos));
1562 }
1563 }
1564 }
1565
1566 auto pos = classname.find('<');
1567 if (pos != std::string::npos)
1568 return sIteratorTypes.find(classname.substr(0, pos)) !=
1569 sIteratorTypes.end();
1570 return false;
1571}
1572
1573//----------------------------------------------------------------------------
1575 Py_XDECREF(fEq);
1576 Py_XDECREF(fNe);
1577 Py_XDECREF(fLAdd);
1578 Py_XDECREF(fRAdd);
1579 Py_XDECREF(fSub);
1580 Py_XDECREF(fLMul);
1581 Py_XDECREF(fRMul);
1582 Py_XDECREF(fDiv);
1583 Py_XDECREF(fHash);
1584}
1585
1586//----------------------------------------------------------------------------
1588 // Re-acquire the GIL before calling PyErr_Occurred() in case it has been
1589 // released; note that the p2.2 code assumes that there are no callbacks in
1590 // C++ to python (or at least none returning errors).
1591 PythonGILRAII python_gil_raii;
1592 PyObject* e = PyErr_Occurred();
1593
1594 return e;
1595}
1596
1597//----------------------------------------------------------------------------
1599 // create a PyError_t RAII object that will capture and store the exception
1600 // data
1602#if PY_VERSION_HEX >= 0x030c0000
1603 error.fValue.reset(PyErr_GetRaisedException());
1604#else
1605 PyObject* pytype = nullptr;
1606 PyObject* pyvalue = nullptr;
1607 PyObject* pytrace = nullptr;
1608 PyErr_Fetch(&pytype, &pyvalue, &pytrace);
1609 error.fType.reset(pytype);
1610 error.fValue.reset(pyvalue);
1611 error.fTrace.reset(pytrace);
1612#endif
1613 return error;
1614}
1615
1616//----------------------------------------------------------------------------
1618#if PY_VERSION_HEX >= 0x030c0000
1619 PyErr_SetRaisedException(error.fValue.release());
1620#else
1621 PyErr_Restore(error.fType.release(), error.fValue.release(),
1622 error.fTrace.release());
1623#endif
1624}
1625
1626//----------------------------------------------------------------------------
1627size_t cpyrt::Utility::FetchError(std::vector<PyError_t>& errors, bool is_cpp) {
1628 // Fetch the current python error, if any, and store it for future use.
1629 if (PyErr_Occurred()) {
1630 errors.emplace_back(FetchPyError());
1631 errors.back().fIsCpp = is_cpp;
1632 }
1633 return errors.size();
1634}
1635
1636//----------------------------------------------------------------------------
1637void cpyrt::Utility::SetDetailedException(std::vector<PyError_t>&& errors,
1638 PyObject* topmsg, PyObject* defexc) {
1639 // Use the collected exceptions to build up a detailed error log.
1640 if (errors.empty()) {
1641 // should not happen ...
1642 PyErr_SetString(defexc, PyUnicode_AsUTF8(topmsg));
1643 Py_DECREF(topmsg);
1644 return;
1645 }
1646
1647 // if a _single_ exception was thrown from C++, assume it has priority (see
1648 // below)
1649 PyError_t* unique_from_cpp = nullptr;
1650 for (auto& e : errors) {
1651 if (e.fIsCpp) {
1652 if (!unique_from_cpp)
1653 unique_from_cpp = &e;
1654 else {
1655 // two C++ exceptions, resort to default behavior
1656 unique_from_cpp = nullptr;
1657 break;
1658 }
1659 }
1660 }
1661
1662 if (unique_from_cpp) {
1663 // report only this error; the idea here is that all other errors come from
1664 // the bindings (e.g. argument conversion errors), while the exception from
1665 // C++ means that it originated from an otherwise successful call
1666
1667 // bind the original C++ object, rather than constructing from topmsg, as it
1668 // is expected to have informative state
1669 RestorePyError(*unique_from_cpp);
1670 } else {
1671 // try to consolidate Python exceptions, otherwise select default
1672 PyObject* exc_type = nullptr;
1673 for (auto& e : errors) {
1674#if PY_VERSION_HEX >= 0x030c0000
1675 PyObject* pytype = (PyObject*)Py_TYPE(e.fValue.get());
1676#else
1677 PyObject* pytype = e.fType.get();
1678#endif
1679 if (!exc_type)
1680 exc_type = pytype;
1681 else if (exc_type != pytype) {
1682 exc_type = defexc;
1683 break;
1684 }
1685 }
1686
1687 // add the details to the topmsg
1688 PyObject* separator = PyUnicode_FromString("\n ");
1689 for (auto& e : errors) {
1690 PyObject* pyvalue = e.fValue.get();
1691 PyUnicode_Append(&topmsg, separator);
1692 if (PyUnicode_Check(pyvalue)) {
1693 PyUnicode_Append(&topmsg, pyvalue);
1694 } else if (pyvalue) {
1695 PyObject* excstr = PyObject_Str(pyvalue);
1696 if (!excstr) {
1697 PyErr_Clear();
1698 excstr = PyObject_Str((PyObject*)Py_TYPE(pyvalue));
1699 }
1700 PyUnicode_AppendAndDel(&topmsg, excstr);
1701 } else {
1702 PyUnicode_AppendAndDel(&topmsg,
1703 PyUnicode_FromString("unknown exception"));
1704 }
1705 }
1706
1707 Py_DECREF(separator);
1708
1709 // set the python exception
1710 PyErr_SetString(exc_type, PyUnicode_AsUTF8(topmsg));
1711 }
1712
1713 Py_DECREF(topmsg);
1714}
1715
1716//----------------------------------------------------------------------------
1717static bool includesDone = false;
1719 // setup Python API for callbacks
1720 if (!includesDone) {
1721 bool okay = interop::Compile(
1722 // basic API (converters etc.)
1723 "#include \"cpyrt/API.h\"\n"
1724
1725 // utilities from the cpyrt public API
1726 "#include \"cpyrt/DispatchPtr.h\"\n"
1727 "#include \"cpyrt/PyException.h\"\n");
1728 includesDone = okay;
1729 }
1730
1731 return includesDone;
1732}
_object PyObject
#define c(i)
Definition RSha256.hxx:101
#define e(i)
Definition RSha256.hxx:103
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t 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 cname
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
@ kUnknown
Definition TStructNode.h:19
static std::set< std::string > sIteratorTypes
Definition Utility.cxx:1546
static bool includesDone
Definition Utility.cxx:1717
static std::unordered_map< void *, PyObject * > sStdFuncLookup
Definition Utility.cxx:1110
static cpyrt::PyCallable * BuildOperator(const std::string &lcname, const std::string &rcname, const char *op, interop::TCppScope_t scope, bool reverse=false)
Definition Utility.cxx:258
static std::unordered_map< std::string, PyObject * > sStdFuncMakerLookup
Definition Utility.cxx:1111
static TC2POperatorMapping_t gC2POperatorMapping
Definition Utility.cxx:29
static bool AddTypeName(std::string &tmpl_name, PyObject *tn, PyObject *arg, cpyrt::Utility::ArgPreference pref, int *pcnt=nullptr)
Definition Utility.cxx:425
static std::set< std::string > gOpRemove
Definition Utility.cxx:31
std::unordered_map< std::string, std::string > TC2POperatorMapping_t
Definition Utility.cxx:28
static std::set< std::string > gOpSkip
Definition Utility.cxx:30
static bool check_scope(const std::string &name)
Definition Utility.cxx:1000
static std::string AnnotationAsText(PyObject *pyobj)
Definition Utility.cxx:410
void AdoptMethod(PyCallable *pc)
interop::TCppScope_t fCppType
Definition CPPScope.h:62
unsigned long long PY_ULONG_LONG
long long PY_LONG_LONG
std::string clean_type(const std::string &cppname, bool template_strip=true, bool const_strip=true)
std::string ConstructTemplateArgs(PyObject *pyname, PyObject *tpArgs, PyObject *args=nullptr, ArgPreference=kNone, int argoff=0, int *pcnt=nullptr)
Definition Utility.cxx:640
bool BufferFormatCompatible(char tc, const char *fmt)
Definition Utility.cxx:1312
std::string ClassName(PyObject *pyobj)
Definition Utility.cxx:1527
void ConstructCallbackPreamble(const std::string &retType, const std::vector< std::string > &argtypes, std::ostringstream &code)
Definition Utility.cxx:1004
std::string CT2CppNameS(PyObject *pytc, bool allow_voidp)
Definition Utility.cxx:935
void RestorePyError(PyError_t &error)
Definition Utility.cxx:1617
void ConstructCallbackReturn(const std::string &retType, int nArgs, std::ostringstream &code)
Definition Utility.cxx:1073
size_t FetchError(std::vector< PyError_t > &, bool is_cpp=false)
Definition Utility.cxx:1627
PyCallable * FindBinaryOperator(PyObject *left, PyObject *right, const char *op, interop::TCppScope_t scope=interop::TCppScope_t{})
Definition Utility.cxx:293
bool IsSTLIterator(const std::string &classname)
Definition Utility.cxx:1547
std::vector< Cpp::TemplateArgInfo > GetTemplateArgsTypes(PyObject *scope, PyObject *tpArgs, PyObject *args=nullptr, ArgPreference=kNone, int argoff=0, int *pcnt=nullptr)
Definition Utility.cxx:892
bool AddToClass(PyObject *pyclass, const char *label, PyCFunction cfunc, int flags=METH_VARARGS)
Definition Utility.cxx:178
PyObject * PyErr_Occurred_WithGIL()
Definition Utility.cxx:1587
bool InitProxy(PyObject *module, PyTypeObject *pytype, const char *name)
Definition Utility.cxx:1168
void SetDetailedException(std::vector< PyError_t > &&errors, PyObject *topmsg, PyObject *defexc)
Definition Utility.cxx:1637
Py_ssize_t GetBuffer(PyObject *pyobject, char tc, int size, void *&buf, bool check=true)
Definition Utility.cxx:1341
std::unordered_map< std::string, char > const & TypecodeMap()
Definition Utility.cxx:1188
std::string MapOperatorName(const std::string &name, bool bTakesParames, bool *stubbed=nullptr)
Definition Utility.cxx:1459
PyObject * FuncPtr2StdFunction(const std::string &retType, const std::string &signature, void *address)
Definition Utility.cxx:1112
PyCallable * FindUnaryOperator(PyObject *pyclass, const char *op)
Definition Utility.cxx:276
PyError_t FetchPyError()
Definition Utility.cxx:1598
bool CPPOverload_Check(T *object)
Definition CPPOverload.h:94
PyObject * CustomInstanceMethod_New(PyObject *func, PyObject *self, PyObject *)
PyObject * gDefaultObject
PyObject * gNullPtrObject
PyObject * CreateScopeProxy(PyObject *, PyObject *args)
unsigned long PyLongOrInt_AsULong(PyObject *pyobject)
Definition Utility.cxx:125
bool CPPScope_Check(T *object)
Definition CPPScope.h:86
bool CPPInstance_Check(T *object)
CPPOverload * CPPOverload_New(const std::string &name, std::vector< PyCallable * > &methods)
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)
TCppType_t GetTypeFromScope(TCppScope_t klass)
TCppMethod_t GetMethodTemplate(TCppScope_t scope, const std::string &name, const std::string &proto)
std::string ResolveName(const std::string &cppitem_name)
bool AppendTypesSlow(const std::string &name, std::vector< Cpp::TemplateArgInfo > &types, TCppScope_t parent=nullptr)
TCppType_t GetReferencedType(TCppType_t type, bool rvalue=false)
Cpp::FuncRef TCppMethod_t
Definition API.h:44
TCppMethod_t GetGlobalOperator(TCppScope_t scope, const std::string &lc, const std::string &rc, const std::string &op)
TCppScope_t GetGlobalScope()
bool Compile(const std::string &code, bool silent=false)
TCppType_t GetPointerType(TCppType_t type)
TCppScope_t GetScope(const std::string &scope_name, TCppScope_t parent_scope=TCppScope_t{})
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
std::unique_ptr< PyObject, PyObjectDeleter > fType
Definition Utility.h:126
std::unique_ptr< PyObject, PyObjectDeleter > fValue
Definition Utility.h:129
std::unique_ptr< PyObject, PyObjectDeleter > fTrace
Definition Utility.h:127
auto * tt
Definition textangle.C:16