[Python] Pybind11, Not find Pure Virtual Function Call When Creating Python-derived Instance...

Discussão em 'Python' iniciado por Stack, Março 14, 2025.

  1. Stack

    Stack Membro Participativo

    When creating a Python-derived class from a C++ base class (with a pure virtual function) via a callback, the Python override is not invoked. Instead, the program attempts to call the pure virtual function (A::go), resulting in a runtime error and segmentation fault.

    Expected Behavior: The overridden Python method (e.g., PyDerivedA.go) should be called, producing the expected output (e.g., "PyDerivedA go called!").

    Actual Behavior: The callback returns an instance of the Python-derived class, but due to improper conversion, the object loses its Python override bindings. When MyRun::Run is executed, it calls the pure virtual function A::go, triggering a runtime error and segmentation fault.

    #include <pybind11/pybind11.h>
    #include <pybind11/functional.h> // 支持 std::function 类型
    #include <memory>
    #include <unordered_map>
    #include <iostream>
    #include <string>
    #include <functional>
    namespace py = pybind11;



    class A {
    public:
    A() { std::cout << "A" << std::endl; }
    virtual void go() = 0;
    virtual ~A()=default;
    };
    using CreationEvaluatorCallback = std::function<std::shared_ptr<A>(const std::string&)>;
    class PyA : public A {
    public:
    using A::A;
    void go() override {
    PYBIND11_OVERLOAD_PURE(
    void, // 返回类型
    A, // 父类
    go // 方法名
    );
    }
    };


    class MyFactory {
    public:
    static MyFactory& Instance() {
    static MyFactory instance; // C++11 保证局部静态变量线程安全
    return instance;
    }
    void Registry(const std::string &name, CreationEvaluatorCallback callback) {
    callback_registry_[name] = callback;
    }

    std::shared_ptr<A> Create(const std::string &evaluator_name) {
    auto iter = callback_registry_.find(evaluator_name);
    if (iter != callback_registry_.end()) {
    return iter->second(evaluator_name);
    }
    // 如果未找到对应的回调,可根据需要返回 nullptr 或抛出异常
    return nullptr;
    }

    private:
    MyFactory() = default;
    ~MyFactory() = default;

    // 禁用拷贝构造和赋值运算符
    MyFactory(const MyFactory&) = delete;
    MyFactory& operator=(const MyFactory&) = delete;

    std::unordered_map<std::string, CreationEvaluatorCallback> callback_registry_;
    };

    class MyRun {
    public:
    MyRun(){}
    ~MyRun(){}
    void Run(const std::string &evaluator_name){
    auto eval = MyFactory::Instance().Create(evaluator_name);
    eval->go();
    }
    };


    PYBIND11_MODULE(example, m) {
    m.doc() = "pybind11 包装 MyFactory 和 MyRun 示例";

    py::class_<A, PyA, std::shared_ptr<A>>(m, "A")
    .def(py::init<>())
    .def("go", &A::go);

    py::class_<MyFactory, std::unique_ptr<MyFactory, py::nodelete>>(m, "MyFactory")
    .def_static("instance", &MyFactory::Instance, py::return_value_policy::reference)
    .def("registry", &MyFactory::Registry)
    .def("create", &MyFactory::Create);

    // 包装 MyRun
    py::class_<MyRun>(m, "MyRun")
    .def(py::init<>())
    .def("run", &MyRun::Run);
    }


    import example
    class PyDerivedA(example.A):
    def __init__(self):
    super().__init__()
    print("111111111111111111")

    def go(self):
    print("PyDerivedA go called!")


    def create_derived(name):
    return PyDerivedA()

    factory = example.MyFactory.instance()
    factory.registry("derived", create_derived)


    runner = example.MyRun()
    runner.run("derived") # 输出 "PyDerivedA go called!"


    Traceback (most recent call last):
    File "/home/mi/fc4tdisk/code/pybind11/build/test.py", line 19, in <module>
    runner.run("derived") # 输出 "PyDerivedA go called!"
    RuntimeError: Tried to call pure virtual function "A::go"

    Continue reading...

Compartilhe esta Página