3#include "structmember.h"
4#if PY_VERSION_HEX >= 0x02050000
11#define CO_NOFREE 0x0040
33#ifndef CPPOverload_MAXFREELIST
34#define CPPOverload_MAXFREELIST 32
39class TPythonCallback :
public PyCallable {
45 if (!PyCallable_Check(callable)) {
46 PyErr_SetString(PyExc_TypeError,
"parameter must be callable");
53 virtual ~TPythonCallback() {
58 virtual PyObject* GetSignature(
bool =
true) {
61 virtual PyObject* GetPrototype(
bool =
true) {
65 if (PyObject_HasAttrString(
fCallable,
"__doc__")) {
66 return PyObject_GetAttrString(
fCallable,
"__doc__");
68 return GetPrototype();
72 virtual int GetPriority() {
return 100; };
73 virtual bool IsGreedy() {
return false; };
75 virtual int GetMaxArgs() {
return 100; };
79 virtual PyObject* GetArgDefault(
int ) {
91 virtual PyCallable* Clone() {
return new TPythonCallback(*
this); }
99 newArgs = PyTuple_New(nargs+1);
101 PyTuple_SET_ITEM(newArgs, 0, (
PyObject*)self);
102 for (
Py_ssize_t iarg = 0; iarg < nargs; ++iarg) {
103 PyObject* pyarg = PyTuple_GET_ITEM(args, iarg);
105 PyTuple_SET_ITEM(newArgs, iarg+1, pyarg);
111 return PyObject_Call(
fCallable, newArgs, kwds);
116static inline bool IsPseudoFunc(CPPOverload* pymeth)
122static int PriorityCmp(PyCallable* left, PyCallable* right)
124 return left->GetPriority() > right->GetPriority();
128static inline void ResetCallState(CPPInstance*& selfnew, CPPInstance* selfold,
bool clear)
130 if (selfnew != selfold) {
140static inline PyObject* HandleReturn(
141 CPPOverload* pymeth, CPPInstance* oldSelf,
PyObject* result)
148 if (
IsCreator(pymeth->fMethodInfo->fFlags)) {
153 pymeth->fSelf->PythonOwns();
158 ((CPPInstance*)result)->PythonOwns();
168 CPPInstance* cppself = (CPPInstance*)pymeth->fSelf;
169 CPPInstance* cppres = (CPPInstance*)result;
176 ptrdiff_t offset = (ptrdiff_t)cppres->GetObject() - (ptrdiff_t)cppself->GetObject();
177 if (0 <= offset && offset < (ptrdiff_t)
Cppyy::SizeOf(cppself->ObjectIsA()))
198 ResetCallState(pymeth->fSelf, oldSelf,
false);
205static PyObject* mp_name(CPPOverload* pymeth,
void*)
211static PyObject* mp_module(CPPOverload* ,
void*)
218static PyObject* mp_doc(CPPOverload* pymeth,
void*)
224 CPPOverload::Methods_t::size_type nMethods = methods.size();
227 PyObject* doc = methods[0]->GetDocString();
235 for (CPPOverload::Methods_t::size_type i = 1; i < nMethods; ++i) {
245static PyObject* mp_meth_func(CPPOverload* pymeth,
void*)
251 *pymeth->fMethodInfo->fRefCount += 1;
252 newPyMeth->fMethodInfo = pymeth->fMethodInfo;
262static PyObject* mp_meth_self(CPPOverload* pymeth,
void*)
266 if (IsPseudoFunc(pymeth)) {
267 PyErr_Format(PyExc_AttributeError,
268 "function %s has no attribute \'im_self\'", pymeth->fMethodInfo->fName.c_str());
270 }
else if (pymeth->fSelf != 0) {
271 Py_INCREF((
PyObject*)pymeth->fSelf);
279static PyObject* mp_meth_class(CPPOverload* pymeth,
void*)
283 if (!IsPseudoFunc(pymeth) && pymeth->fMethodInfo->fMethods.size()) {
284 PyObject* pyclass = pymeth->fMethodInfo->fMethods[0]->GetScopeProxy();
286 PyErr_Format(PyExc_AttributeError,
287 "function %s has no attribute \'im_class\'", pymeth->fMethodInfo->fName.c_str());
295static PyObject* mp_func_closure(CPPOverload* ,
void*)
302static PyObject* mp_func_code(CPPOverload* pymeth,
void*)
305#if PY_VERSION_HEX < 0x03000000
310 PyObject* co_varnames = methods.size() == 1 ? methods[0]->GetCoVarNames() :
nullptr;
313 co_varnames = PyTuple_New(1 + 1 );
318 int co_argcount = (
int)PyTuple_Size(co_varnames);
321 PyObject* co_code = PyString_FromStringAndSize(
"d\x00\x00S", 4);
324 PyObject* co_consts = PyTuple_New(0);
325 PyObject* co_names = PyTuple_New(0);
328 PyObject* co_unused = PyTuple_New(0);
331 PyObject* co_filename = PyString_FromString(
"cppyy.py");
334 PyObject* co_name = PyString_FromString(pymeth->GetName().c_str());
339 PyObject* co_lnotab = PyString_FromString(
"\x00\x01\x0c\x01");
357 Py_DECREF(co_lnotab);
359 Py_DECREF(co_unused);
360 Py_DECREF(co_filename);
361 Py_DECREF(co_varnames);
363 Py_DECREF(co_consts);
375static PyObject* mp_func_defaults(CPPOverload* pymeth,
void*)
381 if (methods.size() != 1)
382 return PyTuple_New(0);
384 int maxarg = methods[0]->GetMaxArgs();
386 PyObject* defaults = PyTuple_New(maxarg);
389 for (
int iarg = 0; iarg < maxarg; ++iarg) {
390 PyObject* defvalue = methods[0]->GetArgDefault(iarg);
392 PyTuple_SET_ITEM(defaults, itup++, defvalue);
394 _PyTuple_Resize(&defaults, itup);
400static PyObject* mp_func_globals(CPPOverload* ,
void*)
404 PyObject* pyglobal = PyModule_GetDict(PyImport_AddModule((
char*)
"cppyy"));
405 Py_XINCREF(pyglobal);
414 pymeth->fMethodInfo->fFlags &= ~flag;
418 long istrue = PyLong_AsLong(value);
419 if (istrue == -1 && PyErr_Occurred()) {
420 PyErr_Format(PyExc_ValueError,
"a boolean 1 or 0 is required for %s",
name);
425 pymeth->fMethodInfo->fFlags |= flag;
427 pymeth->fMethodInfo->fFlags &= ~flag;
433static PyObject* mp_getcreates(CPPOverload* pymeth,
void*)
440static int mp_setcreates(CPPOverload* pymeth,
PyObject* value,
void*)
447static PyObject* mp_getmempolicy(CPPOverload* pymeth,
void*)
460static int mp_setmempolicy(CPPOverload* pymeth,
PyObject* value,
void*)
463 long mempolicy = PyLong_AsLong(value);
466 pymeth->fMethodInfo->fFlags &= ~CallContext::kUseStrict;
469 pymeth->fMethodInfo->fFlags &= ~CallContext::kUseHeuristics;
471 PyErr_SetString(PyExc_ValueError,
472 "expected kMemoryStrict or kMemoryHeuristics as value for __mempolicy__");
481#define CPPYY_BOOLEAN_PROPERTY(name, flag, label) \
482static PyObject* mp_get##name(CPPOverload* pymeth, void*) { \
483 if (pymeth->fMethodInfo->fFlags & flag) { \
489static int mp_set##name(CPPOverload* pymeth, PyObject* value, void*) { \
490 return set_flag(pymeth, value, flag, label); \
499static PyGetSetDef mp_getset[] = {
500 {(
char*)
"__name__", (getter)mp_name,
nullptr,
nullptr,
nullptr},
501 {(
char*)
"__module__", (getter)mp_module,
nullptr,
nullptr,
nullptr},
502 {(
char*)
"__doc__", (getter)mp_doc,
nullptr,
nullptr,
nullptr},
507 {(
char*)
"im_func", (getter)mp_meth_func,
nullptr,
nullptr,
nullptr},
508 {(
char*)
"im_self", (getter)mp_meth_self,
nullptr,
nullptr,
nullptr},
509 {(
char*)
"im_class", (getter)mp_meth_class,
nullptr,
nullptr,
nullptr},
511 {(
char*)
"func_closure", (getter)mp_func_closure,
nullptr,
nullptr,
nullptr},
512 {(
char*)
"func_code", (getter)mp_func_code,
nullptr,
nullptr,
nullptr},
513 {(
char*)
"func_defaults", (getter)mp_func_defaults,
nullptr,
nullptr,
nullptr},
514 {(
char*)
"func_globals", (getter)mp_func_globals,
nullptr,
nullptr,
nullptr},
515 {(
char*)
"func_doc", (getter)mp_doc,
nullptr,
nullptr,
nullptr},
516 {(
char*)
"func_name", (getter)mp_name,
nullptr,
nullptr,
nullptr},
518 {(
char*)
"__creates__", (getter)mp_getcreates, (setter)mp_setcreates,
519 (
char*)
"For ownership rules of result: if true, objects are python-owned",
nullptr},
520 {(
char*)
"__mempolicy__", (getter)mp_getmempolicy, (setter)mp_setmempolicy,
521 (
char*)
"For argument ownership rules: like global, either heuristic or strict",
nullptr},
522 {(
char*)
"__set_lifeline__", (getter)mp_getlifeline, (setter)mp_setlifeline,
523 (
char*)
"If true, set a lifeline from the return value onto self",
nullptr},
524 {(
char*)
"__release_gil__", (getter)mp_getthreaded, (setter)mp_setthreaded,
525 (
char*)
"If true, releases GIL on call into C++",
nullptr},
526 {(
char*)
"__useffi__", (getter)mp_getuseffi, (setter)mp_setuseffi,
527 (
char*)
"not implemented",
nullptr},
528 {(
char*)
"__sig2exc__", (getter)mp_getsig2exc, (setter)mp_setsig2exc,
529 (
char*)
"If true, turn signals into Python exceptions",
nullptr},
530 {(
char*)
nullptr,
nullptr,
nullptr,
nullptr,
nullptr}
538 CPPInstance* oldSelf = pymeth->fSelf;
541 auto& methods = pymeth->fMethodInfo->fMethods;
543 CPPOverload::Methods_t::size_type nMethods = methods.size();
546 const auto mflags = pymeth->fMethodInfo->fFlags;
554 if (kwds && PyDict_CheckExact(kwds) && PyDict_Size(kwds) != 0) {
557 if (!PyDict_Size(kwds)) kwds =
nullptr;
565 PyObject* result = methods[0]->Call(pymeth->fSelf, args, kwds, &ctxt);
566 return HandleReturn(pymeth, oldSelf, result);
573 auto& dispatchMap = pymeth->fMethodInfo->fDispatchMap;
574 PyCallable* memoized_pc =
nullptr;
575 for (
const auto& p : dispatchMap) {
576 if (p.first == sighash) {
577 memoized_pc = p.second;
582 PyObject* result = memoized_pc->Call(pymeth->fSelf, args, kwds, &ctxt);
583 result = HandleReturn(pymeth, oldSelf, result);
594 std::stable_sort(methods.begin(), methods.end(), PriorityCmp);
598 std::vector<Utility::PyError_t> errors;
599 std::vector<bool> implicit_possible(methods.size());
600 for (
int stage = 0; stage < 2; ++stage) {
601 bool bHaveImplicit =
false;
602 for (CPPOverload::Methods_t::size_type i = 0; i < nMethods; ++i) {
603 if (stage && !implicit_possible[i])
606 PyObject* result = methods[i]->Call(pymeth->fSelf, args, kwds, &ctxt);
610 dispatchMap.push_back(std::make_pair(sighash, methods[i]));
614 for (
auto& p : dispatchMap) {
615 if (p.first == sighash) {
616 p.second = methods[i];
625 return HandleReturn(pymeth, oldSelf, result);
635 if (!PyErr_Occurred()) {
637 PyObject* sig = methods[i]->GetPrototype();
638 PyErr_Format(PyExc_SystemError,
"%s =>\n %s",
645 bHaveImplicit =
true;
646 implicit_possible[i] =
true;
647 ctxt.fFlags &= ~CallContext::kHaveImplicit;
649 implicit_possible[i] =
false;
650 ResetCallState(pymeth->fSelf, oldSelf,
false);
662 "none of the %d overloaded methods succeeded. Full details:", (
int)nMethods);
670static PyObject* mp_str(CPPOverload* cppinst)
673 std::ostringstream
s;
674 s <<
"<C++ overload \"" << cppinst->fMethodInfo->fName <<
"\" at " << (
void*)cppinst <<
">";
679static CPPOverload* mp_descrget(CPPOverload* pymeth, CPPInstance* pyobj,
PyObject*)
689 if (newPyMeth != NULL) {
690 free_list = (CPPOverload*)(newPyMeth->fSelf);
701 *pymeth->fMethodInfo->fRefCount += 1;
702 newPyMeth->fMethodInfo = pymeth->fMethodInfo;
706 newPyMeth->fSelf = pyobj;
708 PyObject_GC_Track(newPyMeth);
718 pymeth->fSelf =
nullptr;
719 pymeth->fMethodInfo =
new CPPOverload::MethodInfo_t;
721 PyObject_GC_Track(pymeth);
726static void mp_dealloc(CPPOverload* pymeth)
729 PyObject_GC_UnTrack(pymeth);
731 Py_CLEAR(pymeth->fSelf);
733 if (--(*pymeth->fMethodInfo->fRefCount) <= 0) {
734 delete pymeth->fMethodInfo;
743 PyObject_GC_Del(pymeth);
752 return _Py_HashPointer(pymeth->fMethodInfo);
756static int mp_traverse(CPPOverload* pymeth, visitproc visit,
void* args)
760 return visit((
PyObject*)pymeth->fSelf, args);
766static int mp_clear(CPPOverload* pymeth)
769 Py_CLEAR(pymeth->fSelf);
775static PyObject* mp_richcompare(CPPOverload* self, CPPOverload* other,
int op)
783 if ((
Py_TYPE(self) ==
Py_TYPE(other) && self->fMethodInfo == other->fMethodInfo) && \
784 ((IsPseudoFunc(self) && IsPseudoFunc(other)) || self->fSelf == other->fSelf)) {
796 PyErr_Format(PyExc_TypeError,
"__overload__() argument 1 must be string, not %.50s",
797 sigarg == Py_None ?
"None" :
Py_TYPE(sigarg)->tp_name);
802 sig1.erase(std::remove(sig1.begin(), sig1.end(),
' '), std::end(sig1));
805 for (
auto& meth : methods) {
809 PyObject* pysig2 = meth->GetSignature(
false);
811 sig2.erase(std::remove(sig2.begin(), sig2.end(),
' '), std::end(sig2));
813 if (sig1 == sig2) found =
true;
816 pysig2 = meth->GetSignature(
true);
818 sig3.erase(std::remove(sig3.begin(), sig3.end(),
' '), std::end(sig3));
820 if (sig1 == sig3) found =
true;
824 CPPOverload* newmeth = mp_new(
nullptr,
nullptr,
nullptr);
826 newmeth->Set(pymeth->fMethodInfo->fName, vec);
829 Py_INCREF(pymeth->fSelf);
830 newmeth->fSelf = pymeth->fSelf;
832 newmeth->fMethodInfo->fFlags = pymeth->fMethodInfo->fFlags;
838 PyErr_Format(PyExc_LookupError,
844static PyObject* mp_add_overload(CPPOverload* pymeth,
PyObject* new_overload)
846 TPythonCallback* cb =
new TPythonCallback(new_overload);
847 pymeth->AdoptMethod(cb);
851static PyMethodDef mp_methods[] = {
852 {(
char*)
"__overload__", (PyCFunction)mp_overload, METH_O,
853 (
char*)
"select overload for dispatch" },
854 {(
char*)
"__add_overload__", (PyCFunction)mp_add_overload, METH_O,
855 (
char*)
"add a new overload" },
856 {(
char*)
nullptr,
nullptr, 0,
nullptr }
865 (
char*)
"cppyy.CPPOverload",
868 (destructor)mp_dealloc,
878 (ternaryfunc)mp_call,
883 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
884 (
char*)
"cppyy method proxy (internal)",
885 (traverseproc)mp_traverse,
887 (richcmpfunc)mp_richcompare,
896 (descrgetfunc)mp_descrget,
909#if PY_VERSION_HEX >= 0x02030000
912#if PY_VERSION_HEX >= 0x02060000
915#if PY_VERSION_HEX >= 0x03040000
932 if (
name ==
"__init__")
937 name.find(
"Clone") != std::string::npos)
945 fMethodInfo->fMethods.push_back(
pc);
954 fMethodInfo->fMethods.insert(fMethodInfo->fMethods.end(),
965 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)
typedef void((*Func_t)())
void MergeOverload(CPPOverload *meth)
void AdoptMethod(PyCallable *pc)
MethodInfo_t * fMethodInfo
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)
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 TCppFuncAddr_t GetFunctionAddress(TCppMethod_t method, bool check_enabled=true)
RPY_EXPORTED size_t SizeOf(TCppType_t klass)
static constexpr double s
static constexpr double pc
CPPOverload::DispatchMap_t fDispatchMap
CPPOverload::Methods_t fMethods
static ECallFlags sMemoryPolicy
static void Clear(PyError_t &e)