4#include "structmember.h"
5#if PY_VERSION_HEX < 0x02050000
7#elif PY_VERSION_HEX < 0x030b0000
12#define CO_NOFREE 0x0040
34#ifndef CPPOverload_MAXFREELIST
35#define CPPOverload_MAXFREELIST 32
40class TPythonCallback :
public PyCallable {
46 if (!PyCallable_Check(callable)) {
47 PyErr_SetString(PyExc_TypeError,
"parameter must be callable");
54 virtual ~TPythonCallback() {
59 virtual PyObject* GetSignature(
bool =
true) {
62 virtual PyObject* GetPrototype(
bool =
true) {
66 if (PyObject_HasAttrString(fCallable,
"__doc__")) {
67 return PyObject_GetAttrString(fCallable,
"__doc__");
69 return GetPrototype();
73 virtual int GetPriority() {
return 100; };
74 virtual bool IsGreedy() {
return false; };
76 virtual int GetMaxArgs() {
return 100; };
80 virtual PyObject* GetArgDefault(
int ) {
92 virtual PyCallable* Clone() {
return new TPythonCallback(*
this); }
100 newArgs = PyTuple_New(nargs+1);
102 PyTuple_SET_ITEM(newArgs, 0, (
PyObject*)self);
103 for (
Py_ssize_t iarg = 0; iarg < nargs; ++iarg) {
104 PyObject* pyarg = PyTuple_GET_ITEM(args, iarg);
106 PyTuple_SET_ITEM(newArgs, iarg+1, pyarg);
112 return PyObject_Call(fCallable, newArgs, kwds);
117static inline bool IsPseudoFunc(CPPOverload* pymeth)
123static int PriorityCmp(PyCallable* left, PyCallable* right)
125 return left->GetPriority() > right->GetPriority();
129static inline void ResetCallState(CPPInstance*& selfnew, CPPInstance* selfold,
bool clear)
131 if (selfnew != selfold) {
141static inline PyObject* HandleReturn(
149 if (
IsCreator(pymeth->fMethodInfo->fFlags)) {
154 pymeth->fSelf->PythonOwns();
159 ((CPPInstance*)
result)->PythonOwns();
169 CPPInstance* cppself = (CPPInstance*)pymeth->fSelf;
170 CPPInstance* cppres = (CPPInstance*)
result;
177 ptrdiff_t
offset = (ptrdiff_t)cppres->GetObject() - (ptrdiff_t)cppself->GetObject();
199 ResetCallState(pymeth->fSelf, oldSelf,
false);
206static PyObject* mp_name(CPPOverload* pymeth,
void*)
212static PyObject* mp_module(CPPOverload* ,
void*)
219static PyObject* mp_doc(CPPOverload* pymeth,
void*)
225 CPPOverload::Methods_t::size_type nMethods = methods.size();
228 PyObject* doc = methods[0]->GetDocString();
236 for (CPPOverload::Methods_t::size_type i = 1; i < nMethods; ++i) {
240 Py_DECREF(separator);
246static PyObject* mp_meth_func(CPPOverload* pymeth,
void*)
252 *pymeth->fMethodInfo->fRefCount += 1;
253 newPyMeth->fMethodInfo = pymeth->fMethodInfo;
263static PyObject* mp_meth_self(CPPOverload* pymeth,
void*)
267 if (IsPseudoFunc(pymeth)) {
268 PyErr_Format(PyExc_AttributeError,
269 "function %s has no attribute \'im_self\'", pymeth->fMethodInfo->fName.c_str());
271 }
else if (pymeth->fSelf != 0) {
272 Py_INCREF((
PyObject*)pymeth->fSelf);
280static PyObject* mp_meth_class(CPPOverload* pymeth,
void*)
284 if (!IsPseudoFunc(pymeth) && pymeth->fMethodInfo->fMethods.size()) {
285 PyObject* pyclass = pymeth->fMethodInfo->fMethods[0]->GetScopeProxy();
287 PyErr_Format(PyExc_AttributeError,
288 "function %s has no attribute \'im_class\'", pymeth->fMethodInfo->fName.c_str());
296static PyObject* mp_func_closure(CPPOverload* ,
void*)
303static PyObject* mp_func_code(CPPOverload* pymeth,
void*)
306#if PY_VERSION_HEX < 0x03000000
311 PyObject* co_varnames = methods.size() == 1 ? methods[0]->GetCoVarNames() :
nullptr;
314 co_varnames = PyTuple_New(1 + 1 );
319 int co_argcount = (
int)PyTuple_Size(co_varnames);
322 PyObject* co_code = PyString_FromStringAndSize(
"d\x00\x00S", 4);
325 PyObject* co_consts = PyTuple_New(0);
326 PyObject* co_names = PyTuple_New(0);
329 PyObject* co_unused = PyTuple_New(0);
332 PyObject* co_filename = PyString_FromString(
"cppyy.py");
335 PyObject* co_name = PyString_FromString(pymeth->GetName().c_str());
340 PyObject* co_lnotab = PyString_FromString(
"\x00\x01\x0c\x01");
358 Py_DECREF(co_lnotab);
360 Py_DECREF(co_unused);
361 Py_DECREF(co_filename);
362 Py_DECREF(co_varnames);
364 Py_DECREF(co_consts);
376static PyObject* mp_func_defaults(CPPOverload* pymeth,
void*)
382 if (methods.size() != 1)
383 return PyTuple_New(0);
385 int maxarg = methods[0]->GetMaxArgs();
387 PyObject* defaults = PyTuple_New(maxarg);
390 for (
int iarg = 0; iarg < maxarg; ++iarg) {
391 PyObject* defvalue = methods[0]->GetArgDefault(iarg);
393 PyTuple_SET_ITEM(defaults, itup++, defvalue);
395 _PyTuple_Resize(&defaults, itup);
401static PyObject* mp_func_globals(CPPOverload* ,
void*)
405 PyObject* pyglobal = PyModule_GetDict(PyImport_AddModule((
char*)
"cppyy"));
406 Py_XINCREF(pyglobal);
415 pymeth->fMethodInfo->fFlags &= ~flag;
419 long istrue = PyLong_AsLong(
value);
420 if (istrue == -1 && PyErr_Occurred()) {
421 PyErr_Format(PyExc_ValueError,
"a boolean 1 or 0 is required for %s",
name);
426 pymeth->fMethodInfo->fFlags |= flag;
428 pymeth->fMethodInfo->fFlags &= ~flag;
434static PyObject* mp_getcreates(CPPOverload* pymeth,
void*)
437 return PyInt_FromLong((
long)
IsCreator(pymeth->fMethodInfo->fFlags));
441static int mp_setcreates(CPPOverload* pymeth,
PyObject*
value,
void*)
448static PyObject* mp_getmempolicy(CPPOverload* pymeth,
void*)
457 return PyInt_FromLong(-1);
461static int mp_setmempolicy(CPPOverload* pymeth,
PyObject*
value,
void*)
464 long mempolicy = PyLong_AsLong(
value);
467 pymeth->fMethodInfo->fFlags &= ~CallContext::kUseStrict;
470 pymeth->fMethodInfo->fFlags &= ~CallContext::kUseHeuristics;
472 PyErr_SetString(PyExc_ValueError,
473 "expected kMemoryStrict or kMemoryHeuristics as value for __mempolicy__");
482#define CPPYY_BOOLEAN_PROPERTY(name, flag, label) \
483static PyObject* mp_get##name(CPPOverload* pymeth, void*) { \
484 if (pymeth->fMethodInfo->fFlags & flag) { \
490static int mp_set##name(CPPOverload* pymeth, PyObject* value, void*) { \
491 return set_flag(pymeth, value, flag, label); \
500static PyGetSetDef mp_getset[] = {
501 {(
char*)
"__name__", (getter)mp_name,
nullptr,
nullptr,
nullptr},
502 {(
char*)
"__module__", (getter)mp_module,
nullptr,
nullptr,
nullptr},
503 {(
char*)
"__doc__", (getter)mp_doc,
nullptr,
nullptr,
nullptr},
508 {(
char*)
"im_func", (getter)mp_meth_func,
nullptr,
nullptr,
nullptr},
509 {(
char*)
"im_self", (getter)mp_meth_self,
nullptr,
nullptr,
nullptr},
510 {(
char*)
"im_class", (getter)mp_meth_class,
nullptr,
nullptr,
nullptr},
512 {(
char*)
"func_closure", (getter)mp_func_closure,
nullptr,
nullptr,
nullptr},
513 {(
char*)
"func_code", (getter)mp_func_code,
nullptr,
nullptr,
nullptr},
514 {(
char*)
"func_defaults", (getter)mp_func_defaults,
nullptr,
nullptr,
nullptr},
515 {(
char*)
"func_globals", (getter)mp_func_globals,
nullptr,
nullptr,
nullptr},
516 {(
char*)
"func_doc", (getter)mp_doc,
nullptr,
nullptr,
nullptr},
517 {(
char*)
"func_name", (getter)mp_name,
nullptr,
nullptr,
nullptr},
519 {(
char*)
"__creates__", (getter)mp_getcreates, (setter)mp_setcreates,
520 (
char*)
"For ownership rules of result: if true, objects are python-owned",
nullptr},
521 {(
char*)
"__mempolicy__", (getter)mp_getmempolicy, (setter)mp_setmempolicy,
522 (
char*)
"For argument ownership rules: like global, either heuristic or strict",
nullptr},
523 {(
char*)
"__set_lifeline__", (getter)mp_getlifeline, (setter)mp_setlifeline,
524 (
char*)
"If true, set a lifeline from the return value onto self",
nullptr},
525 {(
char*)
"__release_gil__", (getter)mp_getthreaded, (setter)mp_setthreaded,
526 (
char*)
"If true, releases GIL on call into C++",
nullptr},
527 {(
char*)
"__useffi__", (getter)mp_getuseffi, (setter)mp_setuseffi,
528 (
char*)
"not implemented",
nullptr},
529 {(
char*)
"__sig2exc__", (getter)mp_getsig2exc, (setter)mp_setsig2exc,
530 (
char*)
"If true, turn signals into Python exceptions",
nullptr},
531 {(
char*)
nullptr,
nullptr,
nullptr,
nullptr,
nullptr}
539 CPPInstance* oldSelf = pymeth->fSelf;
542 auto& methods = pymeth->fMethodInfo->fMethods;
544 CPPOverload::Methods_t::size_type nMethods = methods.size();
547 const auto mflags = pymeth->fMethodInfo->fFlags;
553 ctxt.fPyContext = (
PyObject*)pymeth->fSelf;
556 if (kwds && PyDict_CheckExact(kwds) && PyDict_Size(kwds) != 0) {
559 if (!PyDict_Size(kwds)) kwds =
nullptr;
567 PyObject*
result = methods[0]->Call(pymeth->fSelf, args, kwds, &ctxt);
568 return HandleReturn(pymeth, oldSelf,
result);
575 auto& dispatchMap = pymeth->fMethodInfo->fDispatchMap;
576 PyCallable* memoized_pc =
nullptr;
577 for (
const auto&
p : dispatchMap) {
578 if (
p.first == sighash) {
579 memoized_pc =
p.second;
584 PyObject*
result = memoized_pc->Call(pymeth->fSelf, args, kwds, &ctxt);
596 std::stable_sort(methods.begin(), methods.end(), PriorityCmp);
600 std::vector<Utility::PyError_t> errors;
601 std::vector<bool> implicit_possible(methods.size());
602 for (
int stage = 0; stage < 2; ++stage) {
603 bool bHaveImplicit =
false;
604 for (CPPOverload::Methods_t::size_type i = 0; i < nMethods; ++i) {
605 if (stage && !implicit_possible[i])
608 PyObject*
result = methods[i]->Call(pymeth->fSelf, args, kwds, &ctxt);
612 dispatchMap.push_back(std::make_pair(sighash, methods[i]));
616 for (
auto&
p : dispatchMap) {
617 if (
p.first == sighash) {
618 p.second = methods[i];
627 return HandleReturn(pymeth, oldSelf,
result);
637 if (!PyErr_Occurred()) {
639 PyObject* sig = methods[i]->GetPrototype();
640 PyErr_Format(PyExc_SystemError,
"%s =>\n %s",
647 bHaveImplicit =
true;
648 implicit_possible[i] =
true;
649 ctxt.fFlags &= ~CallContext::kHaveImplicit;
651 implicit_possible[i] =
false;
652 ResetCallState(pymeth->fSelf, oldSelf,
false);
664 "none of the %d overloaded methods succeeded. Full details:", (
int)nMethods);
672static PyObject* mp_str(CPPOverload* cppinst)
675 std::ostringstream s;
676 s <<
"<C++ overload \"" << cppinst->fMethodInfo->fName <<
"\" at " << (
void*)cppinst <<
">";
681static CPPOverload* mp_descrget(CPPOverload* pymeth, CPPInstance* pyobj,
PyObject*)
691 if (newPyMeth != NULL) {
692 free_list = (CPPOverload*)(newPyMeth->fSelf);
703 *pymeth->fMethodInfo->fRefCount += 1;
704 newPyMeth->fMethodInfo = pymeth->fMethodInfo;
708 newPyMeth->fSelf = pyobj;
710 PyObject_GC_Track(newPyMeth);
720 pymeth->fSelf =
nullptr;
721 pymeth->fMethodInfo =
new CPPOverload::MethodInfo_t;
723 PyObject_GC_Track(pymeth);
728static void mp_dealloc(CPPOverload* pymeth)
731 PyObject_GC_UnTrack(pymeth);
733 Py_CLEAR(pymeth->fSelf);
735 if (--(*pymeth->fMethodInfo->fRefCount) <= 0) {
736 delete pymeth->fMethodInfo;
745 PyObject_GC_Del(pymeth);
754 return _Py_HashPointer(pymeth->fMethodInfo);
758static int mp_traverse(CPPOverload* pymeth, visitproc visit,
void* args)
762 return visit((
PyObject*)pymeth->fSelf, args);
768static int mp_clear(CPPOverload* pymeth)
771 Py_CLEAR(pymeth->fSelf);
777static PyObject* mp_richcompare(CPPOverload* self, CPPOverload* other,
int op)
785 if ((
Py_TYPE(self) ==
Py_TYPE(other) && self->fMethodInfo == other->fMethodInfo) && \
786 ((IsPseudoFunc(self) && IsPseudoFunc(other)) || self->fSelf == other->fSelf)) {
797 const char* sigarg =
nullptr;
800 Py_ssize_t args_size = PyTuple_GET_SIZE(args);
802 PyArg_ParseTuple(args,
const_cast<char*
>(
"s|i:__overload__"), &sigarg, &want_const)) {
803 want_const = args_size == 1 ? -1 : want_const;
804 return pymeth->FindOverload(sigarg ? sigarg :
"", want_const);
805 }
else if (args_size &&
806 PyArg_ParseTuple(args,
const_cast<char*
>(
"O|i:__overload__"), &sigarg_tuple, &want_const)) {
808 want_const = args_size == 1 ? -1 : want_const;
809 return pymeth->FindOverload(sigarg_tuple, want_const);
811 PyErr_Format(PyExc_TypeError,
"Unexpected arguments to __overload__");
817static PyObject* mp_add_overload(CPPOverload* pymeth,
PyObject* new_overload)
819 TPythonCallback* cb =
new TPythonCallback(new_overload);
820 pymeth->AdoptMethod(cb);
829 if (!PyArg_ParseTuple(args,
const_cast<char*
>(
"i|i:__cpp_reflex__"), &request, &
format))
832 return pymeth->fMethodInfo->fMethods[0]->Reflex(request,
format);
836static PyMethodDef mp_methods[] = {
837 {(
char*)
"__overload__", (PyCFunction)mp_overload, METH_VARARGS,
838 (
char*)
"select overload for dispatch" },
839 {(
char*)
"__add_overload__", (PyCFunction)mp_add_overload, METH_O,
840 (
char*)
"add a new overload" },
841 {(
char*)
"__cpp_reflex__", (PyCFunction)mp_reflex, METH_VARARGS,
842 (
char*)
"C++ overload reflection information" },
843 {(
char*)
nullptr,
nullptr, 0,
nullptr }
852 (
char*)
"cppyy.CPPOverload",
855 (destructor)mp_dealloc,
865 (ternaryfunc)mp_call,
870 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
871 (
char*)
"cppyy method proxy (internal)",
872 (traverseproc)mp_traverse,
874 (richcmpfunc)mp_richcompare,
883 (descrgetfunc)mp_descrget,
896#if PY_VERSION_HEX >= 0x02030000
899#if PY_VERSION_HEX >= 0x02060000
902#if PY_VERSION_HEX >= 0x03040000
919 if (
name ==
"__init__")
924 name.find(
"Clone") != std::string::npos)
932 fMethodInfo->fMethods.push_back(pc);
941 fMethodInfo->fMethods.insert(fMethodInfo->fMethods.end(),
951 bool accept_any = signature ==
":any:";
954 std::string sig1{
"("};
956 sig1.append(signature); sig1.append(
")");
957 sig1.erase(std::remove(sig1.begin(), sig1.end(),
' '), std::end(sig1));
961 for (
auto& meth : methods) {
963 bool found = accept_any;
965 PyObject* pysig2 = meth->GetSignature(
false);
967 sig2.erase(std::remove(sig2.begin(), sig2.end(),
' '), std::end(sig2));
969 if (sig1 == sig2) found =
true;
972 pysig2 = meth->GetSignature(
true);
974 sig3.erase(std::remove(sig3.begin(), sig3.end(),
' '), std::end(sig3));
976 if (sig1 == sig3) found =
true;
980 if (found && 0 <= want_const) {
981 bool isconst = meth->IsConst();
982 if (!((want_const && isconst) || (!want_const && !isconst)))
988 newmeth = mp_new(
nullptr,
nullptr,
nullptr);
990 newmeth->
Set(fMethodInfo->fName,
vec);
994 newmeth->
fSelf = fSelf;
1006 PyErr_Format(PyExc_LookupError,
"signature \"%s\" not found", signature.c_str());
1020 size_t best_method = 0, method_index = 0;
1022 for (
auto& meth : methods) {
1023 if (0 <= want_const) {
1024 bool isconst = meth->IsConst();
1025 if (!((want_const && isconst) || (!want_const && !isconst)))
1029 int score = meth->GetArgMatchScore(args_tuple);
1031 if (score < min_score) {
1034 best_method = method_index;
1041 std::string sigargs(
"(");
1043 for (
int i = 0; i <
n; i++) {
1044 PyObject *pItem = PyTuple_GetItem(args_tuple, i);
1046 PyErr_Format(PyExc_LookupError,
"argument types should be in string format");
1050 sigargs += arg_type +
", ";
1054 PyErr_Format(PyExc_LookupError,
"signature with arguments \"%s\" not found", sigargs.c_str());
1058 CPPOverload* newmeth = mp_new(
nullptr,
nullptr,
nullptr);
1060 vec.push_back(methods[best_method]->Clone());
1061 newmeth->
Set(fMethodInfo->fName,
vec);
1065 newmeth->
fSelf = fSelf;
1076 for (Methods_t::iterator it = fMethods.begin(); it != fMethods.end(); ++it) {
#define CPPOverload_MAXFREELIST
#define CPPYY_BOOLEAN_PROPERTY(name, flag, label)
#define CPyCppyy_PyText_Append
#define CPyCppyy_PyText_AsString
#define CPyCppyy_PyText_AppendAndDel
#define CPyCppyy_PyText_FromFormat
#define CPyCppyy_PyText_FromString
#define CPyCppyy_PyText_Check
#define PyVarObject_HEAD_INIT(type, size)
std::ios_base::fmtflags fFlags
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t format
void MergeOverload(CPPOverload *meth)
void AdoptMethod(PyCallable *pc)
MethodInfo_t * fMethodInfo
PyObject * FindOverload(const std::string &signature, int want_const=-1)
PyObject_HEAD CPPInstance * fSelf
std::vector< PyCallable * > Methods_t
void Set(const std::string &name, std::vector< PyCallable * > &methods)
size_t FetchError(std::vector< PyError_t > &)
void SetDetailedException(std::vector< PyError_t > &errors, PyObject *topmsg, PyObject *defexc)
Set of helper functions that are invoked from the pythonizors, on the Python side.
bool HaveImplicit(CallContext *ctxt)
PyObject * GetScopeProxy(Cppyy::TCppScope_t)
static PyMethodObject * free_list
bool NoImplicit(CallContext *ctxt)
bool IsCreator(uint64_t flags)
bool CPPInstance_Check(T *object)
PyTypeObject CPPOverload_Type
uint64_t HashSignature(PyObject *args)
bool IsConstructor(uint64_t flags)
bool IsSorted(uint64_t flags)
RPY_EXPORTED size_t SizeOf(TCppType_t klass)
RPY_EXPORTED TCppFuncAddr_t GetFunctionAddress(TCppMethod_t method, bool check_enabled=true)
void(off) SmallVectorTemplateBase< T
CPPOverload::DispatchMap_t fDispatchMap
CPPOverload::Methods_t fMethods
static ECallFlags sMemoryPolicy
static void Clear(PyError_t &e)