pybind11 以虚拟类作为参数或返回类型的函数绑定(已解决)

问题描述 投票:0回答:1

这是一个代码示例,其中我有一个虚拟类 A,其子类 A_2 和其他以 A 作为参数/返回类型的类/函数。在使用中我可以使用 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"));
};


以下是对应的编译命令:


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

以及我们如何从 python 中使用它的 python 示例:

import my_module

b = my_module.B()

a2 = b.somefunc(5.)

c = my_module.C()

c.somefunc(a2)

如果我编译它,我会收到此错误:

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;

如果我绑定 A* 而不是 A,则会收到此错误:

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

使用 A 而不是 A* 作为函数 somefunc 的参数/返回类型并不能以任何方式修复它。

我该怎么做才能正确绑定类 B 和 C?

谢谢

c++ abstract-class pybind11
1个回答
0
投票

使用 Tranpoline 类解决了问题:

#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"));
};
© www.soinside.com 2019 - 2024. All rights reserved.