inheritancetypespybind11

How to access derived type at runtime with pybind11?


I have a Base and a Derived class in C++ for which I create Python-bindings with pybind11. The base class implements a function (__repr__) in which I want to access the runtime type name of self. However, the way I do it, it just provides me the base type:

class Base {};

class Derived : public Base {};

PYBIND11_MODULE(mwe, m) {
    py::class_<Base>(m, "Base")
        .def("__repr__", [](const Base& self) {
            // want to see the runtime type (i.e. most derived)
            // of the `self` object!
            return py::type::of(py::cast(self)).attr("__name__");
        });
    py::class_<Derived, Base>(m, "Derived")
        .def(py::init<>());
}

In Python:

from mwe import Base, Derived

print(Derived())  # returns "Base", not "Derived"

How would I access the runtime type of self instead?


Solution

  • The second look into the documentation gave me the right hint:

    In C++, a type is only considered polymorphic if it has at least one virtual function and pybind11 will automatically recognize this

    (see https://pybind11.readthedocs.io/en/stable/classes.html).

    That means in my case Derived is a non-polymorphic type behind a Base pointer, such that Python just sees the Base.

    One way to fix this would be to add a virtual function to Base to achieve actual polymorphism, e.g.:

    class Base {
        virtual ~Base() = default;
    };
    

    However, this is a change on the implementation and I still wonder whether one could achieve the intended behavior without changing Base or Derived.