Closed
Description
EDIT(eric): Adding a tracking list.
- Enumerate possible solutions and drawbacks, e.g.
- Should investigate usage of
py::wraper<>
trampoline shim with GC garbage collection (to prevent inheritance slicing) #2757 Usingpy::wrapper<>
and GC ressurrection - Others...
- Should investigate usage of
- Identify best path forward, implement.
Issue description
Hello,
Consider deriving a C++ class in Python and overloading virtual functions, including a 'Clone' function.
When passing a derived Python object to C++ without holding a reference (like calling Clone()), the information about the Python type is lost. In this case the wrong base-class function is called. More complex cases may result in a SEGFAULT.
Is there any way to work around/fix this without causing memory leaks?
Thanks!
Reproducible example code
test.cpp
// test.cpp
#include <pybind11/pybind11.h>
#include <Python.h>
#include <iostream>
#include <memory>
using std::cout;
using std::endl;
using std::unique_ptr;
using std::shared_ptr;
using std::make_shared;
namespace py = pybind11;
class Base {
public:
virtual void Print() {
cout << "Base!" << endl;
}
virtual shared_ptr<Base> Clone() {
return make_shared<Base>();
}
};
class PyBase : public Base {
public:
using Base::Base; // Inherit constructors
void Print() override { PYBIND11_OVERLOAD(void, Base, Print, ); }
shared_ptr<Base> Clone() override { PYBIND11_OVERLOAD(shared_ptr<Base>, Base, Clone, ); }
};
PYBIND11_MODULE(libtest, m) {
py::class_<Base, py::wrapper<PyBase>, shared_ptr<Base>>(m, "Base")
.def(py::init<>())
;
m.def("Test", [] ( shared_ptr<Base> b ) {
shared_ptr<Base> d = b->Clone();
// Python object of d is already dead!
d->Print(); // prints "Base!"
});
}
test.py
from libtest import *
class Derived(Base):
def __init__(self):
super().__init__()
def Print(self):
print("Derived!")
def Clone(self):
return Derived()
Test(Derived())