Polymorphism and pybind11

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:

 /* polymorphism.hpp */ #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.

 /* polymorphism.hpp */ #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"); } 
+5
source share
1 answer

I don't know why, but pybind11 seems to have problems with the source pointers in mylist() . The example works correctly if you change the return type to vector<unique_ptr<MyBase>> . The following example compiles into the python example module and produces the expected result.

example.cpp:

 #include <pybind11/stl.h> #include <pybind11/pybind11.h> #include <iostream> #include <memory> #include <vector> class MyBase { public: virtual void print() const { std::cout << "MyBase::print()" << std::endl; } }; class MyDerived : public MyBase { public: virtual void print() const override { std::cout << "MyDerived::print()" << std::endl; } }; std::vector<std::unique_ptr<MyBase>> mylist() { std::vector<std::unique_ptr<MyBase>> v; v.push_back(std::make_unique<MyBase>()); v.push_back(std::make_unique<MyDerived>()); return v; } PYBIND11_MODULE(example, m) { pybind11::class_<MyBase>(m, "MyBase") .def(pybind11::init<>()) .def("print", &MyBase::print) .def("__repr__", [](MyBase const&) { return "MyBase"; }); pybind11::class_<MyDerived>(m, "MyDerived") .def(pybind11::init<>()) .def("print", &MyDerived::print) .def("__repr__", [](MyDerived const&) { return "MyDerived"; }); m.def("mylist", &mylist, "returns a list"); } 

python shell:

 >>> import example >>> l = example.mylist() >>> l[0].print() MyBase::print() >>> l[1].print() MyDerived::print() 
+1
source

Source: https://habr.com/ru/post/1276234/


All Articles