pybind11 icon indicating copy to clipboard operation
pybind11 copied to clipboard

[BUG]: Using Virtual Inheritance and Multiple Inheritance Together Can Cause Crashes

Open hzywlx opened this issue 11 months ago • 1 comments

Required prerequisites

  • [X] Make sure you've read the documentation. Your issue may be addressed there.
  • [X] Search the issue tracker and Discussions to verify that this hasn't already been reported. +1 or comment there if it has.
  • [ ] Consider asking first in the Gitter chat room or in a Discussion.

What version (or hash if on master) of pybind11 are you using?

2.13.6

Problem description

  • Environment:

    • Windows 11
    • Visual Studio 2019
    • Python v3.12.4
    • Pybind11 v2.13.6
  • Issue: When exporting a C++ class that uses virtual inheritance of multiple base classes through Pybind11, deleting the class object in Python code causes a crash.

  • Steps to Reproduce:

    • Create a C++ class with virtual inheritance of multiple base classes.
    • Export the class to Python using Pybind11.
    • Create and delete an instance of the class in Python code.
  • Expected Behavior: The class object should be deleted without causing a crash.

  • Actual Behavior: Deleting the class object in Python code causes a crash, when del derived manual or exit test function.

  • Additional Information: Removing virtual inheritance from the C++ class resolves the issue.

Reproducible example code

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

namespace py = pybind11;

class Base1 {
public:
    virtual ~Base1() = default;
    virtual std::string greet_base1() = 0;
};

class Base2 {
public:
    virtual ~Base2() = default;
    virtual std::string greet_base2() = 0;
};

class Derived : 
    public Base1,
    public Base2
{
public:
    virtual ~Derived() = default;
    virtual std::string greet_derived() = 0;
};

// create DerivedImpl class
class DerivedImpl : public Derived {
public:
    virtual ~DerivedImpl() = default;

    virtual std::string greet_derived() {
        return "Hello derived";
    }

    virtual std::string greet_base1() {
        return "Hello base1";
    }

    virtual std::string greet_base2() {
        return "Hello base2";
    }
private:
    int z;
};

class DerivedFactory {
public:
    static Derived* create_derived() {
        return new DerivedImpl();
    }

    static void delete_derived(Derived* obj) {
        return delete obj;
    }
};

PYBIND11_MODULE(pybind_test, m) {

    py::class_<Base1, std::unique_ptr<Base1, py::nodelete>>(m, "Base1")
        .def("greet_base1", &Base1::greet_base1);

    py::class_<Base2, std::unique_ptr<Base2, py::nodelete>>(m, "Base2")
        .def("greet_base2", &Base2::greet_base2);

    py::class_<Derived, Base1, Base2, std::unique_ptr<Derived, py::nodelete>>(m, "Derived")
        .def("greet_derived", &Derived::greet_derived);

    // create DerivedFactory class binding
    py::class_<DerivedFactory>(m, "DerivedFactory")
        .def_static("create_derived", &DerivedFactory::create_derived)
        .def_static("delete_derived", &DerivedFactory::delete_derived);
}

import pybind_test

def test():
    derived = pybind_test.DerivedFactory.create_derived()
    print(derived.greet_derived())
    print(derived.greet_base1())
    print(derived.greet_base2())
    pybind_test.DerivedFactory.delete_derived(derived)
    # del derived
    print("end of test")

if __name__ == "__main__":
    test()
    print("end of main")

Is this a regression? Put the last known working version here if it is.

Not a regression

hzywlx avatar Dec 29 '24 08:12 hzywlx

have you tried using https://pybind11.readthedocs.io/en/stable/reference.html#_CPPv420multiple_inheritance

petersteneteg avatar May 26 '25 12:05 petersteneteg