Skip to content

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

Description

@hzywlx

Required prerequisites

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    triageNew bug, unverified

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions