Skip to content

[BUG]: PYBIND11_OVERRIDE_IMPL issue with multiple inheritance due to static_cast #3902

Open
@Holt59

Description

@Holt59

Required prerequisites

Problem description

When using PYBIND11_OVERRIDE_PURE inside of class with multiple inheritance, e.g.

class A { virtual ~A() {} };
class B { virtual std::string fn() const = 0; virtual ~B() {} }

class PyClass : public A, public B {
public:
    std::string fn() const override {
        PYBIND11_OVERRIDE_PURE(QString, B, fn, );
    }
};

// A is not exposed in Python

py::class_<B,  PyB>(m, "B", py::multiple_inheritance())
    .def(py::init<>()) // should this be init_alias?
    .def("function", &B::fn);

This functions throws a "Tried to call pure virtual function ..." exception when constructing a new B in Python.

Removing this static_cast:

https://github.com/pybind/pybind11/blob/master/include/pybind11/pybind11.h#L2729

...solves the issue.

Is there any reason to have this static_cast here? Is there something I'm missing?

Reproducible example code

#include <iostream>

#include <pybind11/embed.h>
#include <pybind11/pybind11.h>

class A {
public:
    virtual ~A() {}
};

class B {
public:
    virtual std::string fn() const = 0;

    virtual ~B() {}
};

constexpr const char* fmt_s = "{:20}: {}\n";

// switch A and B here fixes the issue since the function is from B
class PyB : public A, public B {
public:
    std::string fn() const override {
        std::cout << "fn(), this           : " << (void*)this << "\n";
        std::cout << "fn(), static_cast<>  : " << (void*)static_cast<const B*>(this)
                  << "\n";
        std::cout << "fn(), dynamic_cast<> : " << (void*)dynamic_cast<const B*>(this)
                  << "\n";

        PYBIND11_OVERRIDE_PURE(std::string, B, fn, );
    }
};

namespace py = pybind11;

PYBIND11_EMBEDDED_MODULE(cpp_module, m) {
    py::class_<B, PyB>(m, "B", py::multiple_inheritance())
        .def(py::init<>())
        .def("fn", &B::fn);
}

int main() {
    py::scoped_interpreter guard{};
    py::exec(R"(
        from cpp_module import B

        class MyB(B):
            def fn(self): return "MyB"

        b = MyB()
    )");
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions