Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
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 _____________________________________________________________________
26#if PY_VERSION_HEX < 0x030b0000
28#endif
29
30typedef std::map<std::string, std::string> TC2POperatorMapping_t;
32static std::set<std::string> gOpSkip;
33static std::set<std::string> gOpRemove;
34
35namespace CPyCppyy {
36// special objects
39}
40
41namespace {
42
43 using namespace CPyCppyy::Utility;
44
45 struct InitOperatorMapping_t {
46 public:
47 InitOperatorMapping_t() {
48 // Initialize the global map of operator names C++ -> python.
49
50 gOpSkip.insert("[]"); // __s/getitem__, depends on return type
51 gOpSkip.insert("+"); // __add__, depends on # of args (see __pos__)
52 gOpSkip.insert("-"); // __sub__, id. (eq. __neg__)
53 gOpSkip.insert("*"); // __mul__ or __deref__
54 gOpSkip.insert("++"); // __postinc__ or __preinc__
55 gOpSkip.insert("--"); // __postdec__ or __predec__
56
57 gOpRemove.insert("new"); // this and the following not handled at all
58 gOpRemove.insert("new[]");
59 gOpRemove.insert("delete");
60 gOpRemove.insert("delete[]");
61
62 gC2POperatorMapping["[]"] = "__getitem__";
63 gC2POperatorMapping["()"] = "__call__";
64 gC2POperatorMapping["%"] = "__mod__";
65 gC2POperatorMapping["**"] = "__pow__";
66 gC2POperatorMapping["<<"] = "__lshift__";
67 gC2POperatorMapping[">>"] = "__rshift__";
68 gC2POperatorMapping["&"] = "__and__";
69 gC2POperatorMapping["&&"] = "__dand__";
70 gC2POperatorMapping["|"] = "__or__";
71 gC2POperatorMapping["||"] = "__dor__";
72 gC2POperatorMapping["^"] = "__xor__";
73 gC2POperatorMapping["~"] = "__invert__";
74 gC2POperatorMapping[","] = "__comma__";
75 gC2POperatorMapping["+="] = "__iadd__";
76 gC2POperatorMapping["-="] = "__isub__";
77 gC2POperatorMapping["*="] = "__imul__";
79 gC2POperatorMapping["%="] = "__imod__";
80 gC2POperatorMapping["**="] = "__ipow__";
81 gC2POperatorMapping["<<="] = "__ilshift__";
82 gC2POperatorMapping[">>="] = "__irshift__";
83 gC2POperatorMapping["&="] = "__iand__";
84 gC2POperatorMapping["|="] = "__ior__";
85 gC2POperatorMapping["^="] = "__ixor__";
86 gC2POperatorMapping["=="] = "__eq__";
87 gC2POperatorMapping["!="] = "__ne__";
88 gC2POperatorMapping[">"] = "__gt__";
89 gC2POperatorMapping["<"] = "__lt__";
90 gC2POperatorMapping[">="] = "__ge__";
91 gC2POperatorMapping["<="] = "__le__";
92
93 // the following type mappings are "exact"
94 gC2POperatorMapping["const char*"] = "__str__";
95 gC2POperatorMapping["char*"] = "__str__";
96 gC2POperatorMapping["const char *"] = gC2POperatorMapping["const char*"];
97 gC2POperatorMapping["char *"] = gC2POperatorMapping["char*"];
98 gC2POperatorMapping["int"] = "__int__";
100 gC2POperatorMapping["double"] = "__float__";
101
102 // the following type mappings are "okay"; the assumption is that they
103 // are not mixed up with the ones above or between themselves (and if
104 // they are, that it is done consistently)
105 gC2POperatorMapping["short"] = "__int__";
106 gC2POperatorMapping["unsigned short"] = "__int__";
107 gC2POperatorMapping["unsigned int"] = CPPYY__long__;
108 gC2POperatorMapping["unsigned long"] = CPPYY__long__;
109 gC2POperatorMapping["long long"] = CPPYY__long__;
110 gC2POperatorMapping["unsigned long long"] = CPPYY__long__;
111 gC2POperatorMapping["float"] = "__float__";
112
113 gC2POperatorMapping["->"] = "__follow__"; // not an actual python operator
114 gC2POperatorMapping["="] = "__assign__"; // id.
115
116#if PY_VERSION_HEX < 0x03000000
117 gC2POperatorMapping["bool"] = "__cpp_nonzero__";
118#else
119 gC2POperatorMapping["bool"] = "__cpp_bool__";
120#endif
121 }
123
124 inline std::string full_scope(const std::string& tpname) {
125 return tpname[0] == ':' ? tpname : "::"+tpname;
126 }
127
128} // unnamed namespace
129
130
131//- public functions ---------------------------------------------------------
133{
134// Convert <pybject> to C++ unsigned long, with bounds checking, allow int -> ulong.
135 if (PyFloat_Check(pyobject)) {
136 PyErr_SetString(PyExc_TypeError, "can\'t convert float to unsigned long");
137 return (unsigned long)-1;
138 } else if (pyobject == CPyCppyy::gDefaultObject) {
139 return (unsigned long)0;
140 }
141
142 unsigned long ul = PyLong_AsUnsignedLong(pyobject);
143 if (ul == (unsigned long)-1 && PyErr_Occurred() && PyInt_Check(pyobject)) {
144 PyErr_Clear();
145 long i = PyInt_AS_LONG(pyobject);
146 if (0 <= i) {
147 ul = (unsigned long)i;
148 } else {
150 "can\'t convert negative value to unsigned long");
151 return (unsigned long)-1;
152 }
153 }
154
155 return ul;
156}
157
158//----------------------------------------------------------------------------
160{
161// Convert <pyobject> to C++ unsigned long long, with bounds checking.
162 if (PyFloat_Check(pyobject)) {
163 PyErr_SetString(PyExc_TypeError, "can\'t convert float to unsigned long long");
164 return -1;
165 } else if (pyobject == CPyCppyy::gDefaultObject) {
166 return (unsigned long)0;
167 }
168
171 PyErr_Clear();
172 long i = PyInt_AS_LONG(pyobject);
173 if (0 <= i) {
174 ull = (PY_ULONG_LONG)i;
175 } else {
177 "can\'t convert negative value to unsigned long long");
178 }
179 }
180
181 return ull;
182}
183
184//----------------------------------------------------------------------------
186 PyObject* pyclass, const char* label, PyCFunction cfunc, int flags)
187{
188// Add the given function to the class under name 'label'.
189
190// use list for clean-up (.so's are unloaded only at interpreter shutdown)
191 static std::list<PyMethodDef> s_pymeths;
192
193 s_pymeths.push_back(PyMethodDef());
194 PyMethodDef* pdef = &s_pymeths.back();
195 pdef->ml_name = const_cast<char*>(label);
196 pdef->ml_meth = cfunc;
197 pdef->ml_flags = flags;
198 pdef->ml_doc = nullptr;
199
200 PyObject* func = PyCFunction_New(pdef, nullptr);
203 bool isOk = PyType_Type.tp_setattro(pyclass, name, method) == 0;
206 Py_DECREF(func);
207
208 if (PyErr_Occurred())
209 return false;
210
211 if (!isOk) {
212 PyErr_Format(PyExc_TypeError, "could not add method %s", label);
213 return false;
214 }
215
216 return true;
217}
218
219//----------------------------------------------------------------------------
220bool CPyCppyy::Utility::AddToClass(PyObject* pyclass, const char* label, const char* func)
221{
222// Add the given function to the class under name 'label'.
223 PyObject* pyfunc = PyObject_GetAttrString(pyclass, const_cast<char*>(func));
224 if (!pyfunc)
225 return false;
226
227 PyObject* pylabel = CPyCppyy_PyText_InternFromString(const_cast<char*>(label));
228 bool isOk = PyType_Type.tp_setattro(pyclass, pylabel, pyfunc) == 0;
230
232 return isOk;
233}
234
235//----------------------------------------------------------------------------
237{
238// Add the given function to the class under name 'label'.
240 (CPPOverload*)PyObject_GetAttrString(pyclass, const_cast<char*>(label));
241
242 if (!method || !CPPOverload_Check(method)) {
243 // not adding to existing CPPOverload; add callable directly to the class
244 if (PyErr_Occurred())
245 PyErr_Clear();
247 method = CPPOverload_New(label, pyfunc);
248 PyObject* pylabel = CPyCppyy_PyText_InternFromString(const_cast<char*>(label));
249 bool isOk = PyType_Type.tp_setattro(pyclass, pylabel, (PyObject*)method) == 0;
252 return isOk;
253 }
254
255 method->AdoptMethod(pyfunc);
256
258 return true;
259}
260
261
262//----------------------------------------------------------------------------
263static inline
264CPyCppyy::PyCallable* BuildOperator(const std::string& lcname, const std::string& rcname,
265 const char* op, Cppyy::TCppScope_t scope, bool reverse=false)
266{
267// Helper to find a function with matching signature in 'funcs'.
268 std::string opname = "operator";
269 opname += op;
270
272 if (idx == (Cppyy::TCppIndex_t)-1)
273 return nullptr;
274
276 if (!reverse)
277 return new CPyCppyy::CPPFunction(scope, meth);
279}
280
281//----------------------------------------------------------------------------
283{
284// Find a callable matching named operator (op) and klass arguments in the global
285// namespace or the klass' namespace.
286
288 return nullptr;
289
291 const std::string& lcname = Cppyy::GetScopedFinalName(klass->fCppType);
292 Cppyy::TCppScope_t scope = Cppyy::GetScope(TypeManip::extract_namespace(lcname));
293 return FindBinaryOperator(lcname, "", op, scope, false);
294}
295
296//----------------------------------------------------------------------------
298 const char* op, Cppyy::TCppScope_t scope)
299{
300// Find a callable matching the named operator (op) and the (left, right)
301// arguments in the global or these objects' namespaces.
302
303 bool reverse = false;
304 if (!CPPInstance_Check(left)) {
305 if (CPPInstance_Check(right))
306 reverse = true;
307 else
308 return nullptr;
309 }
310
311// retrieve the class names to match the signature of any found global functions
312 const std::string& lcname = ClassName(left);
313 const std::string& rcname = ClassName(right);
314 return FindBinaryOperator(lcname, rcname, op, scope, reverse);
315}
316
317//----------------------------------------------------------------------------
319 const std::string& lcname, const std::string& rcname,
320 const char* op, Cppyy::TCppScope_t scope, bool reverse)
321{
322// Find a global function with a matching signature; search __gnu_cxx, std::__1,
323// and __cppyy_internal pro-actively (as there's AFAICS no way to unearth 'using'
324// information).
325
326 if (rcname == "<unknown>" || lcname == "<unknown>")
327 return nullptr;
328
329 PyCallable* pyfunc = 0;
330
331 if (!scope) {
332 // TODO: the following should remain sync with what clingwrapper does in its
333 // type remapper; there must be a better way?
334 if (lcname == "str" || lcname == "unicode" || lcname == "complex")
335 scope = Cppyy::GetScope("std");
336 else scope = Cppyy::GetScope(TypeManip::extract_namespace(lcname));
337 }
338 if (scope)
339 pyfunc = BuildOperator(lcname, rcname, op, scope, reverse);
340
341 if (!pyfunc && scope != Cppyy::gGlobalScope) // search in global scope anyway
343
344 if (!pyfunc) {
345 // For GNU on clang, search the internal __gnu_cxx namespace for binary operators (is
346 // typically the case for STL iterators operator==/!=.
347 // TODO: only look in __gnu_cxx for iterators (and more generally: do lookups in the
348 // namespace where the class is defined
349 static Cppyy::TCppScope_t gnucxx = Cppyy::GetScope("__gnu_cxx");
350 if (gnucxx)
351 pyfunc = BuildOperator(lcname, rcname, op, gnucxx, reverse);
352 }
353
354 if (!pyfunc) {
355 // Same for clang (on Mac only?). TODO: find proper pre-processor magic to only use those
356 // specific namespaces that are actually around; although to be sure, this isn't expensive.
357 static Cppyy::TCppScope_t std__1 = Cppyy::GetScope("std::__1");
358
359 if (std__1
361 && lcname.find("__wrap_iter") == std::string::npos // wrapper call does not compile
362#endif
363 ) {
364 pyfunc = BuildOperator(lcname, rcname, op, std__1, reverse);
365 }
366 }
367
368 if (!pyfunc) {
369 // One more, mostly for Mac, but again not sure whether this is not a general issue. Some
370 // operators are declared as friends only in classes, so then they're not found in the
371 // global namespace, so this helper let's the compiler resolve the operator.
372 static Cppyy::TCppScope_t s_intern = Cppyy::GetScope("__cppyy_internal");
373 if (s_intern) {
374 std::stringstream fname, proto;
375 if (strncmp(op, "==", 2) == 0) { fname << "is_equal<"; }
376 else if (strncmp(op, "!=", 2) == 0) { fname << "is_not_equal<"; }
377 else { fname << "not_implemented<"; }
378 fname << lcname << ", " << rcname << ">";
379 proto << "const " << lcname << "&, const " << rcname;
382 }
383 }
384
385 return pyfunc;
386}
387
388//----------------------------------------------------------------------------
389static inline std::string AnnotationAsText(PyObject* pyobj)
390{
393 if (!pystr) {
394 PyErr_Clear();
396 }
397
398 std::string str = CPyCppyy_PyText_AsString(pystr);
400 return str;
401 }
403}
404
405static bool AddTypeName(std::string& tmpl_name, PyObject* tn, PyObject* arg,
407{
408// Determine the appropriate C++ type for a given Python type; this is a helper because
409// it can recurse if the type is list or tuple and needs matching on std::vector.
410 using namespace CPyCppyy;
411 using namespace CPyCppyy::Utility;
412
413 if (tn == (PyObject*)&PyInt_Type) {
414 if (arg) {
415#if PY_VERSION_HEX < 0x03000000
416 long l = PyInt_AS_LONG(arg);
417 tmpl_name.append((l < INT_MIN || INT_MAX < l) ? "long" : "int");
418#else
420 if (ll == (PY_LONG_LONG)-1 && PyErr_Occurred()) {
421 PyErr_Clear();
423 if (ull == (PY_ULONG_LONG)-1 && PyErr_Occurred()) {
424 PyErr_Clear();
425 tmpl_name.append("int"); // still out of range, will fail later
426 } else
427 tmpl_name.append("unsigned long long"); // since already failed long long
428 } else
429 tmpl_name.append((ll < INT_MIN || INT_MAX < ll) ? \
430 ((ll < LONG_MIN || LONG_MAX < ll) ? "long long" : "long") : "int");
431#endif
432 } else
433 tmpl_name.append("int");
434
435 return true;
436 }
437
438#if PY_VERSION_HEX < 0x03000000
439 if (tn == (PyObject*)&PyLong_Type) {
440 if (arg) {
442 if (ll == (PY_LONG_LONG)-1 && PyErr_Occurred()) {
443 PyErr_Clear();
445 if (ull == (PY_ULONG_LONG)-1 && PyErr_Occurred()) {
446 PyErr_Clear();
447 tmpl_name.append("long"); // still out of range, will fail later
448 } else
449 tmpl_name.append("unsigned long long"); // since already failed long long
450 } else
451 tmpl_name.append((ll < LONG_MIN || LONG_MAX < ll) ? "long long" : "long");
452 } else
453 tmpl_name.append("long");
454
455 return true;
456 }
457#endif
458
459 if (tn == (PyObject*)&PyFloat_Type) {
460 // special case for floats (Python-speak for double) if from argument (only)
461 tmpl_name.append(arg ? "double" : "float");
462 return true;
463 }
464
465#if PY_VERSION_HEX < 0x03000000
466 if (tn == (PyObject*)&PyString_Type) {
467#else
468 if (tn == (PyObject*)&PyUnicode_Type) {
469#endif
470 tmpl_name.append("std::string");
471 return true;
472 }
473
474 if (tn == (PyObject*)&PyList_Type || tn == (PyObject*)&PyTuple_Type) {
475 if (arg && PySequence_Size(arg)) {
476 std::string subtype{"std::initializer_list<"};
480 if (ret) {
481 tmpl_name.append(subtype);
482 tmpl_name.append(">");
483 }
485 // Error occurred in inner call to AddTypeName, which means it has
486 // also set a TypeError. We return and let the error propagate.
487 if (!ret) {
488 return false;
489 }
490 }
491
492 return true;
493 }
494
495 if (CPPScope_Check(tn)) {
497 if (arg) {
498 // try to specialize the type match for the given object
500 if (CPPInstance_Check(pyobj)) {
501 if (pyobj->fFlags & CPPInstance::kIsRValue)
502 tmpl_name.append("&&");
503 else {
504 if (pcnt) *pcnt += 1;
505 if ((pyobj->fFlags & CPPInstance::kIsReference) || pref == kPointer)
506 tmpl_name.push_back('*');
507 else if (pref != kValue)
508 tmpl_name.push_back('&');
509 }
510 }
511 }
512
513 return true;
514 }
515
516 if (tn == (PyObject*)&CPPOverload_Type) {
517 PyObject* tpName = arg ? \
518 PyObject_GetAttr(arg, PyStrings::gCppName) : \
519 CPyCppyy_PyText_FromString("void* (*)(...)");
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)) {
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) tpn << ", ";
541 PyObject* item = PyList_GET_ITEM(values, i);
542 tpn << (CPPScope_Check(item) ? full_scope(ClassName(item)) : AnnotationAsText(item));
543 }
544 Py_DECREF(values);
545
546 tpn << ')';
547 tmpl_name.append(tpn.str());
548
550 return true;
551
552 } else
553 PyErr_Clear();
554 }
556 } else
557 PyErr_Clear();
558
559 // ctypes function pointer
560 PyObject* argtypes = nullptr;
561 PyObject* ret = nullptr;
562 if ((argtypes = PyObject_GetAttrString(arg, "argtypes")) && (ret = PyObject_GetAttrString(arg, "restype"))) {
563 std::ostringstream tpn;
564 PyObject* pytc = PyObject_GetAttr(ret, PyStrings::gCTypesType);
565 tpn << CT2CppNameS(pytc, false)
566 << " (*)(";
568
569 for (Py_ssize_t i = 0; i < PySequence_Length(argtypes); ++i) {
570 if (i) tpn << ", ";
572 pytc = PyObject_GetAttr(item, PyStrings::gCTypesType);
573 tpn << CT2CppNameS(pytc, false);
576 }
577
578 tpn << ')';
579 tmpl_name.append(tpn.str());
580
581 Py_DECREF(ret);
583
584 return true;
585
586 } else {
587 PyErr_Clear();
590 }
591
592 // callable C++ type (e.g. std::function)
593 PyObject* tpName = PyObject_GetAttr(arg, PyStrings::gCppName);
594 if (tpName) {
596 tmpl_name.append(CPPScope_Check(arg) ? full_scope(cname) : cname);
598 return true;
599 }
600 PyErr_Clear();
601 }
602
603 for (auto nn : {PyStrings::gCppName, PyStrings::gName}) {
605 if (tpName) {
608 return true;
609 }
610 PyErr_Clear();
611 }
612
614 // last ditch attempt, works for things like int values; since this is a
615 // source of errors otherwise, it is limited to specific types and not
616 // generally used (str(obj) can print anything ...)
620 return true;
621 }
622
623// Give up with a TypeError
624
625// Try to get a readable representation of the argument
627 const char *repr_cstr = repr ? PyUnicode_AsUTF8(repr) : "<unprintable>";
628
629 PyErr_Format(PyExc_TypeError, "could not construct C++ name from template argument %s", repr_cstr);
630
632 return false;
633}
634
637{
638// Helper to construct the "<type, type, ...>" part of a templated name (either
639// for a class or method lookup
641
642// Note: directly appending to string is a lot faster than stringstream
643 std::string tmpl_name;
644 tmpl_name.reserve(128);
645 if (pyname)
647 tmpl_name.push_back('<');
648
649 if (pcnt) *pcnt = 0; // count number of times 'pref' is used
650
652 for (int i = argoff; i < nArgs; ++i) {
653 // add type as string to name
657 // some common numeric types (separated out for performance: checking for
658 // __cpp_name__ and/or __name__ is rather expensive)
659 } else {
660 if (!AddTypeName(tmpl_name, tn, (args ? PyTuple_GET_ITEM(args, i) : nullptr), pref, pcnt)) {
661 return "";
662 }
663 }
664
665 // add a comma, as needed (no space as internally, final names don't have them)
666 if (i != nArgs-1)
667 tmpl_name.push_back(',');
668 }
669
670// close template name
671 tmpl_name.push_back('>');
672
673 return tmpl_name;
674}
675
676//----------------------------------------------------------------------------
678{
679// helper to convert ctypes' `_type_` info to the equivalent C++ name
680 const char* name = "";
682 char tc = ((char*)CPyCppyy_PyText_AsString(pytc))[0];
683 switch (tc) {
684 case '?': name = "bool"; break;
685 case 'c': name = "char"; break;
686 case 'b': name = "char"; break;
687 case 'B': name = "unsigned char"; break;
688 case 'h': name = "short"; break;
689 case 'H': name = "unsigned short"; break;
690 case 'i': name = "int"; break;
691 case 'I': name = "unsigned int"; break;
692 case 'l': name = "long"; break;
693 case 'L': name = "unsigned long"; break;
694 case 'q': name = "long long"; break;
695 case 'Q': name = "unsigned long long"; break;
696 case 'f': name = "float"; break;
697 case 'd': name = "double"; break;
698 case 'g': name = "long double"; break;
699 case 'z': name = "const char*"; break;
700 default: name = (allow_voidp ? "void*" : nullptr); break;
701 }
702 }
703
704 return name;
705}
706
707//----------------------------------------------------------------------------
708static inline bool check_scope(const std::string& name)
709{
711}
712
714 const std::vector<std::string>& argtypes, std::ostringstream& code)
715{
716// Generate function setup to be used in callbacks (wrappers and overrides).
717 int nArgs = (int)argtypes.size();
718
719// return value and argument type converters
720 bool isVoid = retType == "void";
721 if (!isVoid)
722 code << " CPYCPPYY_STATIC std::unique_ptr<CPyCppyy::Converter, std::function<void(CPyCppyy::Converter*)>> "
723 "retconv{CPyCppyy::CreateConverter(\""
724 << retType << "\"), CPyCppyy::DestroyConverter};\n";
725 std::vector<bool> arg_is_ptr;
726 if (nArgs) {
727 arg_is_ptr.resize(nArgs);
728 code << " CPYCPPYY_STATIC std::vector<std::unique_ptr<CPyCppyy::Converter, std::function<void(CPyCppyy::Converter*)>>> argcvs;\n"
729 << " if (argcvs.empty()) {\n"
730 << " argcvs.reserve(" << nArgs << ");\n";
731 for (int i = 0; i < nArgs; ++i) {
732 arg_is_ptr[i] = false;
733 code << " argcvs.emplace_back(CPyCppyy::CreateConverter(\"";
734 const std::string& at = argtypes[i];
735 const std::string& res_at = Cppyy::ResolveName(at);
736 const std::string& cpd = TypeManip::compound(res_at);
737 if (!cpd.empty() && check_scope(res_at)) {
738 // in case of a pointer, the original argument needs to be used to ensure
739 // the pointer-value remains comparable
740 //
741 // in case of a reference, there is no extra indirection on the C++ side as
742 // would be when converting a data member, so adjust the converter
743 arg_is_ptr[i] = cpd.back() == '*';
744 if (arg_is_ptr[i] || cpd.back() == '&') {
745 code << res_at.substr(0, res_at.size()-1);
746 } else code << at;
747 } else
748 code << at;
749 code << "\"), CPyCppyy::DestroyConverter);\n";
750 }
751 code << " }\n";
752 }
753
754// declare return value (TODO: this does not work for most non-builtin values)
755 if (!isVoid)
756 code << " " << retType << " ret{};\n";
757
758// acquire GIL
759 code << " PyGILState_STATE state = PyGILState_Ensure();\n";
760
761// build argument tuple if needed
762 if (nArgs) {
763 code << " std::vector<PyObject*> pyargs;\n";
764 code << " pyargs.reserve(" << nArgs << ");\n"
765 << " try {\n";
766 for (int i = 0; i < nArgs; ++i) {
767 code << " pyargs.emplace_back(argcvs[" << i << "]->FromMemory((void*)";
768 if (!arg_is_ptr[i]) code << '&';
769 code << "arg" << i << "));\n"
770 << " if (!pyargs.back()) throw " << i << ";\n";
771 }
772 code << " } catch(int) {\n"
773 << " for (auto pyarg : pyargs) Py_XDECREF(pyarg);\n"
774 << " CPyCppyy::PyException pyexc; PyGILState_Release(state); throw pyexc;\n"
775 << " }\n";
776 }
777}
778
779void CPyCppyy::Utility::ConstructCallbackReturn(const std::string& retType, int nArgs, std::ostringstream& code)
780{
781// Generate code for return value conversion and error handling.
782 bool isVoid = retType == "void";
783 bool isPtr = Cppyy::ResolveName(retType).back() == '*';
784
785 if (nArgs)
786 code << " for (auto pyarg : pyargs) Py_DECREF(pyarg);\n";
787 code << " bool cOk = (bool)pyresult;\n"
788 " if (pyresult) {\n";
789 if (isPtr) {
790 // If the return type is a CPPInstance, owned by Python, and the ref-count down
791 // to 1, the return will hold a dangling pointer, so set it to nullptr instead.
792 code << " if (!CPyCppyy::Instance_IsLively(pyresult))\n"
793 " ret = nullptr;\n"
794 " else {\n";
795 }
796 code << (isVoid ? "" : " cOk = retconv->ToMemory(pyresult, (void*)&ret);\n")
797 << " Py_DECREF(pyresult);\n }\n";
798 if (isPtr) code << " }\n";
799 code << " if (!cOk) {" // assume error set when converter failed
800// TODO: On Windows, throwing a C++ exception here makes the code hang; leave
801// the error be which allows at least one layer of propagation
802#ifdef _WIN32
803 " /* do nothing */ }\n"
804#else
805 " CPyCppyy::PyException pyexc; PyGILState_Release(state); throw pyexc; }\n"
806#endif
807 " PyGILState_Release(state);\n"
808 " return";
809 code << (isVoid ? ";\n }\n" : " ret;\n }\n");
810}
811
812
813//----------------------------------------------------------------------------
814static std::map<void*, PyObject*> sStdFuncLookup;
815static std::map<std::string, PyObject*> sStdFuncMakerLookup;
817 const std::string& retType, const std::string& signature, void* address)
818{
819// Convert a function pointer to an equivalent std::function<> object.
820 static int maker_count = 0;
821
822 auto pf = sStdFuncLookup.find(address);
823 if (pf != sStdFuncLookup.end()) {
824 Py_INCREF(pf->second);
825 return pf->second;
826 }
827
828 PyObject* maker = nullptr;
829
831 if (pm == sStdFuncMakerLookup.end()) {
832 std::ostringstream fname;
833 fname << "ptr2func" << ++maker_count;
834
835 std::ostringstream code;
836 code << "namespace __cppyy_internal { std::function<"
837 << retType << signature << "> " << fname.str()
838 << "(intptr_t faddr) { return (" << retType << "(*)" << signature << ")faddr;} }";
839
840 if (!Cppyy::Compile(code.str())) {
841 PyErr_SetString(PyExc_TypeError, "conversion to std::function failed");
842 return nullptr;
843 }
844
845 PyObject* pyscope = CreateScopeProxy("__cppyy_internal");
846 maker = PyObject_GetAttrString(pyscope, fname.str().c_str());
848 if (!maker)
849 return nullptr;
850
851 // cache the new maker (TODO: does it make sense to use weakrefs?)
853 } else
854 maker = pm->second;
855
856 PyObject* args = PyTuple_New(1);
857 PyTuple_SET_ITEM(args, 0, PyLong_FromLongLong((intptr_t)address));
858 PyObject* func = PyObject_Call(maker, args, NULL);
859 Py_DECREF(args);
860
861 if (func) { // prevent moving this func object, since then it can not be reused
862 ((CPPInstance*)func)->fFlags |= CPPInstance::kIsLValue;
863 Py_INCREF(func); // TODO: use weak? The C++ maker doesn't go away either
864 sStdFuncLookup[address] = func;
865 }
866
867 return func;
868}
869
870
871//----------------------------------------------------------------------------
873{
874// Initialize a proxy class for use by python, and add it to the module.
875
876// finalize proxy type
877 if (PyType_Ready(pytype) < 0)
878 return false;
879
880// add proxy type to the given module
881 Py_INCREF(pytype); // PyModule_AddObject steals reference
882 if (PyModule_AddObject(module, (char*)name, (PyObject*)pytype) < 0) {
884 return false;
885 }
886
887// declare success
888 return true;
889}
890
891//----------------------------------------------------------------------------
892std::map<std::string, char> const &CPyCppyy::Utility::TypecodeMap()
893{
894 // See https://docs.python.org/3/library/array.html#array.array
895 static std::map<std::string, char> typecodeMap{
896 {"char", 'b'},
897 {"unsigned char", 'B'},
898#if PY_VERSION_HEX < 0x03100000
899 {"wchar_t", 'u'},
900#endif
901#if PY_VERSION_HEX >= 0x030d0000
902 {"Py_UCS4", 'w'},
903#endif
904 {"short", 'h'},
905 {"unsigned short", 'H'},
906 {"int", 'i'},
907 {"unsigned int", 'I'},
908 {"long", 'l'},
909 {"unsigned long", 'L'},
910 {"long long", 'q'},
911 {"unsigned long long", 'Q'},
912 {"float", 'f'},
913 {"double", 'd'}
914 };
915 return typecodeMap;
916}
917
918//----------------------------------------------------------------------------
920{
921// Retrieve a linear buffer pointer from the given pyobject.
922
923// special case: don't handle character strings here (yes, they're buffers, but not quite)
925 return 0;
926
927// special case: bytes array
928 if ((!check || tc == '*' || tc == 'B') && PyByteArray_CheckExact(pyobject)) {
931 }
932
933// new-style buffer interface
936 return 0; // PyObject_GetBuffer() crashes on some platforms for some zero-sized seqeunces
937 PyErr_Clear();
939 memset(&bufinfo, 0, sizeof(Py_buffer));
941 if (tc == '*' || strchr(bufinfo.format, tc)
942 // if `long int` and `int` are the same size (on Windows and 32bit Linux,
943 // for example), `ctypes` isn't too picky about the type format, so make
944 // sure both integer types pass the type check
945 || (sizeof(long int) == sizeof(int) && ((tc == 'I' && strchr(bufinfo.format, 'L')) ||
946 (tc == 'i' && strchr(bufinfo.format, 'l'))))
947 // complex float is 'Zf' in bufinfo.format, but 'z' in single char
948 || (tc == 'z' && strstr(bufinfo.format, "Zf"))
949 // allow 'signed char' ('b') from array to pass through '?' (bool as from struct)
950 || (tc == '?' && strchr(bufinfo.format, 'b'))
951 ) {
952 buf = bufinfo.buf;
953
954 if (check && bufinfo.itemsize != size) {
956 "buffer itemsize (%ld) does not match expected size (%d)", bufinfo.itemsize, size);
958 return 0;
959 }
960
961 Py_ssize_t buflen = 0;
962 if (buf && bufinfo.ndim == 0)
963 buflen = bufinfo.len/bufinfo.itemsize;
964 else if (buf && bufinfo.ndim == 1)
965 buflen = bufinfo.shape ? bufinfo.shape[0] : bufinfo.len/bufinfo.itemsize;
967 if (buflen)
968 return buflen;
969 } else {
970 // have buf, but format mismatch: bail out now, otherwise the old
971 // code will return based on itemsize match
973 return 0;
974 }
975 } else if (bufinfo.obj)
977 PyErr_Clear();
978 }
979
980// attempt to retrieve pointer through old-style buffer interface
981 PyBufferProcs* bufprocs = Py_TYPE(pyobject)->tp_as_buffer;
982
983 PySequenceMethods* seqmeths = Py_TYPE(pyobject)->tp_as_sequence;
984 if (seqmeths != 0 && bufprocs != 0
985#if PY_VERSION_HEX < 0x03000000
986 && bufprocs->bf_getwritebuffer != 0
987 && (*(bufprocs->bf_getsegcount))(pyobject, 0) == 1
988#else
989 && bufprocs->bf_getbuffer != 0
990#endif
991 ) {
992
993 // get the buffer
994#if PY_VERSION_HEX < 0x03000000
995 Py_ssize_t buflen = (*(bufprocs->bf_getwritebuffer))(pyobject, 0, &buf);
996#else
998 (*(bufprocs->bf_getbuffer))(pyobject, &bufinfo, PyBUF_WRITABLE);
999 buf = (char*)bufinfo.buf;
1000 Py_ssize_t buflen = bufinfo.len;
1002#endif
1003
1004 if (buf && check == true) {
1005 // determine buffer compatibility (use "buf" as a status flag)
1006 PyObject* pytc = tc != '*' ? PyObject_GetAttr(pyobject, PyStrings::gTypeCode) : nullptr;
1007 if (pytc != 0) { // for array objects
1009 if (!(cpytc == tc || (tc == '?' && cpytc == 'b')))
1010 buf = 0; // no match
1011 Py_DECREF(pytc);
1012 } else if (seqmeths->sq_length &&
1013 (int)(buflen/(*(seqmeths->sq_length))(pyobject)) == size) {
1014 // this is a gamble ... may or may not be ok, but that's for the user
1015 PyErr_Clear();
1016 } else if (buflen == size) {
1017 // also a gamble, but at least 1 item will fit into the buffer, so very likely ok ...
1018 PyErr_Clear();
1019 } else {
1020 buf = 0; // not compatible
1021
1022 // clarify error message
1023 auto error = FetchPyError();
1025 (char*)"%s and given element size (%ld) do not match needed (%d)",
1026 CPyCppyy_PyText_AsString(error.fValue.get()),
1027 seqmeths->sq_length ? (long)(buflen/(*(seqmeths->sq_length))(pyobject)) : (long)buflen,
1028 size);
1029 error.fValue.reset(pyvalue2);
1030 RestorePyError(error);
1031 }
1032 }
1033
1034 if (!buf) return 0;
1035 return buflen/(size ? size : 1);
1036 }
1037
1038 return 0;
1039}
1040
1041//----------------------------------------------------------------------------
1042std::string CPyCppyy::Utility::MapOperatorName(const std::string& name, bool bTakesParams, bool* stubbed)
1043{
1044// Map the given C++ operator name on the python equivalent.
1045 if (8 < name.size() && name.substr(0, 8) == "operator") {
1046 std::string op = name.substr(8, std::string::npos);
1047
1048 // stripping ...
1049 std::string::size_type start = 0, end = op.size();
1050 while (start < end && isspace(op[start])) ++start;
1051 while (start < end && isspace(op[end-1])) --end;
1052 op = op.substr(start, end - start);
1053
1054 // certain operators should be removed completely (e.g. operator delete & friends)
1055 if (gOpRemove.find(op) != gOpRemove.end())
1056 return "";
1057
1058 // check first if none, to prevent spurious deserializing downstream
1059 TC2POperatorMapping_t::iterator pop = gC2POperatorMapping.find(op);
1060 if (pop == gC2POperatorMapping.end() && gOpSkip.find(op) == gOpSkip.end()) {
1062 pop = gC2POperatorMapping.find(op);
1063 }
1064
1065 // map C++ operator to python equivalent, or made up name if no equivalent exists
1066 if (pop != gC2POperatorMapping.end()) {
1067 return pop->second;
1068
1069 } else if (op == "*") {
1070 // dereference v.s. multiplication of two instances
1071 if (!bTakesParams) return "__deref__";
1072 if (stubbed) *stubbed = true;
1073 return "__mul__";
1074
1075 } else if (op == "/") {
1076 // no unary, but is stubbed
1077 return CPPYY__div__;
1078
1079 } else if (op == "+") {
1080 // unary positive v.s. addition of two instances
1081 if (!bTakesParams) return "__pos__";
1082 if (stubbed) *stubbed = true;
1083 return "__add__";
1084
1085 } else if (op == "-") {
1086 // unary negative v.s. subtraction of two instances
1087 if (!bTakesParams) return "__neg__";
1088 if (stubbed) *stubbed = true;
1089 return "__sub__";
1090
1091 } else if (op == "++") {
1092 // prefix v.s. postfix increment
1093 return bTakesParams ? "__postinc__" : "__preinc__";
1094
1095 } else if (op == "--") {
1096 // prefix v.s. postfix decrement
1097 return bTakesParams ? "__postdec__" : "__predec__";
1098 }
1099
1100 }
1101
1102// might get here, as not all operator methods are handled (new, delete, etc.)
1103 return name;
1104}
1105
1106//----------------------------------------------------------------------------
1108{
1109// Retrieve the class name from the given Python instance.
1110 std::string clname = "<unknown>";
1112 PyObject* pyname = PyObject_GetAttr(pyclass, PyStrings::gCppName);
1113 if (!pyname) {
1114 PyErr_Clear();
1115 pyname = PyObject_GetAttr(pyclass, PyStrings::gName);
1116 }
1117
1118 if (pyname) {
1121 } else
1122 PyErr_Clear();
1123 return clname;
1124}
1125
1126//----------------------------------------------------------------------------
1127static std::set<std::string> sIteratorTypes;
1128bool CPyCppyy::Utility::IsSTLIterator(const std::string& classname)
1129{
1130// attempt to recognize STL iterators (TODO: probably belongs in the backend), using
1131// a couple of common container classes with different iterator protocols (note that
1132// mapping iterators are handled separately in the pythonizations) as exemplars (the
1133// actual, resolved, names will be compiler-specific) that are picked b/c they are
1134// baked into the CoreLegacy dictionary
1135 if (sIteratorTypes.empty()) {
1136 std::string tt = "<int>::";
1137 for (auto c : {"std::vector", "std::list", "std::deque"}) {
1138 for (auto i : {"iterator", "const_iterator"}) {
1139 const std::string& itname = Cppyy::ResolveName(c+tt+i);
1140 auto pos = itname.find('<');
1141 if (pos != std::string::npos)
1142 sIteratorTypes.insert(itname.substr(0, pos));
1143 }
1144 }
1145 }
1146
1147 auto pos = classname.find('<');
1148 if (pos != std::string::npos)
1149 return sIteratorTypes.find(classname.substr(0, pos)) != sIteratorTypes.end();
1150 return false;
1151}
1152
1153
1154//----------------------------------------------------------------------------
1165
1166
1167//----------------------------------------------------------------------------
1169{
1170// Re-acquire the GIL before calling PyErr_Occurred() in case it has been
1171// released; note that the p2.2 code assumes that there are no callbacks in
1172// C++ to python (or at least none returning errors).
1173#if PY_VERSION_HEX >= 0x02030000
1174 PyGILState_STATE gstate = PyGILState_Ensure();
1177#else
1178 if (PyThreadState_GET())
1179 return PyErr_Occurred();
1180 PyObject* e = 0;
1181#endif
1182
1183 return e;
1184}
1185
1186
1187//----------------------------------------------------------------------------
1189{
1190 // create a PyError_t RAII object that will capture and store the exception data
1192#if PY_VERSION_HEX >= 0x030c0000
1193 error.fValue.reset(PyErr_GetRaisedException());
1194#else
1195 PyObject *pytype = nullptr;
1196 PyObject *pyvalue = nullptr;
1197 PyObject *pytrace = nullptr;
1199 error.fType.reset(pytype);
1200 error.fValue.reset(pyvalue);
1201 error.fTrace.reset(pytrace);
1202#endif
1203 return error;
1204}
1205
1206
1207//----------------------------------------------------------------------------
1209{
1210#if PY_VERSION_HEX >= 0x030c0000
1211 PyErr_SetRaisedException(error.fValue.release());
1212#else
1213 PyErr_Restore(error.fType.release(), error.fValue.release(), error.fTrace.release());
1214#endif
1215}
1216
1217
1218//----------------------------------------------------------------------------
1219size_t CPyCppyy::Utility::FetchError(std::vector<PyError_t>& errors, bool is_cpp)
1220{
1221// Fetch the current python error, if any, and store it for future use.
1222 if (PyErr_Occurred()) {
1223 errors.emplace_back(FetchPyError());
1224 errors.back().fIsCpp = is_cpp;
1225 }
1226 return errors.size();
1227}
1228
1229//----------------------------------------------------------------------------
1231{
1232// Use the collected exceptions to build up a detailed error log.
1233 if (errors.empty()) {
1234 // should not happen ...
1237 return;
1238 }
1239
1240// if a _single_ exception was thrown from C++, assume it has priority (see below)
1241 PyError_t* unique_from_cpp = nullptr;
1242 for (auto& e : errors) {
1243 if (e.fIsCpp) {
1244 if (!unique_from_cpp)
1245 unique_from_cpp = &e;
1246 else {
1247 // two C++ exceptions, resort to default behavior
1248 unique_from_cpp = nullptr;
1249 break;
1250 }
1251 }
1252 }
1253
1254 if (unique_from_cpp) {
1255 // report only this error; the idea here is that all other errors come from
1256 // the bindings (e.g. argument conversion errors), while the exception from
1257 // C++ means that it originated from an otherwise successful call
1258
1259 // bind the original C++ object, rather than constructing from topmsg, as it
1260 // is expected to have informative state
1262 } else {
1263 // try to consolidate Python exceptions, otherwise select default
1264 PyObject* exc_type = nullptr;
1265 for (auto& e : errors) {
1266#if PY_VERSION_HEX >= 0x030c0000
1267 PyObject* pytype = (PyObject*)Py_TYPE(e.fValue.get());
1268#else
1269 PyObject* pytype = e.fType.get();
1270#endif
1271 if (!exc_type) exc_type = pytype;
1272 else if (exc_type != pytype) {
1273 exc_type = defexc;
1274 break;
1275 }
1276 }
1277
1278 // add the details to the topmsg
1279 PyObject* separator = CPyCppyy_PyText_FromString("\n ");
1280 for (auto& e : errors) {
1281 PyObject *pyvalue = e.fValue.get();
1282 CPyCppyy_PyText_Append(&topmsg, separator);
1285 } else if (pyvalue) {
1287 if (!excstr) {
1288 PyErr_Clear();
1290 }
1292 } else {
1294 CPyCppyy_PyText_FromString("unknown exception"));
1295 }
1296 }
1297
1298 Py_DECREF(separator);
1299
1300 // set the python exception
1302 }
1303
1305}
1306
1307
1308//----------------------------------------------------------------------------
1309static bool includesDone = false;
1311{
1312// setup Python API for callbacks
1313 if (!includesDone) {
1314 bool okay = Cppyy::Compile(
1315 // basic API (converters etc.)
1316 "#include \"CPyCppyy/API.h\"\n"
1317
1318 // utilities from the CPyCppyy public API
1319 "#include \"CPyCppyy/DispatchPtr.h\"\n"
1320 "#include \"CPyCppyy/PyException.h\"\n"
1321 );
1323 }
1324
1325 return includesDone;
1326}
#define Py_TYPE(ob)
Definition CPyCppyy.h:196
#define CPPYY__long__
Definition CPyCppyy.h:109
#define CPPYY__div__
Definition CPyCppyy.h:111
#define CPyCppyy_PyText_InternFromString
Definition CPyCppyy.h:82
int Py_ssize_t
Definition CPyCppyy.h:215
#define PyBytes_Check
Definition CPyCppyy.h:61
#define CPyCppyy_PyText_Append
Definition CPyCppyy.h:83
#define CPyCppyy_PyText_AsString
Definition CPyCppyy.h:76
#define CPyCppyy_PyText_AppendAndDel
Definition CPyCppyy.h:84
#define CPyCppyy_PyText_FromFormat
Definition CPyCppyy.h:80
#define CPyCppyy_PyText_FromString
Definition CPyCppyy.h:81
#define CPPYY__idiv__
Definition CPyCppyy.h:110
#define CPyCppyy_PyText_Check
Definition CPyCppyy.h:74
_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
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
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
char name[80]
Definition TGX11.cxx:146
static std::set< std::string > sIteratorTypes
Definition Utility.cxx:1127
static bool includesDone
Definition Utility.cxx:1309
static TC2POperatorMapping_t gC2POperatorMapping
Definition Utility.cxx:31
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:264
static std::set< std::string > gOpRemove
Definition Utility.cxx:33
static std::map< std::string, PyObject * > sStdFuncMakerLookup
Definition Utility.cxx:815
static std::set< std::string > gOpSkip
Definition Utility.cxx:32
static std::map< void *, PyObject * > sStdFuncLookup
Definition Utility.cxx:814
static bool AddTypeName(std::string &tmpl_name, PyObject *tn, PyObject *arg, CPyCppyy::Utility::ArgPreference pref, int *pcnt=nullptr)
Definition Utility.cxx:405
static bool check_scope(const std::string &name)
Definition Utility.cxx:708
std::map< std::string, std::string > TC2POperatorMapping_t
Definition Utility.cxx:30
static std::string AnnotationAsText(PyObject *pyobj)
Definition Utility.cxx:389
const char * proto
Definition civetweb.c:18822
const_iterator end() const
unsigned long long PY_ULONG_LONG
Definition cpp_cppyy.h:24
std::string clean_type(const std::string &cppname, bool template_strip=true, bool const_strip=true)
PyCallable * FindBinaryOperator(PyObject *left, PyObject *right, const char *op, Cppyy::TCppScope_t scope=0)
Definition Utility.cxx:297
std::string CT2CppNameS(PyObject *pytc, bool allow_voidp)
Definition Utility.cxx:677
void ConstructCallbackPreamble(const std::string &retType, const std::vector< std::string > &argtypes, std::ostringstream &code)
Definition Utility.cxx:713
void ConstructCallbackReturn(const std::string &retType, int nArgs, std::ostringstream &code)
Definition Utility.cxx:779
void RestorePyError(PyError_t &error)
Definition Utility.cxx:1208
std::map< std::string, char > const & TypecodeMap()
Definition Utility.cxx:892
void SetDetailedException(std::vector< PyError_t > &&errors, PyObject *topmsg, PyObject *defexc)
Definition Utility.cxx:1230
Py_ssize_t GetBuffer(PyObject *pyobject, char tc, int size, void *&buf, bool check=true)
Definition Utility.cxx:919
std::string ConstructTemplateArgs(PyObject *pyname, PyObject *tpArgs, PyObject *args=nullptr, ArgPreference=kNone, int argoff=0, int *pcnt=nullptr)
Definition Utility.cxx:635
PyObject * FuncPtr2StdFunction(const std::string &retType, const std::string &signature, void *address)
Definition Utility.cxx:816
PyCallable * FindUnaryOperator(PyObject *pyclass, const char *op)
Definition Utility.cxx:282
size_t FetchError(std::vector< PyError_t > &, bool is_cpp=false)
Definition Utility.cxx:1219
std::string MapOperatorName(const std::string &name, bool bTakesParames, bool *stubbed=nullptr)
Definition Utility.cxx:1042
bool InitProxy(PyObject *module, PyTypeObject *pytype, const char *name)
Definition Utility.cxx:872
bool AddToClass(PyObject *pyclass, const char *label, PyCFunction cfunc, int flags=METH_VARARGS)
Definition Utility.cxx:185
PyError_t FetchPyError()
Definition Utility.cxx:1188
bool IsSTLIterator(const std::string &classname)
Definition Utility.cxx:1128
std::string ClassName(PyObject *pyobj)
Definition Utility.cxx:1107
PyObject * PyErr_Occurred_WithGIL()
Definition Utility.cxx:1168
CPPOverload * CPPOverload_New(const std::string &name, std::vector< PyCallable * > &methods)
unsigned long PyLongOrInt_AsULong(PyObject *pyobject)
Definition Utility.cxx:132
PyObject * gDefaultObject
PyObject * CustomInstanceMethod_New(PyObject *func, PyObject *self, PyObject *pyclass)
bool gDictLookupActive
Definition Utility.cxx:27
PyObject * CreateScopeProxy(Cppyy::TCppScope_t, const unsigned flags=0)
bool CPPOverload_Check(T *object)
Definition CPPOverload.h:94
bool CPPScope_Check(T *object)
Definition CPPScope.h:81
PY_ULONG_LONG PyLongOrInt_AsULong64(PyObject *pyobject)
Definition Utility.cxx:159
bool CPPInstance_Check(T *object)
PyObject * gNullPtrObject
size_t TCppIndex_t
Definition cpp_cppyy.h:40
intptr_t TCppMethod_t
Definition cpp_cppyy.h:38
RPY_EXPORTED bool Compile(const std::string &code, bool silent=false)
RPY_EXPORTED TCppScope_t gGlobalScope
Definition cpp_cppyy.h:69
RPY_EXPORTED TCppMethod_t GetMethodTemplate(TCppScope_t scope, const std::string &name, const std::string &proto)
RPY_EXPORTED std::string ResolveName(const std::string &cppitem_name)
RPY_EXPORTED std::string GetScopedFinalName(TCppType_t type)
RPY_EXPORTED TCppMethod_t GetMethod(TCppScope_t scope, TCppIndex_t imeth)
RPY_EXPORTED TCppScope_t GetScope(const std::string &scope_name)
size_t TCppScope_t
Definition cpp_cppyy.h:34
RPY_EXPORTED TCppIndex_t GetGlobalOperator(TCppType_t scope, const std::string &lc, const std::string &rc, const std::string &op)
std::unique_ptr< PyObject, PyObjectDeleter > fTrace
Definition Utility.h:109
std::unique_ptr< PyObject, PyObjectDeleter > fValue
Definition Utility.h:111
std::unique_ptr< PyObject, PyObjectDeleter > fType
Definition Utility.h:108
TLine l
Definition textangle.C:4
auto * tt
Definition textangle.C:16