here is an example of a code where i have a virtual class A, with its child A_2 and other classes/functions that have A as argument/return type. In use i could use instead A_2, A_3 ...
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <iostream>
namespace py = pybind11;
class A {
public:
float i;
A();
virtual void virt_func() = 0;
};
class A_2 : public A {
public:
A_2();
A_2(float value){ i = value ;};
void virt_func() override {std::cout << i << std::endl ;};
};
class B {
public:
B();
A* somefunc(float i){ A* a = new A_2(i); return a;};
};
class C {
public:
C();
void somefunc(A* a){
std::cout << a->i << std::endl ;
};
};
PYBIND11_MODULE(my_module, m)
{
m.doc() = "Some module.";
py::class_<A> py_A(m, "A");
py_A.def(py::init<>());
py::class_<A_2> py_A_2(m, "A_2");
py_A_2.def(py::init<>());
py_A_2.def(py::init<float>(), py::arg("value"));
py_A_2.def("virt_func", &A_2::virt_func);
py::class_<B> py_B(m, "B");
py_B.def(py::init<>());
py_B.def("somefunc", &B::somefunc, py::arg("i"));
py::class_<C> py_C(m, "B");
py_C.def(py::init<>());
py_C.def("somefunc", &C::somefunc, py::arg("a"));
};
Here are the corresponding compilation commands:
g++ \
-O2 \
-std=c++14 \
$(python -m pybind11 --includes) \
example.cpp \
-lMDLSOLVER \
-ldl \
-fPIC \
-c \
-nostartfiles \
-o ./my_module.o
if test -f "my_module.o"
then
g++ -std=c++14 -shared ./my_module.o -o my_module.so
if test -f "my_module.so"
then
rm my_module.o
python test_python.py
fi
fi
and a python example of how we could use it from python:
import my_module
b = my_module.B()
a2 = b.somefunc(5.)
c = my_module.C()
c.somefunc(a2)
If i compile it, i get this error:
erreur: invalid new-expression of abstract class type ‘A’
return new Class{std::forward<Args>(args)...};
^
example.cpp:8:7: note: because the following virtual functions are pure within ‘A’:
class A {
^
example.cpp:13:22: note: virtual void A::virt_func()
virtual void virt_func() = 0;
If i bind A* instead of A, i get this error instead:
Traceback (most recent call last):
File "test_python.py", line 1, in <module>
import my_module
ImportError: /home/catA/tm267619/Workspace/MendelWork/BindingMendel/essai_bin_virtual/my_module.so: undefined symbol: _ZTV1A
Having A instead of A* as argument/return type of the functions somefunc are not fixing it in any way.
What can i do to be able to bind the classes B and C without error?
Thanks
The problem is solved using a Tranpoline class:
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <iostream>
#include <sstream>
namespace py = pybind11;
class A {
public:
float i = 0;
A() {};
virtual void virt_func() = 0;
void set_i(float val){i = val;};
};
class trampoline_A : public A{
using A::A;
void virt_func() override {
PYBIND11_OVERRIDE_PURE(
void, /* Return type */
A, /* Parent class */
virt_func, /* Name of function in C++ (must match Python name) */
/* Argument(s) */
);
};
};
class A_2 : public A {
public:
A_2() {};
A_2(float value){ i = value ;};
void virt_func() override {std::cout << "calling virt_func from A_2 : " << i << std::endl ;};
};
class B {
public:
B() {};
A* somefunc(float i){ A* a = new A_2(i); return a;};
};
class C {
public:
C() {};
void somefunc(A* a){
std::cout << a->i << std::endl ;
};
};
PYBIND11_MODULE(my_module, m)
{
m.doc() = "Some module.";
py::class_<A, trampoline_A> py_A(m, "A") ;
py_A.def(py::init<>());
py_A.def("set_i", &A::set_i);
py::class_<A_2, A> py_A_2(m, "A_2");
py_A_2.def(py::init<>());
py_A_2.def(py::init<float>());
py_A_2.def("virt_func", &A_2::virt_func);
py_A_2.def("set_i", &A_2::set_i);
py_A_2.def("__repr__", [](A_2& a){
std::stringstream ss;
ss << a.i;
std::string str = ss.str();
std::string result = "A_2 object with value: " + str;
return result;
});
py::class_<B> py_B(m, "B");
py_B.def(py::init<>());
py_B.def("somefunc", &B::somefunc, py::arg("i"));
py::class_<C> py_C(m, "C");
py_C.def(py::init<>());
py_C.def("somefunc", &C::somefunc, py::arg("a"));
};