I have weird behavior with pybind11 when I want to use C ++ polymorphism in Python. Here is a simple example of my problem:
import polymorphism as plm a = plm.mylist() print(a) a[0].print() a[1].print()
The output of this script is
[MyBase, MyDerived]
Mybase
Mybase
but expected result
[MyBase, MyDerived]
Mybase
Myerdive
because mylist returns std :: vector, which contains an instance of the derived class (MyDerived) as the second member. It is strange that MyDerived is recognized when I print the list as a whole.
Here is the C ++ code header file:
#ifndef POLYMORPHISM_HPP #define POLYMORPHISM_HPP #include <vector> class MyBase { public: virtual void print() const; }; class MyDerived : public MyBase { public: virtual void print() const; }; std::vector<MyBase*> mylist(); #endif
And here is the cpp file:
#include "polymorphism.hpp" #include <iostream> #include <pybind11/stl.h> #include <pybind11/pybind11.h> void MyBase::print() const { std::cout << "MyBase" << std::endl; } void MyDerived::print() const { std::cout << "MyDerived" << std::endl; } std::vector<MyBase*> mylist() { std::vector<MyBase*> list(2); list[0] = new MyBase(); list[1] = new MyDerived(); return list; } PYBIND11_MODULE(polymorphism, m) { pybind11::class_<MyBase>(m, "MyBase") .def(pybind11::init<>()) .def("print", &MyBase::print) .def("__repr__", [](const MyBase &a) { return "MyBase"; }); pybind11::class_<MyDerived, MyBase>(m, "MyDerived") .def(pybind11::init<>()) .def("print", &MyDerived::print) .def("__repr__", [](const MyDerived &a) { return "MyDerived"; }); m.def("mylist", &mylist, "return a list"); }
EDIT: more surprisingly, when I remove the "print" binding for MyDerived, I get the following error message
[MyBase, MyDerived]
Mybase
Traceback (last last call):
File "test.py", line 8, in
and [1] .print ()
AttributeError: the object "polymorphism.MyDerived" does not have the attribute 'print'
This message means that MyDerived is well known when the wrong print version is called (if I understand well).
EDIT 2: This version uses trampoline classes. However, this version leads to the same wrong exit.
#ifndef POLYMORPHISM_HPP #define POLYMORPHISM_HPP #include <vector> #include <pybind11/stl.h> #include <pybind11/pybind11.h> class MyBase { public: virtual void print() const; }; class MyDerived : public MyBase { public: virtual void print() const; }; std::vector<MyBase*> mylist(); class PyMyBase : public MyBase { public: using MyBase::MyBase; // Inherit constructors void print() const override { PYBIND11_OVERLOAD(void, MyBase, print ); } }; class PyMyDerived : public MyDerived { public: using MyDerived::MyDerived; // Inherit constructors void print() const override { PYBIND11_OVERLOAD(void, MyDerived, print);} }; #endif
Here is the corresponding cpp file:
/* polymorphism.cpp */ #include "polymorphism.hpp" #include <iostream> void MyBase::print() const { std::cout << "MyBase" << std::endl; } void MyDerived::print() const { std::cout << "MyDerived" << std::endl; } std::vector<MyBase*> mylist() { std::vector<MyBase*> list(2); list[0] = new MyBase(); list[1] = new MyDerived(); return list; } PYBIND11_MODULE(polymorphism, m) { pybind11::class_<MyBase, PyMyBase>(m, "MyBase") .def(pybind11::init<>()) .def("print", &MyBase::print) .def("__repr__", [](const MyBase &a) { return "MyBase"; }); pybind11::class_<MyDerived, PyMyDerived>(m, "MyDerived") .def(pybind11::init<>()) .def("print", &MyDerived::print) .def("__repr__", [](const MyDerived &a) { return "MyDerived"; }); m.def("mylist", &mylist, "return a list"); }