pybind/python_example

Segfault on cleanup with multiple_inheritance and return_value_policy::reference

arjansomers opened this issue · 0 comments

I have a base (pure-abstract) interface that is extended by another (pure abstract) interface.

Then i have an implementation of the base interface and a implementation of the extended interface that derives the extended interface and the base implementation via virtual inheritance (diamond)

Now if i return a reference of type InterfaceExtened i can correctly use it.

If i however delete the object that is being referenced on the c++ side, then i get an error during program exit. I do not get such an error when doing the same using InterfaceBase.

I create file pybind11bug.cpp:

#include <pybind11/pybind11.h>
#include <memory>
#include <iostream>

class InterfaceBase {
public:
    virtual ~InterfaceBase() = default;
    virtual void foo() = 0;  
};

class InterfaceExtended: public virtual InterfaceBase {
public:
    virtual ~InterfaceExtended() = default;
    virtual void bar() = 0;  
};

class ImplementationBase: public virtual InterfaceBase {
public:
    virtual void foo() { std::cout << "foo" << std::endl;};  
};

class ImplementationExtended: public ImplementationBase, public InterfaceExtended {
public:
    virtual void bar() { std::cout << "bar" << std::endl; };  
};

class Helper {
public:
    InterfaceBase& createBaseInstance() {
        instanceBase.reset(new ImplementationBase());
        return *instanceBase;
    }
    
    void deletebaseInstance() {
        instanceBase.reset();
    }

    InterfaceExtended& createExtendedInstance() {
        instanceExt.reset(new ImplementationExtended());
        return *instanceExt;
    }

    void deleteBaseInstance() {
        instanceBase.reset();
    }

    void deleteExtendedInstance() {
        instanceExt.reset();
    }

private:
    std::unique_ptr<InterfaceBase> instanceBase;
    std::unique_ptr<InterfaceExtended> instanceExt;
};

namespace py = pybind11;
    
PYBIND11_MODULE(example, m) {
    py::class_<InterfaceBase>(m, "InterfaceBase")
        .def("foo", &InterfaceBase::foo);
    py::class_<InterfaceExtended, InterfaceBase>(m, "InterfaceExtended", py::multiple_inheritance())
        .def("bar", &InterfaceExtended::bar);
    py::class_<Helper>(m, "Helper")
        .def(py::init())
        .def("create_base_instance", &Helper::createBaseInstance, py::return_value_policy::reference)
        .def("create_extended_instance", &Helper::createExtendedInstance, py::return_value_policy::reference)
        .def("delete_base_instance", &Helper::deleteBaseInstance)
        .def("delete_extended_instance", &Helper::deleteExtendedInstance);
}

I compile with:

c++ -O3 -Wall -shared -std=c++11 -fPIC `python3 -m pybind11 --includes` pybind11bug.cpp -o example`python3-config --extension-suffix`

Then i run:

import example

helper = example.Helper()
base = helper.create_base_instance()
ext = helper.create_extended_instance()
ext.foo()
ext.bar()
# deleting a return_value_policy::reference on c++ side 
# for the base (non-virtual inheritance class) is fine
helper.delete_base_instance()
# deleting a return_value_policy::reference on c++ side 
# for the extended (virtual inheritance class) generates a segfault on cleanup
# (when commenting out the next line there is no segfault)
helper.delete_extended_instance()