@@ -34,7 +34,26 @@ static inline void InjectMethod(Cppyy::TCppMethod_t method, const std::string& m
3434 if (Cppyy::IsConstMethod (method)) code << " const " ;
3535 code << " {\n " ;
3636
37- // start function body
37+ // on destruction, the Python object may go first, in which case provide a diagnostic
38+ // warning (raising a PyException may not be possible as this could happen during
39+ // program shutdown); note that this means that the actual result will be the default
40+ // and the caller may need to act on that, but that's still an improvement over a
41+ // possible crash
42+ code << " PyObject* iself = (PyObject*)_internal_self;\n "
43+ " if (!iself || iself == Py_None) {\n "
44+ " PyErr_Warn(PyExc_RuntimeWarning, (char*)\" Call attempted on deleted python-side proxy\" );\n "
45+ " return" ;
46+ if (retType != " void" ) {
47+ if (retType.back () != ' *' )
48+ code << " " << CPyCppyy::TypeManip::remove_const (retType) << " {}" ;
49+ else
50+ code << " nullptr" ;
51+ }
52+ code << " ;\n "
53+ " }\n "
54+ " Py_INCREF(iself);\n " ;
55+
56+ // start actual function body
3857 Utility::ConstructCallbackPreamble (retType, argtypes, code);
3958
4059// perform actual method call
@@ -43,13 +62,13 @@ static inline void InjectMethod(Cppyy::TCppMethod_t method, const std::string& m
4362#else
4463 code << " PyObject* mtPyName = PyUnicode_FromString(\" " << mtCppName << " \" );\n "
4564#endif
46- " PyObject* pyresult = PyObject_CallMethodObjArgs((PyObject*)_internal_self , mtPyName" ;
65+ " PyObject* pyresult = PyObject_CallMethodObjArgs(iself , mtPyName" ;
4766 for (Cppyy::TCppIndex_t i = 0 ; i < nArgs; ++i)
4867 code << " , pyargs[" << i << " ]" ;
49- code << " , NULL);\n Py_DECREF(mtPyName);\n " ;
68+ code << " , NULL);\n Py_DECREF(mtPyName);\n Py_DECREF(iself); \n " ;
5069
5170// close
52- Utility::ConstructCallbackReturn (retType, nArgs, code);
71+ Utility::ConstructCallbackReturn (retType, ( int ) nArgs, code);
5372}
5473
5574// ----------------------------------------------------------------------------
@@ -127,11 +146,11 @@ namespace {
127146using namespace Cppyy ;
128147
129148static inline
130- std::vector<TCppMethod_t > FindBaseMethod (TCppScope_t tbase, const std::string mtCppName)
149+ std::vector<TCppIndex_t > FindBaseMethod (TCppScope_t tbase, const std::string mtCppName)
131150{
132151// Recursively walk the inheritance tree to find the overloads of the named method
133- std::vector<TCppMethod_t > result;
134- result = GetMethodsFromName (tbase, mtCppName);
152+ std::vector<TCppIndex_t > result;
153+ result = GetMethodIndicesFromName (tbase, mtCppName);
135154 if (result.empty ()) {
136155 for (TCppIndex_t ibase = 0 ; ibase < GetNumBases (tbase); ++ibase) {
137156 TCppScope_t b = GetScope (GetBaseName (tbase, ibase));
@@ -215,17 +234,22 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct,
215234
216235// add a virtual destructor for good measure, which is allowed to be "overridden" by
217236// the conventional __destruct__ method (note that __del__ is always called, too, if
218- // provided, but only when the Python object goes away)
237+ // provided, but only when the Python object goes away; furthermore, if the Python
238+ // object goes before the C++ one, only __del__ is called)
219239 if (PyMapping_HasKeyString (dct, (char *)" __destruct__" )) {
220240 code << " virtual ~" << derivedName << " () {\n "
241+ " PyObject* iself = (PyObject*)_internal_self;\n "
242+ " if (!iself || iself == Py_None)\n "
243+ " return;\n " // safe, as destructor always returns void
244+ " Py_INCREF(iself);\n "
221245 " PyObject* mtPyName = PyUnicode_FromString(\" __destruct__\" );\n "
222- " PyObject* pyresult = PyObject_CallMethodObjArgs((PyObject*)_internal_self , mtPyName, NULL);\n "
223- " Py_DECREF(mtPyName);\n " ;
246+ " PyObject* pyresult = PyObject_CallMethodObjArgs(iself , mtPyName, NULL);\n "
247+ " Py_DECREF(mtPyName);\n Py_DECREF(iself); \n " ;
224248
225249 // this being a destructor, print on exception rather than propagate using the
226250 // magic C++ exception ...
227- code << " if (!pyresult) PyErr_Print();\n "
228- " else { Py_DECREF(pyresult); }\n "
251+ code << " if (!pyresult) PyErr_Print();\n "
252+ " else { Py_DECREF(pyresult); }\n "
229253 " }\n " ;
230254 } else
231255 code << " virtual ~" << derivedName << " () {}\n " ;
@@ -342,10 +366,10 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct,
342366 // TODO: should probably invert this looping; but that makes handling overloads clunky
343367 PyObject* key = PyList_GET_ITEM (keys, i);
344368 std::string mtCppName = CPyCppyy_PyText_AsString (key);
345- const auto & methods = FindBaseMethod (tbase, mtCppName);
346- for (auto method : methods )
347- InjectMethod (method , mtCppName, code);
348- if (!methods .empty ()) {
369+ const auto & v = FindBaseMethod (tbase, mtCppName);
370+ for (auto idx : v )
371+ InjectMethod (Cppyy::GetMethod (tbase, idx) , mtCppName, code);
372+ if (!v .empty ()) {
349373 if (PyDict_DelItem (clbs, key) != 0 ) PyErr_Clear ();
350374 }
351375 }
0 commit comments