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"