Logo ROOT  
Reference Guide
Utility.cxx
Go to the documentation of this file.
1// Bindings
2#include "CPyCppyy.h"
3#include "Utility.h"
4#include "CPPFunction.h"
5#include "CPPInstance.h"
6#include "CPPOverload.h"
7#include "ProxyWrappers.h"
8#include "PyCallable.h"
9#include "PyStrings.h"
10#include "CustomPyTypes.h"
11#include "TemplateProxy.h"
12#include "TypeManip.h"
13
14// Standard
15#include <limits.h>
16#include <string.h>
17#include <algorithm>
18#include <list>
19#include <mutex>
20#include <set>
21#include <sstream>
22#include <utility>
23
24
25//- data _____________________________________________________________________
28
29typedef std::map<std::string, std::string> TC2POperatorMapping_t;
31static std::set<std::string> gOpSkip;
32static std::set<std::string> gOpRemove;
33
34namespace {
35
36 using namespace CPyCppyy::Utility;
37
38 struct InitOperatorMapping_t {
39 public:
40 InitOperatorMapping_t() {
41 // Initialize the global map of operator names C++ -> python.
42
43 gOpSkip.insert("[]"); // __s/getitem__, depends on return type
44 gOpSkip.insert("+"); // __add__, depends on # of args (see __pos__)
45 gOpSkip.insert("-"); // __sub__, id. (eq. __neg__)
46 gOpSkip.insert("*"); // __mul__ or __deref__
47 gOpSkip.insert("++"); // __postinc__ or __preinc__
48 gOpSkip.insert("--"); // __postdec__ or __predec__
49
50 gOpRemove.insert("new"); // this and the following not handled at all
51 gOpRemove.insert("new[]");
52 gOpRemove.insert("delete");
53 gOpRemove.insert("delete[]");
54
55 gC2POperatorMapping["[]"] = "__getitem__";
56 gC2POperatorMapping["()"] = "__call__";
58 gC2POperatorMapping["%"] = "__mod__";
59 gC2POperatorMapping["**"] = "__pow__";
60 gC2POperatorMapping["<<"] = "__lshift__";
61 gC2POperatorMapping[">>"] = "__rshift__";
62 gC2POperatorMapping["&"] = "__and__";
63 gC2POperatorMapping["&&"] = "__dand__";
64 gC2POperatorMapping["|"] = "__or__";
65 gC2POperatorMapping["||"] = "__dor__";
66 gC2POperatorMapping["^"] = "__xor__";
67 gC2POperatorMapping["~"] = "__invert__";
68 gC2POperatorMapping[","] = "__comma__";
69 gC2POperatorMapping["+="] = "__iadd__";
70 gC2POperatorMapping["-="] = "__isub__";
71 gC2POperatorMapping["*="] = "__imul__";
73 gC2POperatorMapping["%="] = "__imod__";
74 gC2POperatorMapping["**="] = "__ipow__";
75 gC2POperatorMapping["<<="] = "__ilshift__";
76 gC2POperatorMapping[">>="] = "__irshift__";
77 gC2POperatorMapping["&="] = "__iand__";
78 gC2POperatorMapping["|="] = "__ior__";
79 gC2POperatorMapping["^="] = "__ixor__";
80 gC2POperatorMapping["=="] = "__eq__";
81 gC2POperatorMapping["!="] = "__ne__";
82 gC2POperatorMapping[">"] = "__gt__";
83 gC2POperatorMapping["<"] = "__lt__";
84 gC2POperatorMapping[">="] = "__ge__";
85 gC2POperatorMapping["<="] = "__le__";
86
87 // the following type mappings are "exact"
88 gC2POperatorMapping["const char*"] = "__str__";
89 gC2POperatorMapping["char*"] = "__str__";
90 gC2POperatorMapping["const char *"] = gC2POperatorMapping["const char*"];
91 gC2POperatorMapping["char *"] = gC2POperatorMapping["char*"];
92 gC2POperatorMapping["int"] = "__int__";
94 gC2POperatorMapping["double"] = "__float__";
95
96 // the following type mappings are "okay"; the assumption is that they
97 // are not mixed up with the ones above or between themselves (and if
98 // they are, that it is done consistently)
99 gC2POperatorMapping["short"] = "__int__";
100 gC2POperatorMapping["unsigned short"] = "__int__";
101 gC2POperatorMapping["unsigned int"] = CPPYY__long__;
102 gC2POperatorMapping["unsigned long"] = CPPYY__long__;
103 gC2POperatorMapping["long long"] = CPPYY__long__;
104 gC2POperatorMapping["unsigned long long"] = CPPYY__long__;
105 gC2POperatorMapping["float"] = "__float__";
106
107 gC2POperatorMapping["->"] = "__follow__"; // not an actual python operator
108 gC2POperatorMapping["="] = "__assign__"; // id.
109
110#if PY_VERSION_HEX < 0x03000000
111 gC2POperatorMapping["bool"] = "__nonzero__";
112#else
113 gC2POperatorMapping["bool"] = "__bool__";
114#endif
115 }
116 } initOperatorMapping_;
117
118// TODO: this should live with Helpers
119 inline void RemoveConst(std::string& cleanName) {
120 std::string::size_type spos = std::string::npos;
121 while ((spos = cleanName.find("const")) != std::string::npos) {
122 cleanName.swap(cleanName.erase(spos, 5));
123 }
124 }
125
126} // unnamed namespace
127
128
129//- public functions ---------------------------------------------------------
131{
132// Convert <pybject> to C++ unsigned long, with bounds checking, allow int -> ulong.
133 unsigned long ul = PyLong_AsUnsignedLong(pyobject);
134 if (PyErr_Occurred() && PyInt_Check(pyobject)) {
135 PyErr_Clear();
136 long i = PyInt_AS_LONG(pyobject);
137 if (0 <= i) {
138 ul = (unsigned long)i;
139 } else {
140 PyErr_SetString(PyExc_ValueError,
141 "can\'t convert negative value to unsigned long");
142 return (unsigned long)-1;
143 }
144 }
145
146 return ul;
147}
148
149//----------------------------------------------------------------------------
151{
152// Convert <pyobject> to C++ unsigned long long, with bounds checking.
153 ULong64_t ull = PyLong_AsUnsignedLongLong(pyobject);
154 if (PyErr_Occurred() && PyInt_Check(pyobject)) {
155 PyErr_Clear();
156 long i = PyInt_AS_LONG(pyobject);
157 if (0 <= i) {
158 ull = (ULong64_t)i;
159 } else {
160 PyErr_SetString(PyExc_ValueError,
161 "can\'t convert negative value to unsigned long long");
162 }
163 }
164
165 return ull;
166}
167
168//----------------------------------------------------------------------------
170 PyObject* pyclass, const char* label, PyCFunction cfunc, int flags)
171{
172// Add the given function to the class under name 'label'.
173
174// use list for clean-up (.so's are unloaded only at interpreter shutdown)
175 static std::list<PyMethodDef> s_pymeths;
176
177 s_pymeths.push_back(PyMethodDef());
178 PyMethodDef* pdef = &s_pymeths.back();
179 pdef->ml_name = const_cast<char*>(label);
180 pdef->ml_meth = cfunc;
181 pdef->ml_flags = flags;
182 pdef->ml_doc = nullptr;
183
184 PyObject* func = PyCFunction_New(pdef, nullptr);
186 PyObject* method = CustomInstanceMethod_New(func, nullptr, pyclass);
187 bool isOk = PyType_Type.tp_setattro(pyclass, name, method) == 0;
188 Py_DECREF(method);
189 Py_DECREF(name);
190 Py_DECREF(func);
191
192 if (PyErr_Occurred())
193 return false;
194
195 if (!isOk) {
196 PyErr_Format(PyExc_TypeError, "could not add method %s", label);
197 return false;
198 }
199
200 return true;
201}
202
203//----------------------------------------------------------------------------
204bool CPyCppyy::Utility::AddToClass(PyObject* pyclass, const char* label, const char* func)
205{
206// Add the given function to the class under name 'label'.
207 PyObject* pyfunc = PyObject_GetAttrString(pyclass, const_cast<char*>(func));
208 if (!pyfunc)
209 return false;
210
211 PyObject* pylabel = CPyCppyy_PyText_InternFromString(const_cast<char*>(label));
212 bool isOk = PyType_Type.tp_setattro(pyclass, pylabel, pyfunc) == 0;
213 Py_DECREF(pylabel);
214
215 Py_DECREF(pyfunc);
216 return isOk;
217}
218
219//----------------------------------------------------------------------------
220bool CPyCppyy::Utility::AddToClass(PyObject* pyclass, const char* label, PyCallable* pyfunc)
221{
222// Add the given function to the class under name 'label'.
223 CPPOverload* method =
224 (CPPOverload*)PyObject_GetAttrString(pyclass, const_cast<char*>(label));
225
226 if (!method || !CPPOverload_Check(method)) {
227 // not adding to existing CPPOverload; add callable directly to the class
228 if (PyErr_Occurred())
229 PyErr_Clear();
230 Py_XDECREF((PyObject*)method);
231 method = CPPOverload_New(label, pyfunc);
232 PyObject* pylabel = CPyCppyy_PyText_InternFromString(const_cast<char*>(label));
233 bool isOk = PyType_Type.tp_setattro(pyclass, pylabel, (PyObject*)method) == 0;
234 Py_DECREF(pylabel);
235 Py_DECREF(method);
236 return isOk;
237 }
238
239 method->AdoptMethod(pyfunc);
240
241 Py_DECREF(method);
242 return true;
243}
244
245
246//----------------------------------------------------------------------------
247static inline
248CPyCppyy::PyCallable* BuildOperator(const std::string& lcname, const std::string& rcname,
249 const char* op, Cppyy::TCppScope_t scope, bool reverse=false)
250{
251// Helper to find a function with matching signature in 'funcs'.
252 std::string opname = "operator";
253 opname += op;
254
255 Cppyy::TCppIndex_t idx = Cppyy::GetGlobalOperator(scope, lcname, rcname, opname);
256 if (idx == (Cppyy::TCppIndex_t)-1)
257 return nullptr;
258
259 Cppyy::TCppMethod_t meth = Cppyy::GetMethod(scope, idx);
260 if (!reverse)
261 return new CPyCppyy::CPPFunction(scope, meth);
262 return new CPyCppyy::CPPReverseBinary(scope, meth);
263}
264
265//----------------------------------------------------------------------------
267{
268// Find a callable matching named operator (op) and klass arguments in the global
269// namespace or the klass' namespace.
270 if (!CPPScope_Check(pyclass))
271 return nullptr;
272
273 CPPClass* klass = (CPPClass*)pyclass;
274 const std::string& lcname = Cppyy::GetScopedFinalName(klass->fCppType);
276 return FindBinaryOperator(lcname, "", op, scope, false);
277}
278
279//----------------------------------------------------------------------------
281 const char* op, Cppyy::TCppScope_t scope)
282{
283// Find a callable matching the named operator (op) and the (left, right)
284// arguments in the global or these objects' namespaces.
285
286 bool reverse = false;
287 if (!CPPInstance_Check(left)) {
288 if (CPPInstance_Check(right))
289 reverse = true;
290 else
291 return nullptr;
292 }
293
294// retrieve the class names to match the signature of any found global functions
295 const std::string& lcname = ClassName(left);
296 const std::string& rcname = ClassName(right);
297 return FindBinaryOperator(lcname, rcname, op, scope, reverse);
298}
299
300//----------------------------------------------------------------------------
302 const std::string& lcname, const std::string& rcname,
303 const char* op, Cppyy::TCppScope_t scope, bool reverse)
304{
305// Find a global function with a matching signature; search __gnu_cxx, std::__1,
306// and __cppyy_internal pro-actively (as there's AFAICS no way to unearth 'using'
307// information).
308
309 if (rcname == "<unknown>" || lcname == "<unknown>")
310 return nullptr;
311
312 PyCallable* pyfunc = 0;
313
314 const std::string& lnsname = TypeManip::extract_namespace(lcname);
315 if (!scope) scope = Cppyy::GetScope(lnsname);
316 if (scope)
317 pyfunc = BuildOperator(lcname, rcname, op, scope, reverse);
318
319 if (!pyfunc && scope != Cppyy::gGlobalScope) // search in global scope anyway
320 pyfunc = BuildOperator(lcname, rcname, op, Cppyy::gGlobalScope, reverse);
321
322 if (!pyfunc) {
323 // For GNU on clang, search the internal __gnu_cxx namespace for binary operators (is
324 // typically the case for STL iterators operator==/!=.
325 // TODO: only look in __gnu_cxx for iterators (and more generally: do lookups in the
326 // namespace where the class is defined
327 static Cppyy::TCppScope_t gnucxx = Cppyy::GetScope("__gnu_cxx");
328 if (gnucxx)
329 pyfunc = BuildOperator(lcname, rcname, op, gnucxx, reverse);
330 }
331
332 if (!pyfunc) {
333 // Same for clang (on Mac only?). TODO: find proper pre-processor magic to only use those
334 // specific namespaces that are actually around; although to be sure, this isn't expensive.
335 static Cppyy::TCppScope_t std__1 = Cppyy::GetScope("std::__1");
336
337 if (std__1
338#ifdef __APPLE__
339 && lcname.find("__wrap_iter") == std::string::npos // wrapper call does not compile
340#endif
341 ) {
342 pyfunc = BuildOperator(lcname, rcname, op, std__1, reverse);
343 }
344 }
345
346 if (!pyfunc) {
347 // One more, mostly for Mac, but again not sure whether this is not a general issue. Some
348 // operators are declared as friends only in classes, so then they're not found in the
349 // global namespace, so this helper let's the compiler resolve the operator.
350 static Cppyy::TCppScope_t s_intern = Cppyy::GetScope("__cppyy_internal");
351 if (s_intern) {
352 std::stringstream fname, proto;
353 if (strncmp(op, "==", 2) == 0) { fname << "is_equal<"; }
354 else if (strncmp(op, "!=", 2) == 0) { fname << "is_not_equal<"; }
355 else { fname << "not_implemented<"; }
356 fname << lcname << ", " << rcname << ">";
357 proto << "const " << lcname << "&, const " << rcname;
358 Cppyy::TCppMethod_t method = Cppyy::GetMethodTemplate(s_intern, fname.str(), proto.str());
359 if (method) pyfunc = new CPPFunction(s_intern, method);
360 }
361 }
362
363 return pyfunc;
364}
365
366//----------------------------------------------------------------------------
367static bool AddTypeName(std::string& tmpl_name, PyObject* tn, PyObject* arg,
368 CPyCppyy::Utility::ArgPreference pref, int* pcnt = nullptr)
369{
370// Determine the appropriate C++ type for a given Python type; this is a helper because
371// it can recurse if the type is list or tuple and needs matching on std::vector.
372 using namespace CPyCppyy;
373 using namespace CPyCppyy::Utility;
374
375 if (tn == (PyObject*)&PyInt_Type) {
376 if (arg) {
377#if PY_VERSION_HEX < 0x03000000
378 long l = PyInt_AS_LONG(arg);
379 tmpl_name.append((l < INT_MIN || INT_MAX < l) ? "long" : "int");
380#else
381 Long64_t ll = PyLong_AsLongLong(arg);
382 if (ll == (Long64_t)-1 && PyErr_Occurred()) {
383 PyErr_Clear();
384 ULong64_t ull = PyLong_AsUnsignedLongLong(arg);
385 if (ull == (ULong64_t)-1 && PyErr_Occurred()) {
386 PyErr_Clear();
387 tmpl_name.append("int"); // still out of range, will fail later
388 } else
389 tmpl_name.append("ULong64_t"); // since already failed long long
390 } else
391 tmpl_name.append((ll < INT_MIN || INT_MAX < ll) ? \
392 ((ll < LONG_MIN || LONG_MAX < ll) ? "Long64_t" : "long") : "int");
393#endif
394 } else
395 tmpl_name.append("int");
396#if PY_VERSION_HEX < 0x03000000
397 } else if (tn == (PyObject*)&PyLong_Type) {
398 if (arg) {
399 Long64_t ll = PyLong_AsLongLong(arg);
400 if (ll == (Long64_t)-1 && PyErr_Occurred()) {
401 PyErr_Clear();
402 ULong64_t ull = PyLong_AsUnsignedLongLong(arg);
403 if (ull == (ULong64_t)-1 && PyErr_Occurred()) {
404 PyErr_Clear();
405 tmpl_name.append("long"); // still out of range, will fail later
406 } else
407 tmpl_name.append("ULong64_t"); // since already failed long long
408 } else
409 tmpl_name.append((ll < LONG_MIN || LONG_MAX < ll) ? "Long64_t" : "long");
410 } else
411 tmpl_name.append("long");
412#endif
413 } else if (tn == (PyObject*)&PyFloat_Type) {
414 // special case for floats (Python-speak for double) if from argument (only)
415 tmpl_name.append(arg ? "double" : "float");
416#if PY_VERSION_HEX < 0x03000000
417 } else if (tn == (PyObject*)&PyString_Type) {
418#else
419 } else if (tn == (PyObject*)&PyUnicode_Type) {
420#endif
421 tmpl_name.append("std::string");
422 } else if (tn == (PyObject*)&PyList_Type || tn == (PyObject*)&PyTuple_Type) {
423 if (arg && PySequence_Size(arg)) {
424 std::string subtype{"std::initializer_list<"};
425 PyObject* item = PySequence_GetItem(arg, 0);
426 ArgPreference subpref = pref == kValue ? kValue : kPointer;
427 if (AddTypeName(subtype, (PyObject*)Py_TYPE(item), item, subpref)) {
428 tmpl_name.append(subtype);
429 tmpl_name.append(">");
430 }
431 Py_DECREF(item);
432 }
433
434 } else if (CPPScope_Check(tn)) {
435 tmpl_name.append(Cppyy::GetScopedFinalName(((CPPClass*)tn)->fCppType));
436 if (arg) {
437 // try to specialize the type match for the given object
438 CPPInstance* pyobj = (CPPInstance*)arg;
439 if (CPPInstance_Check(pyobj)) {
440 if (pyobj->fFlags & CPPInstance::kIsRValue)
441 tmpl_name.append("&&");
442 else {
443 if (pcnt) *pcnt += 1;
444 if ((pyobj->fFlags & CPPInstance::kIsReference) || pref == kPointer)
445 tmpl_name.push_back('*');
446 else if (pref != kValue)
447 tmpl_name.push_back('&');
448 }
449 }
450 }
451 } else if (PyObject_HasAttr(tn, PyStrings::gCppName)) {
452 PyObject* tpName = PyObject_GetAttr(tn, PyStrings::gCppName);
453 tmpl_name.append(CPyCppyy_PyText_AsString(tpName));
454 Py_DECREF(tpName);
455 } else if (PyObject_HasAttr(tn, PyStrings::gName)) {
456 PyObject* tpName = PyObject_GetAttr(tn, PyStrings::gName);
457 tmpl_name.append(CPyCppyy_PyText_AsString(tpName));
458 Py_DECREF(tpName);
459 } else if (PyInt_Check(tn) || PyLong_Check(tn) || PyFloat_Check(tn)) {
460 // last ditch attempt, works for things like int values; since this is a
461 // source of errors otherwise, it is limited to specific types and not
462 // generally used (str(obj) can print anything ...)
463 PyObject* pystr = PyObject_Str(tn);
464 tmpl_name.append(CPyCppyy_PyText_AsString(pystr));
465 Py_DECREF(pystr);
466 } else {
467 return false;
468 }
469
470 return true;
471}
472
474 PyObject* pyname, PyObject* tpArgs, PyObject* args, ArgPreference pref, int argoff, int* pcnt)
475{
476// Helper to construct the "<type, type, ...>" part of a templated name (either
477// for a class or method lookup
478 bool justOne = !PyTuple_CheckExact(tpArgs);
479
480// Note: directly appending to string is a lot faster than stringstream
481 std::string tmpl_name;
482 tmpl_name.reserve(128);
483 if (pyname)
484 tmpl_name.append(CPyCppyy_PyText_AsString(pyname));
485 tmpl_name.push_back('<');
486
487 if (pcnt) *pcnt = 0; // count number of times 'pref' is used
488
489 Py_ssize_t nArgs = justOne ? 1 : PyTuple_GET_SIZE(tpArgs);
490 for (int i = argoff; i < nArgs; ++i) {
491 // add type as string to name
492 PyObject* tn = justOne ? tpArgs : PyTuple_GET_ITEM(tpArgs, i);
493 if (CPyCppyy_PyText_Check(tn)) {
494 tmpl_name.append(CPyCppyy_PyText_AsString(tn));
495 // some commmon numeric types (separated out for performance: checking for
496 // __cpp_name__ and/or __name__ is rather expensive)
497 } else {
498 if (!AddTypeName(tmpl_name, tn, (args ? PyTuple_GET_ITEM(args, i) : nullptr), pref, pcnt)) {
499 PyErr_SetString(PyExc_SyntaxError,
500 "could not construct C++ name from provided template argument.");
501 return "";
502 }
503 }
504
505 // add a comma, as needed (no space as internally, final names don't have them)
506 if (i != nArgs-1)
507 tmpl_name.push_back(',');
508 }
509
510// close template name
511 tmpl_name.push_back('>');
512
513 return tmpl_name;
514}
515
516//----------------------------------------------------------------------------
517void CPyCppyy::Utility::ConstructCallbackPreamble(const std::string& retType,
518 const std::vector<std::string>& argtypes, std::ostringstream& code)
519{
520// Generate function setup to be used in callbacks (wrappers and overrides).
521 int nArgs = (int)argtypes.size();
522
523// return value and argument type converters
524 bool isVoid = retType == "void";
525 if (!isVoid)
526 code << " CPYCPPYY_STATIC std::unique_ptr<CPyCppyy::Converter, std::function<void(CPyCppyy::Converter*)>> "
527 "retconv{CPyCppyy::CreateConverter(\""
528 << retType << "\"), CPyCppyy::DestroyConverter};\n";
529 if (nArgs) {
530 code << " CPYCPPYY_STATIC std::vector<std::unique_ptr<CPyCppyy::Converter, std::function<void(CPyCppyy::Converter*)>>> argcvs;\n"
531 << " if (argcvs.empty()) {\n"
532 << " argcvs.reserve(" << nArgs << ");\n";
533 for (int i = 0; i < nArgs; ++i)
534 code << " argcvs.emplace_back(CPyCppyy::CreateConverter(\"" << argtypes[i] << "\"), CPyCppyy::DestroyConverter);\n";
535 code << " }\n";
536 }
537
538// declare return value (TODO: this does not work for most non-builtin values)
539 if (!isVoid)
540 code << " " << retType << " ret{};\n";
541
542// acquire GIL
543 code << " PyGILState_STATE state = PyGILState_Ensure();\n";
544
545// build argument tuple if needed
546 if (nArgs) {
547 code << " std::vector<PyObject*> pyargs;\n";
548 code << " pyargs.reserve(" << nArgs << ");\n"
549 << " try {\n";
550 for (int i = 0; i < nArgs; ++i) {
551 code << " pyargs.emplace_back(argcvs[" << i << "]->FromMemory((void*)&arg" << i << "));\n"
552 << " if (!pyargs.back()) throw " << i << ";\n";
553 }
554 code << " } catch(int) {\n"
555 << " for (auto pyarg : pyargs) Py_XDECREF(pyarg);\n"
556 << " PyGILState_Release(state); throw CPyCppyy::PyException{};\n"
557 << " }\n";
558 }
559}
560
561void CPyCppyy::Utility::ConstructCallbackReturn(const std::string& retType, int nArgs, std::ostringstream& code)
562{
563// Generate code for return value conversion and error handling.
564 bool isVoid = retType == "void";
565 bool isPtr = Cppyy::ResolveName(retType).back() == '*';
566 if (nArgs)
567 code << " for (auto pyarg : pyargs) Py_DECREF(pyarg);\n";
568 code << " bool cOk = (bool)pyresult;\n"
569 " if (pyresult) {\n";
570 if (isPtr) {
571 // If the return type is a CPPInstance, owned by Python, and the ref-count down
572 // to 1, the return will hold a dangling pointer, so set it to nullptr instead.
573 code << " if (!CPyCppyy::Instance_IsLively(pyresult))\n"
574 " ret = nullptr;\n"
575 " else {\n";
576 }
577 code << (isVoid ? "" : " cOk = retconv->ToMemory(pyresult, &ret);\n")
578 << " Py_DECREF(pyresult);\n }\n";
579 if (isPtr) code << " }\n";
580 code << " if (!cOk) {" // assume error set when converter failed
581// TODO: On Windows, throwing a C++ exception here makes the code hang; leave
582// the error be which allows at least one layer of propagation
583#ifdef _WIN32
584 " /* do nothing */ }\n"
585#else
586 " PyGILState_Release(state); throw CPyCppyy::PyException{}; }\n"
587#endif
588 " PyGILState_Release(state);\n"
589 " return";
590 code << (isVoid ? ";\n }\n" : " ret;\n }\n");
591}
592
593//----------------------------------------------------------------------------
594bool CPyCppyy::Utility::InitProxy(PyObject* module, PyTypeObject* pytype, const char* name)
595{
596// Initialize a proxy class for use by python, and add it to the module.
597
598// finalize proxy type
599 if (PyType_Ready(pytype) < 0)
600 return false;
601
602// add proxy type to the given module
603 Py_INCREF(pytype); // PyModule_AddObject steals reference
604 if (PyModule_AddObject(module, (char*)name, (PyObject*)pytype) < 0) {
605 Py_DECREF(pytype);
606 return false;
607 }
608
609// declare success
610 return true;
611}
612
613//----------------------------------------------------------------------------
614Py_ssize_t CPyCppyy::Utility::GetBuffer(PyObject* pyobject, char tc, int size, void*& buf, bool check)
615{
616// Retrieve a linear buffer pointer from the given pyobject.
617
618// special case: don't handle character strings here (yes, they're buffers, but not quite)
619 if (PyBytes_Check(pyobject))
620 return 0;
621
622// special case: bytes array
623 if ((!check || tc == '*' || tc == 'B') && PyByteArray_CheckExact(pyobject)) {
624 buf = PyByteArray_AS_STRING(pyobject);
625 return PyByteArray_GET_SIZE(pyobject);
626 }
627
628// new-style buffer interface
629 if (PyObject_CheckBuffer(pyobject)) {
630 Py_buffer bufinfo;
631 memset(&bufinfo, 0, sizeof(Py_buffer));
632 if (PyObject_GetBuffer(pyobject, &bufinfo, PyBUF_FORMAT) == 0) {
633 if (tc == '*' || strchr(bufinfo.format, tc)
634#ifdef _WIN32
635 // ctypes is inconsistent in format on Windows; either way these types are the same size
636 || (tc == 'I' && strchr(bufinfo.format, 'L')) || (tc == 'i' && strchr(bufinfo.format, 'l'))
637#endif
638 // allow 'signed char' ('b') from array to pass through '?' (bool as from struct)
639 || (tc == '?' && strchr(bufinfo.format, 'b'))
640 ) {
641 buf = bufinfo.buf;
642 if (buf && bufinfo.ndim == 0) {
643 PyBuffer_Release(&bufinfo);
644 return bufinfo.len/bufinfo.itemsize;
645 } else if (buf && bufinfo.ndim == 1) {
646 Py_ssize_t size1d = bufinfo.shape ? bufinfo.shape[0] : bufinfo.len/bufinfo.itemsize;
647 PyBuffer_Release(&bufinfo);
648 return size1d;
649 }
650 } else {
651 // have buf, but format mismatch: bail out now, otherwise the old
652 // code will return based on itemsize match
653 PyBuffer_Release(&bufinfo);
654 return 0;
655 }
656 }
657 PyErr_Clear();
658 }
659
660// attempt to retrieve pointer through old-style buffer interface
661 PyBufferProcs* bufprocs = Py_TYPE(pyobject)->tp_as_buffer;
662
663 PySequenceMethods* seqmeths = Py_TYPE(pyobject)->tp_as_sequence;
664 if (seqmeths != 0 && bufprocs != 0
665#if PY_VERSION_HEX < 0x03000000
666 && bufprocs->bf_getwritebuffer != 0
667 && (*(bufprocs->bf_getsegcount))(pyobject, 0) == 1
668#else
669 && bufprocs->bf_getbuffer != 0
670#endif
671 ) {
672
673 // get the buffer
674#if PY_VERSION_HEX < 0x03000000
675 Py_ssize_t buflen = (*(bufprocs->bf_getwritebuffer))(pyobject, 0, &buf);
676#else
677 Py_buffer bufinfo;
678 (*(bufprocs->bf_getbuffer))(pyobject, &bufinfo, PyBUF_WRITABLE);
679 buf = (char*)bufinfo.buf;
680 Py_ssize_t buflen = bufinfo.len;
681#if PY_VERSION_HEX < 0x03010000
682 PyBuffer_Release(pyobject, &bufinfo);
683#else
684 PyBuffer_Release(&bufinfo);
685#endif
686#endif
687
688 if (buf && check == true) {
689 // determine buffer compatibility (use "buf" as a status flag)
690 PyObject* pytc = PyObject_GetAttr(pyobject, PyStrings::gTypeCode);
691 if (pytc != 0) { // for array objects
692 char cpytc = CPyCppyy_PyText_AsString(pytc)[0];
693 if (!(cpytc == tc || (tc == '?' && cpytc == 'b')))
694 buf = 0; // no match
695 Py_DECREF(pytc);
696 } else if (seqmeths->sq_length &&
697 (int)(buflen/(*(seqmeths->sq_length))(pyobject)) == size) {
698 // this is a gamble ... may or may not be ok, but that's for the user
699 PyErr_Clear();
700 } else if (buflen == size) {
701 // also a gamble, but at least 1 item will fit into the buffer, so very likely ok ...
702 PyErr_Clear();
703 } else {
704 buf = 0; // not compatible
705
706 // clarify error message
707 PyObject* pytype = 0, *pyvalue = 0, *pytrace = 0;
708 PyErr_Fetch(&pytype, &pyvalue, &pytrace);
710 (char*)"%s and given element size (%ld) do not match needed (%d)",
712 seqmeths->sq_length ? (Long_t)(buflen/(*(seqmeths->sq_length))(pyobject)) : (Long_t)buflen,
713 size);
714 Py_DECREF(pyvalue);
715 PyErr_Restore(pytype, pyvalue2, pytrace);
716 }
717 }
718
719 if (!buf) return 0;
720 return buflen/(size ? size : 1);
721 }
722
723 return 0;
724}
725
726//----------------------------------------------------------------------------
727std::string CPyCppyy::Utility::MapOperatorName(const std::string& name, bool bTakesParams)
728{
729// Map the given C++ operator name on the python equivalent.
730 if (8 < name.size() && name.substr(0, 8) == "operator") {
731 std::string op = name.substr(8, std::string::npos);
732
733 // stripping ...
734 std::string::size_type start = 0, end = op.size();
735 while (start < end && isspace(op[start])) ++start;
736 while (start < end && isspace(op[end-1])) --end;
737 op = op.substr(start, end - start);
738
739 // certain operators should be removed completely (e.g. operator delete & friends)
740 if (gOpRemove.find(op) != gOpRemove.end())
741 return "";
742
743 // check first if none, to prevent spurious deserializing downstream
744 TC2POperatorMapping_t::iterator pop = gC2POperatorMapping.find(op);
745 if (pop == gC2POperatorMapping.end() && gOpSkip.find(op) == gOpSkip.end()) {
746 op = Cppyy::ResolveName(op);
747 pop = gC2POperatorMapping.find(op);
748 }
749
750 // map C++ operator to python equivalent, or made up name if no equivalent exists
751 if (pop != gC2POperatorMapping.end()) {
752 return pop->second;
753
754 } else if (op == "*") {
755 // dereference v.s. multiplication of two instances
756 return bTakesParams ? "__mul__" : "__deref__";
757
758 } else if (op == "+") {
759 // unary positive v.s. addition of two instances
760 return bTakesParams ? "__add__" : "__pos__";
761
762 } else if (op == "-") {
763 // unary negative v.s. subtraction of two instances
764 return bTakesParams ? "__sub__" : "__neg__";
765
766 } else if (op == "++") {
767 // prefix v.s. postfix increment
768 return bTakesParams ? "__postinc__" : "__preinc__";
769
770 } else if (op == "--") {
771 // prefix v.s. postfix decrement
772 return bTakesParams ? "__postdec__" : "__predec__";
773 }
774
775 }
776
777// might get here, as not all operator methods are handled (new, delete, etc.)
778 return name;
779}
780
781//----------------------------------------------------------------------------
782const std::string CPyCppyy::Utility::Compound(const std::string& name)
783{
784// TODO: consolidate with other string manipulations in TypeManip.cxx
785// Break down the compound of a fully qualified type name.
786 std::string cleanName = name;
787 RemoveConst(cleanName);
788
789 std::string compound = "";
790 for (int ipos = (int)cleanName.size()-1; 0 <= ipos; --ipos) {
791 char c = cleanName[ipos];
792 if (isspace(c)) continue;
793 if (isalnum(c) || c == '_' || c == '>' || c == ')') break;
794
795 compound = c + compound;
796 }
797
798// for arrays (TODO: deal with the actual size)
799 if (compound == "]")
800 return "[]";
801
802 return compound;
803}
804
805//----------------------------------------------------------------------------
807{
808// TODO: consolidate with other string manipulations in Helpers.cxx
809// Extract size from an array type, if available.
810 std::string cleanName = name;
811 RemoveConst(cleanName);
812
813 if (cleanName[cleanName.size()-1] == ']') {
814 std::string::size_type idx = cleanName.rfind('[');
815 if (idx != std::string::npos) {
816 const std::string asize = cleanName.substr(idx+1, cleanName.size()-2);
817 return strtoul(asize.c_str(), nullptr, 0);
818 }
819 }
820
821 return -1;
822}
823
824//----------------------------------------------------------------------------
826{
827// Retrieve the class name from the given Python instance.
828 std::string clname = "<unknown>";
829 PyObject* pyclass = (PyObject*)Py_TYPE(pyobj);
830 PyObject* pyname = PyObject_GetAttr(pyclass, PyStrings::gCppName);
831 if (!pyname) {
832 PyErr_Clear();
833 pyname = PyObject_GetAttr(pyclass, PyStrings::gName);
834 }
835
836 if (pyname) {
838 Py_DECREF(pyname);
839 } else
840 PyErr_Clear();
841 return clname;
842}
843
844
845//----------------------------------------------------------------------------
847{
848 Py_XDECREF(fEq);
849 Py_XDECREF(fNe);
850 Py_XDECREF(fLAdd); Py_XDECREF(fRAdd);
851 Py_XDECREF(fSub);
852 Py_XDECREF(fLMul); Py_XDECREF(fRMul);
853 Py_XDECREF(fDiv);
854 Py_XDECREF(fHash);
855}
856
857
858//----------------------------------------------------------------------------
860{
861// Re-acquire the GIL before calling PyErr_Occurred() in case it has been
862// released; note that the p2.2 code assumes that there are no callbacks in
863// C++ to python (or at least none returning errors).
864#if PY_VERSION_HEX >= 0x02030000
865 PyGILState_STATE gstate = PyGILState_Ensure();
866 PyObject* e = PyErr_Occurred();
867 PyGILState_Release(gstate);
868#else
869 if (PyThreadState_GET())
870 return PyErr_Occurred();
871 PyObject* e = 0;
872#endif
873
874 return e;
875}
876
877
878//----------------------------------------------------------------------------
879size_t CPyCppyy::Utility::FetchError(std::vector<PyError_t>& errors)
880{
881// Fetch the current python error, if any, and store it for future use.
882 if (PyErr_Occurred()) {
883 PyError_t e;
884 PyErr_Fetch(&e.fType, &e.fValue, &e.fTrace);
885 errors.push_back(e);
886 }
887 return errors.size();
888}
889
890//----------------------------------------------------------------------------
891void CPyCppyy::Utility::SetDetailedException(std::vector<PyError_t>& errors, PyObject* topmsg, PyObject* defexc)
892{
893// Use the collected exceptions to build up a detailed error log.
894 if (errors.empty()) {
895 // should not happen ...
896 PyErr_SetString(defexc, CPyCppyy_PyText_AsString(topmsg));
897 Py_DECREF(topmsg);
898 return;
899 }
900
901// add the details to the topmsg
903
904 PyObject* exc_type = nullptr;
905 for (auto& e : errors) {
906 if (!exc_type) exc_type = e.fType;
907 else if (exc_type != e.fType) exc_type = defexc;
909 if (CPyCppyy_PyText_Check(e.fValue)) {
910 CPyCppyy_PyText_Append(&topmsg, e.fValue);
911 } else if (e.fValue) {
912 PyObject* excstr = PyObject_Str(e.fValue);
913 if (!excstr) {
914 PyErr_Clear();
915 excstr = PyObject_Str((PyObject*)Py_TYPE(e.fValue));
916 }
917 CPyCppyy_PyText_AppendAndDel(&topmsg, excstr);
918 } else {
920 CPyCppyy_PyText_FromString("unknown exception"));
921 }
922 }
923
924 Py_DECREF(separator);
925 std::for_each(errors.begin(), errors.end(), PyError_t::Clear);
926
927// set the python exception
928 PyErr_SetString(exc_type, CPyCppyy_PyText_AsString(topmsg));
929 Py_DECREF(topmsg);
930}
931
932
933//----------------------------------------------------------------------------
934static bool includesDone = false;
936{
937// setup Python API for callbacks
938 if (!includesDone) {
939 bool okay = Cppyy::Compile(
940 // basic API (converters etc.)
941 "#include \"CPyCppyy/API.h\"\n"
942
943 // utilities from the CPyCppyy public API
944 "#include \"CPyCppyy/DispatchPtr.h\"\n"
945 "#include \"CPyCppyy/PyException.h\"\n"
946 );
947 includesDone = okay;
948 }
949
950 return includesDone;
951}
#define Py_TYPE(ob)
Definition: CPyCppyy.h:209
#define CPPYY__long__
Definition: CPyCppyy.h:128
#define CPPYY__div__
Definition: CPyCppyy.h:130
PyDictEntry *(* dict_lookup_func)(PyDictObject *, PyObject *, long)
Definition: CPyCppyy.h:69
#define CPyCppyy_PyText_InternFromString
Definition: CPyCppyy.h:103
#define PyBytes_Check
Definition: CPyCppyy.h:83
#define CPyCppyy_PyText_Append
Definition: CPyCppyy.h:104
#define CPyCppyy_PyText_AsString
Definition: CPyCppyy.h:97
#define CPyCppyy_PyText_AppendAndDel
Definition: CPyCppyy.h:105
#define CPyCppyy_PyText_FromFormat
Definition: CPyCppyy.h:101
#define CPyCppyy_PyText_FromString
Definition: CPyCppyy.h:102
#define CPPYY__idiv__
Definition: CPyCppyy.h:129
#define CPyCppyy_PyText_Check
Definition: CPyCppyy.h:95
long
Definition: Converters.cxx:858
@ kPointer
Definition: GuiTypes.h:374
_object PyObject
Definition: PyMethodBase.h:41
#define c(i)
Definition: RSha256.hxx:101
#define e(i)
Definition: RSha256.hxx:103
long Long_t
Definition: RtypesCore.h:52
long long Long64_t
Definition: RtypesCore.h:71
unsigned long long ULong64_t
Definition: RtypesCore.h:72
@ kIsReference
Definition: TDictionary.h:82
char name[80]
Definition: TGX11.cxx:109
#define pyname
Definition: TMCParticle.cxx:19
static bool includesDone
Definition: Utility.cxx:934
static TC2POperatorMapping_t gC2POperatorMapping
Definition: Utility.cxx:30
static CPyCppyy::PyCallable * BuildOperator(const std::string &lcname, const std::string &rcname, const char *op, Cppyy::TCppScope_t scope, bool reverse=false)
Definition: Utility.cxx:248
static std::set< std::string > gOpRemove
Definition: Utility.cxx:32
static std::set< std::string > gOpSkip
Definition: Utility.cxx:31
static bool AddTypeName(std::string &tmpl_name, PyObject *tn, PyObject *arg, CPyCppyy::Utility::ArgPreference pref, int *pcnt=nullptr)
Definition: Utility.cxx:367
std::map< std::string, std::string > TC2POperatorMapping_t
Definition: Utility.cxx:29
const char * proto
Definition: civetweb.c:16604
void AdoptMethod(PyCallable *pc)
Cppyy::TCppType_t fCppType
Definition: CPPScope.h:50
void compound()
Definition: compound.C:25
RooCmdArg ClassName(const char *name)
PyObject * gName
Definition: PyStrings.cxx:26
PyObject * gCppName
Definition: PyStrings.cxx:10
PyObject * gTypeCode
Definition: PyStrings.cxx:28
std::string extract_namespace(const std::string &name)
Definition: TypeManip.cxx:159
PyCallable * FindBinaryOperator(PyObject *left, PyObject *right, const char *op, Cppyy::TCppScope_t scope=0)
Definition: Utility.cxx:280
size_t FetchError(std::vector< PyError_t > &)
Definition: Utility.cxx:879
void ConstructCallbackPreamble(const std::string &retType, const std::vector< std::string > &argtypes, std::ostringstream &code)
Definition: Utility.cxx:517
void ConstructCallbackReturn(const std::string &retType, int nArgs, std::ostringstream &code)
Definition: Utility.cxx:561
std::string MapOperatorName(const std::string &name, bool bTakesParames)
Definition: Utility.cxx:727
Py_ssize_t GetBuffer(PyObject *pyobject, char tc, int size, void *&buf, bool check=true)
Definition: Utility.cxx:614
std::string ConstructTemplateArgs(PyObject *pyname, PyObject *tpArgs, PyObject *args=nullptr, ArgPreference=kNone, int argoff=0, int *pcnt=nullptr)
Definition: Utility.cxx:473
PyCallable * FindUnaryOperator(PyObject *pyclass, const char *op)
Definition: Utility.cxx:266
void SetDetailedException(std::vector< PyError_t > &errors, PyObject *topmsg, PyObject *defexc)
Definition: Utility.cxx:891
bool InitProxy(PyObject *module, PyTypeObject *pytype, const char *name)
Definition: Utility.cxx:594
bool AddToClass(PyObject *pyclass, const char *label, PyCFunction cfunc, int flags=METH_VARARGS)
Definition: Utility.cxx:169
Py_ssize_t ArraySize(const std::string &name)
Definition: Utility.cxx:806
const std::string Compound(const std::string &name)
Definition: Utility.cxx:782
std::string ClassName(PyObject *pyobj)
Definition: Utility.cxx:825
bool IncludePython()
Definition: Utility.cxx:935
PyObject * PyErr_Occurred_WithGIL()
Definition: Utility.cxx:859
CPPOverload * CPPOverload_New(const std::string &name, std::vector< PyCallable * > &methods)
Definition: CPPOverload.h:91
unsigned long PyLongOrInt_AsULong(PyObject *pyobject)
Definition: Utility.cxx:130
PyObject * CustomInstanceMethod_New(PyObject *func, PyObject *self, PyObject *pyclass)
bool gDictLookupActive
Definition: Utility.cxx:27
dict_lookup_func gDictLookupOrg
Definition: Utility.cxx:26
bool CPPOverload_Check(T *object)
Definition: CPPOverload.h:79
bool CPPScope_Check(T *object)
Definition: CPPScope.h:76
bool CPPInstance_Check(T *object)
Definition: CPPInstance.h:118
ULong64_t PyLongOrInt_AsULong64(PyObject *pyobject)
Definition: Utility.cxx:150
size_t TCppIndex_t
Definition: cpp_cppyy.h:24
RPY_EXPORTED bool Compile(const std::string &code)
intptr_t TCppMethod_t
Definition: cpp_cppyy.h:22
RPY_EXPORTED TCppIndex_t GetGlobalOperator(TCppType_t scope, const std::string &lc, const std::string &rc, const std::string &op)
RPY_EXPORTED TCppMethod_t GetMethodTemplate(TCppScope_t scope, const std::string &name, const std::string &proto)
RPY_EXPORTED TCppMethod_t GetMethod(TCppScope_t scope, TCppIndex_t imeth)
size_t TCppScope_t
Definition: cpp_cppyy.h:18
RPY_EXPORTED std::string ResolveName(const std::string &cppitem_name)
RPY_EXPORTED std::string GetScopedFinalName(TCppType_t type)
RPY_EXPORTED TCppScope_t GetScope(const std::string &scope_name)
RPY_EXPORTED TCppScope_t gGlobalScope
Definition: cpp_cppyy.h:51
static const std::string separator("@@@")
static void Clear(PyError_t &e)
Definition: Utility.h:82
auto * l
Definition: textangle.C:4