4#include "structmember.h"
5#if PY_VERSION_HEX >= 0x02050000
6#if PY_VERSION_HEX < 0x030b0000
14#define CO_NOFREE 0x0040
36#ifndef CPPOverload_MAXFREELIST
37#define CPPOverload_MAXFREELIST 32
43class TPythonCallback :
public PyCallable {
47 TPythonCallback(
PyObject* callable) : fCallable(
nullptr)
49 if (!PyCallable_Check(callable)) {
50 PyErr_SetString(PyExc_TypeError,
"parameter must be callable");
57 ~TPythonCallback()
override {
62 PyObject* GetSignature(
bool =
true)
override {
65 PyObject* GetSignatureNames()
override {
66 return PyTuple_New(0);
68 PyObject* GetSignatureTypes()
override {
69 return PyTuple_New(0);
71 PyObject* GetPrototype(
bool =
true)
override {
75 if (PyObject_HasAttrString(fCallable,
"__doc__")) {
76 return PyObject_GetAttrString(fCallable,
"__doc__");
78 return GetPrototype();
82 int GetPriority()
override {
return 100; };
83 bool IsGreedy()
override {
return false; };
85 int GetMaxArgs()
override {
return 100; };
89 PyObject* GetArgDefault(
int ,
bool =
true)
override {
101 PyCallable* Clone()
override {
return new TPythonCallback(*
this); }
106#if PY_VERSION_HEX >= 0x03080000
108 if (nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET) {
110 nargsf &= ~PY_VECTORCALL_ARGUMENTS_OFFSET;
113 Py_ssize_t nkwargs = kwds ? PyTuple_GET_SIZE(kwds) : 0;
114 Py_ssize_t totalargs = PyVectorcall_NARGS(nargsf)+nkwargs;
121 memcpy((
void*)&newArgs[1], args, totalargs *
sizeof(
PyObject*));
129 if (nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET)
131 else PyMem_Free((
void*)args);
137 newArgs = PyTuple_New(nargs+1);
139 PyTuple_SET_ITEM(newArgs, 0, (
PyObject*)self);
140 for (
Py_ssize_t iarg = 0; iarg < nargs; ++iarg) {
141 PyObject* pyarg = PyTuple_GET_ITEM(args, iarg);
143 PyTuple_SET_ITEM(newArgs, iarg+1, pyarg);
149 PyObject* result = PyObject_Call(fCallable, newArgs, kwds);
157static inline bool IsPseudoFunc(
CPPOverload* pymeth)
163static int PriorityCmp(
const std::pair<int, PyCallable*>& left,
const std::pair<int, PyCallable*>& right)
165 return left.first > right.first;
172 if (descr_self != im_self) {
174 im_self = descr_self;
179static inline PyObject* HandleReturn(
187 if (
IsCreator(pymeth->fMethodInfo->fFlags)) {
192 im_self->PythonOwns();
197 cppres->PythonOwns();
215 ptrdiff_t offset = (ptrdiff_t)cppres->GetObject() - (ptrdiff_t)im_self->GetObject();
216 if (0 <= offset && offset < (ptrdiff_t)
Cppyy::SizeOf(im_self->ObjectIsA()))
235 ResetCallState(pymeth->fSelf, im_self);
257 if (pymeth->fMethodInfo->fDoc) {
258 Py_INCREF(pymeth->fMethodInfo->fDoc);
259 return pymeth->fMethodInfo->fDoc;
266 CPPOverload::Methods_t::size_type nMethods = methods.size();
269 PyObject* doc = methods[0]->GetDocString();
277 for (CPPOverload::Methods_t::size_type i = 1; i < nMethods; ++i) {
281 Py_DECREF(separator);
288 Py_XDECREF(pymeth->fMethodInfo->fDoc);
290 pymeth->fMethodInfo->fDoc = val;
309 PyObject *overloads_names_dict = PyDict_New();
312 PyDict_SetItem(overloads_names_dict, method->GetPrototype(), method->GetSignatureNames());
315 return overloads_names_dict;
333 PyObject *overloads_types_dict = PyDict_New();
336 PyDict_SetItem(overloads_types_dict, method->GetPrototype(), method->GetSignatureTypes());
339 return overloads_types_dict;
349 *pymeth->fMethodInfo->fRefCount += 1;
350 newPyMeth->fMethodInfo = pymeth->fMethodInfo;
364 if (IsPseudoFunc(pymeth)) {
365 PyErr_Format(PyExc_AttributeError,
366 "function %s has no attribute \'im_self\'", pymeth->fMethodInfo->fName.c_str());
368 }
else if (pymeth->fSelf != 0) {
369 Py_INCREF((
PyObject*)pymeth->fSelf);
381 if (!IsPseudoFunc(pymeth) && pymeth->fMethodInfo->fMethods.size()) {
382 PyObject* pyclass = pymeth->fMethodInfo->fMethods[0]->GetScopeProxy();
384 PyErr_Format(PyExc_AttributeError,
385 "function %s has no attribute \'im_class\'", pymeth->fMethodInfo->fName.c_str());
400#if PY_VERSION_HEX < 0x03000000
401#define CPyCppyy_Py3_UNUSED(name) name
403#define CPyCppyy_Py3_UNUSED(name)
410#if PY_VERSION_HEX < 0x03000000
415 PyObject* co_varnames = methods.size() == 1 ? methods[0]->GetCoVarNames() :
nullptr;
418 co_varnames = PyTuple_New(1 + 1 );
423 int co_argcount = (
int)PyTuple_Size(co_varnames);
426 PyObject* co_code = PyString_FromStringAndSize(
"d\x00\x00S", 4);
429 PyObject* co_consts = PyTuple_New(0);
430 PyObject* co_names = PyTuple_New(0);
433 PyObject* co_unused = PyTuple_New(0);
436 PyObject* co_filename = PyString_FromString(
"cppyy.py");
439 PyObject* co_name = PyString_FromString(pymeth->GetName().c_str());
444 PyObject* co_lnotab = PyString_FromString(
"\x00\x01\x0c\x01");
462 Py_DECREF(co_lnotab);
464 Py_DECREF(co_unused);
465 Py_DECREF(co_filename);
466 Py_DECREF(co_varnames);
468 Py_DECREF(co_consts);
485 if (methods.size() != 1)
486 return PyTuple_New(0);
488 int maxarg = methods[0]->GetMaxArgs();
490 PyObject* defaults = PyTuple_New(maxarg);
493 for (
int iarg = 0; iarg < maxarg; ++iarg) {
494 PyObject* defvalue = methods[0]->GetArgDefault(iarg);
496 PyTuple_SET_ITEM(defaults, itup++, defvalue);
500 _PyTuple_Resize(&defaults, itup);
510 PyObject* pyglobal = PyModule_GetDict(PyImport_AddModule((
char*)
"cppyy"));
511 Py_XINCREF(pyglobal);
520 pymeth->fMethodInfo->fFlags &= ~flag;
524 long istrue = PyLong_AsLong(value);
525 if (istrue == -1 && PyErr_Occurred()) {
526 PyErr_Format(PyExc_ValueError,
"a boolean 1 or 0 is required for %s",
name);
531 pymeth->fMethodInfo->fFlags |= flag;
533 pymeth->fMethodInfo->fFlags &= ~flag;
542 return PyInt_FromLong((
long)
IsCreator(pymeth->fMethodInfo->fFlags));
552constexpr const char *mempolicy_error_message =
553 "The __mempolicy__ attribute can't be used, because in the past it was reserved to manage the local memory policy. "
554 "If you want to do that now, please implement a pythonization for your class that uses SetOwnership() to manage the "
555 "ownership of arguments according to your needs.";
560 PyErr_SetString(PyExc_RuntimeError, mempolicy_error_message);
567 PyErr_SetString(PyExc_RuntimeError, mempolicy_error_message);
572#define CPPYY_BOOLEAN_PROPERTY(name, flag, label) \
573static PyObject* mp_get##name(CPPOverload* pymeth, void*) { \
574 if (pymeth->fMethodInfo->fFlags & flag) { \
580static int mp_set##name(CPPOverload* pymeth, PyObject* value, void*) { \
581 return set_flag(pymeth, value, flag, label); \
589static
PyObject* mp_getcppname(CPPOverload* pymeth,
void*)
594 auto& methods = pymeth->fMethodInfo->fMethods;
598 if (methods.size() == 1)
599 return methods[0]->GetTypeName();
606static PyGetSetDef mp_getset[] = {
607 {(
char*)
"__name__", (getter)mp_name,
nullptr,
nullptr,
nullptr},
608 {(
char*)
"__module__", (getter)mp_module,
nullptr,
nullptr,
nullptr},
609 {(
char*)
"__doc__", (getter)mp_doc, (setter)mp_doc_set,
nullptr,
nullptr},
614 {(
char*)
"im_func", (getter)mp_meth_func,
nullptr,
nullptr,
nullptr},
615 {(
char*)
"im_self", (getter)mp_meth_self,
nullptr,
nullptr,
nullptr},
616 {(
char*)
"im_class", (getter)mp_meth_class,
nullptr,
nullptr,
nullptr},
618 {(
char*)
"func_closure", (getter)mp_func_closure,
nullptr,
nullptr,
nullptr},
619 {(
char*)
"func_code", (getter)mp_func_code,
nullptr,
nullptr,
nullptr},
620 {(
char*)
"func_defaults", (getter)mp_func_defaults,
nullptr,
nullptr,
nullptr},
621 {(
char*)
"func_globals", (getter)mp_func_globals,
nullptr,
nullptr,
nullptr},
622 {(
char*)
"func_doc", (getter)mp_doc, (setter)mp_doc_set,
nullptr,
nullptr},
623 {(
char*)
"func_name", (getter)mp_name,
nullptr,
nullptr,
nullptr},
624 {(
char*)
"func_overloads_types", (getter)mp_func_overloads_types,
nullptr,
nullptr,
nullptr},
625 {(
char*)
"func_overloads_names", (getter)mp_func_overloads_names,
nullptr,
nullptr,
nullptr},
629 {(
char*)
"__creates__", (getter)mp_getcreates, (setter)mp_setcreates,
630 (
char*)
"For ownership rules of result: if true, objects are python-owned",
nullptr},
631 {(
char*)
"__mempolicy__", (getter)mp_getmempolicy, (setter)mp_setmempolicy,
632 (
char*)
"Unused",
nullptr},
633 {(
char*)
"__set_lifeline__", (getter)mp_getlifeline, (setter)mp_setlifeline,
634 (
char*)
"If true, set a lifeline from the return value onto self",
nullptr},
635 {(
char*)
"__release_gil__", (getter)mp_getthreaded, (setter)mp_setthreaded,
636 (
char*)
"If true, releases GIL on call into C++",
nullptr},
637 {(
char*)
"__useffi__", (getter)mp_getuseffi, (setter)mp_setuseffi,
638 (
char*)
"not implemented",
nullptr},
639 {(
char*)
"__sig2exc__", (getter)mp_getsig2exc, (setter)mp_setsig2exc,
640 (
char*)
"If true, turn signals into Python exceptions",
nullptr},
643 {(
char*)
"__cpp_name__", (getter)mp_getcppname,
nullptr,
nullptr,
nullptr},
645 {(
char*)
nullptr,
nullptr,
nullptr,
nullptr,
nullptr}
649#if PY_VERSION_HEX >= 0x03080000
656#if PY_VERSION_HEX < 0x03080000
657 size_t nargsf = PyTuple_GET_SIZE(args);
668 auto& methods = pymeth->fMethodInfo->fMethods;
670 CPPOverload::Methods_t::size_type nMethods = methods.size();
673 const auto mflags = pymeth->fMethodInfo->
fFlags;
678 ctxt.fPyContext = (
PyObject*)im_self;
686 PyObject* result = methods[0]->Call(im_self, args, nargsf, kwds, &ctxt);
687 return HandleReturn(pymeth, im_self, result);
694 auto& dispatchMap = pymeth->fMethodInfo->fDispatchMap;
696 for (
const auto& p : dispatchMap) {
697 if (p.first == sighash) {
698 memoized_pc = p.second;
706 PyObject* result = memoized_pc->Call(im_self, args, nargsf, kwds, &ctxt);
708 return HandleReturn(pymeth, im_self, result);
713 ResetCallState(pymeth->fSelf, im_self);
721 std::vector<std::pair<int, PyCallable*>> pm; pm.reserve(methods.size());
722 for (
auto ptr : methods)
723 pm.emplace_back(ptr->GetPriority(), ptr);
724 std::stable_sort(pm.begin(), pm.end(), PriorityCmp);
725 for (CPPOverload::Methods_t::size_type i = 0; i < methods.size(); ++i)
726 methods[i] = pm[i].second;
730 std::vector<Utility::PyError_t> errors;
731 std::vector<bool> implicit_possible(methods.size());
732 for (
int stage = 0; stage < 2; ++stage) {
733 bool bHaveImplicit =
false;
734 for (CPPOverload::Methods_t::size_type i = 0; i < nMethods; ++i) {
735 if (stage && !implicit_possible[i])
738 PyObject* result = methods[i]->Call(im_self, args, nargsf, kwds, &ctxt);
742 dispatchMap.push_back(std::make_pair(sighash, methods[i]));
746 for (
auto& p : dispatchMap) {
747 if (p.first == sighash) {
748 p.second = methods[i];
754 return HandleReturn(pymeth, im_self, result);
760 ResetCallState(pymeth->fSelf, im_self);
765 if (!PyErr_Occurred()) {
767 PyObject* sig = methods[i]->GetPrototype();
768 PyErr_Format(PyExc_SystemError,
"%s =>\n %s",
779 bHaveImplicit =
true;
780 implicit_possible[i] =
true;
783 implicit_possible[i] =
false;
784 ResetCallState(pymeth->fSelf, im_self);
796 "none of the %d overloaded methods succeeded. Full details:", (
int)nMethods);
807 std::ostringstream s;
808 s <<
"<C++ overload \"" << cppinst->fMethodInfo->fName <<
"\" at " << (
void*)cppinst <<
">";
826#if PY_VERSION_HEX < 0x03080000
827 if (!pyobj || (
PyObject*)pyobj == Py_None ) {
828 Py_XDECREF(pymeth->fSelf); pymeth->fSelf =
nullptr;
836 bool gc_track =
false;
838 if (newPyMeth != NULL) {
850 *pymeth->fMethodInfo->fRefCount += 1;
851 newPyMeth->fMethodInfo = pymeth->fMethodInfo;
853#if PY_VERSION_HEX >= 0x03080000
854 newPyMeth->fVectorCall = pymeth->fVectorCall;
856 if (pyobj && (
PyObject*)pyobj != Py_None) {
858 newPyMeth->fSelf = pyobj;
861 newPyMeth->fSelf =
nullptr;
872 newPyMeth->fSelf = pyobj;
881 PyObject_GC_Track(newPyMeth);
892 pymeth->fSelf =
nullptr;
896 PyObject_GC_Track(pymeth);
904 PyObject_GC_UnTrack(pymeth);
906 Py_CLEAR(pymeth->fSelf);
908 if (--(*pymeth->fMethodInfo->fRefCount) <= 0) {
909 delete pymeth->fMethodInfo;
913 pymeth->fSelf = (CPyCppyy::CPPInstance*)
free_list;
917 PyObject_GC_Del(pymeth);
926#if PY_VERSION_HEX >= 0x030d0000
927 return Py_HashPointer(pymeth->fMethodInfo);
929 return _Py_HashPointer(pymeth->fMethodInfo);
934static int mp_traverse(
CPPOverload* pymeth, visitproc visit,
void* args)
938 return visit((
PyObject*)pymeth->fSelf, args);
947 Py_CLEAR(pymeth->fSelf);
961 if ((
Py_TYPE(self) ==
Py_TYPE(other) && self->fMethodInfo == other->fMethodInfo) && \
962 ((IsPseudoFunc(self) && IsPseudoFunc(other)) || self->fSelf == other->fSelf)) {
973 const char* sigarg =
nullptr;
976 Py_ssize_t args_size = PyTuple_GET_SIZE(args);
978 PyArg_ParseTuple(args,
const_cast<char*
>(
"s|i:__overload__"), &sigarg, &want_const)) {
979 want_const = args_size == 1 ? -1 : want_const;
980 return pymeth->FindOverload(sigarg ? sigarg :
"", want_const);
981 }
else if (args_size &&
982 PyArg_ParseTuple(args,
const_cast<char*
>(
"O|i:__overload__"), &sigarg_tuple, &want_const)) {
984 want_const = args_size == 1 ? -1 : want_const;
985 return pymeth->FindOverload(sigarg_tuple, want_const);
987 PyErr_Format(PyExc_TypeError,
"Unexpected arguments to __overload__");
994 TPythonCallback* cb =
new TPythonCallback(new_overload);
995 pymeth->AdoptMethod(cb);
1004 if (!PyArg_ParseTuple(args,
const_cast<char*
>(
"i|i:__cpp_reflex__"), &request, &format))
1007 return pymeth->fMethodInfo->fMethods[0]->Reflex(request, format);
1011static PyMethodDef mp_methods[] = {
1012 {(
char*)
"__overload__", (PyCFunction)mp_overload, METH_VARARGS,
1013 (
char*)
"select overload for dispatch" },
1014 {(
char*)
"__add_overload__", (PyCFunction)mp_add_overload, METH_O,
1015 (
char*)
"add a new overload" },
1016 {(
char*)
"__cpp_reflex__", (PyCFunction)mp_reflex, METH_VARARGS,
1017 (
char*)
"C++ overload reflection information" },
1018 {(
char*)
nullptr,
nullptr, 0,
nullptr }
1027 (
char*)
"cppyy.CPPOverload",
1030 (destructor)mp_dealloc,
1031#
if PY_VERSION_HEX >= 0x03080000
1044#
if PY_VERSION_HEX >= 0x03080000
1045 (ternaryfunc)PyVectorcall_Call,
1047 (ternaryfunc)mp_call,
1053 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC
1054#if PY_VERSION_HEX >= 0x03080000
1055 | Py_TPFLAGS_HAVE_VECTORCALL | Py_TPFLAGS_METHOD_DESCRIPTOR
1058 (
char*)
"cppyy method proxy (internal)",
1059 (traverseproc)mp_traverse,
1061 (richcmpfunc)mp_richcompare,
1070 (descrgetfunc)mp_descr_get,
1083#if PY_VERSION_HEX >= 0x02030000
1086#if PY_VERSION_HEX >= 0x02060000
1089#if PY_VERSION_HEX >= 0x03040000
1092#if PY_VERSION_HEX >= 0x03080000
1095#if PY_VERSION_HEX >= 0x030c0000
1098#if PY_VERSION_HEX >= 0x030d0000
1115 if (
name ==
"__init__")
1122 std::string_view name_maybe_template =
name;
1123 auto begin_template = name_maybe_template.find_first_of(
'<');
1124 if (begin_template <= name_maybe_template.size()) {
1125 name_maybe_template = name_maybe_template.substr(0, begin_template);
1127 if (name_maybe_template.find(
"Clone") != std::string_view::npos) {
1132#if PY_VERSION_HEX >= 0x03080000
1133 fVectorCall = (vectorcallfunc)mp_vectorcall;
1160 bool accept_any = signature ==
":any:";
1163 std::string sig1{
"("};
1165 sig1.append(signature); sig1.append(
")");
1166 sig1.erase(std::remove(sig1.begin(), sig1.end(),
' '), std::end(sig1));
1170 for (
auto& meth : methods) {
1171 bool found = accept_any;
1173 PyObject* pysig2 = meth->GetSignature(
false);
1175 sig2.erase(std::remove(sig2.begin(), sig2.end(),
' '), std::end(sig2));
1177 if (sig1 == sig2) found =
true;
1180 pysig2 = meth->GetSignature(
true);
1182 sig3.erase(std::remove(sig3.begin(), sig3.end(),
' '), std::end(sig3));
1184 if (sig1 == sig3) found =
true;
1188 if (found && 0 <= want_const) {
1189 bool isconst = meth->IsConst();
1190 if (!((want_const && isconst) || (!want_const && !isconst)))
1196 newmeth = mp_new(
nullptr,
nullptr,
nullptr);
1214 PyErr_Format(PyExc_LookupError,
"signature \"%s\" not found", signature.c_str());
1228 size_t best_method = 0, method_index = 0;
1230 for (
auto& meth : methods) {
1231 if (0 <= want_const) {
1232 bool isconst = meth->IsConst();
1233 if (!((want_const && isconst) || (!want_const && !isconst)))
1237 int score = meth->GetArgMatchScore(args_tuple);
1239 if (score < min_score) {
1242 best_method = method_index;
1249 std::string sigargs(
"(");
1251 for (
int i = 0; i <
n; i++) {
1252 PyObject *pItem = PyTuple_GetItem(args_tuple, i);
1254 PyErr_Format(PyExc_LookupError,
"argument types should be in string format");
1258 sigargs += arg_type +
", ";
1262 PyErr_Format(PyExc_LookupError,
"signature with arguments \"%s\" not found", sigargs.c_str());
1266 CPPOverload* newmeth = mp_new(
nullptr,
nullptr,
nullptr);
1268 vec.push_back(methods[best_method]->Clone());
1284 for (Methods_t::iterator it =
fMethods.begin(); it !=
fMethods.end(); ++it) {
#define CPyCppyy_Py3_UNUSED(name)
#define CPPOverload_MAXFREELIST
#define CPPYY_BOOLEAN_PROPERTY(name, flag, label)
#define CPyCppyy_PyText_Append
#define CPyCppyy_PyText_AsString
PyObject * CPyCppyy_PyArgs_t
#define CPyCppyy_PyText_AppendAndDel
PyObject * CPyCppyy_PyObject_Call(PyObject *cb, PyObject *args, size_t, PyObject *kwds)
#define CPyCppyy_PyText_FromFormat
#define CPyCppyy_PyText_FromString
#define CPyCppyy_PyText_Check
#define PyVarObject_HEAD_INIT(type, size)
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)
void SetDetailedException(std::vector< PyError_t > &&errors, PyObject *topmsg, PyObject *defexc)
size_t FetchError(std::vector< PyError_t > &, bool is_cpp=false)
bool HaveImplicit(CallContext *ctxt)
PyObject * GetScopeProxy(Cppyy::TCppScope_t)
static PyMethodObject * free_list
bool NoImplicit(CallContext *ctxt)
bool IsCreator(uint64_t flags)
uint64_t HashSignature(CPyCppyy_PyArgs_t args, size_t nargsf)
bool CPPInstance_Check(T *object)
PyTypeObject CPPOverload_Type
bool IsConstructor(uint64_t flags)
bool IsSorted(uint64_t flags)
RPY_EXPORTED size_t SizeOf(TCppType_t klass)
void(off) SmallVectorTemplateBase< T
CPPOverload::DispatchMap_t fDispatchMap
CPPOverload::Methods_t fMethods
static uint32_t & GlobalPolicyFlags()