pybind11
pybind11 copied to clipboard
[BUG]: Pure Virtual Function Call When Creating Python-derived Instance via C++ Callback
Required prerequisites
- [x] Make sure you've read the documentation. Your issue may be addressed there.
- [x] Search the issue tracker and Discussions to verify that this hasn't already been reported. +1 or comment there if it has.
- [x] Consider asking first in the Gitter chat room or in a Discussion.
What version (or hash if on master) of pybind11 are you using?
d28904f12e906dc43f72b549a6b26bceb9c2caff
Problem description
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.
build git:(master) python test.py
A
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"
Reproducible example code
#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
# 例如在 Python 中定义一个 A 的子类
class PyDerivedA(example.A):
def __init__(self):
super().__init__()
print("111111111111111111")
def go(self):
print("PyDerivedA go called!")
# 注册回调,将字符串 "derived" 与创建 PyDerivedA 实例的回调绑定
def create_derived(name):
return PyDerivedA()
factory = example.MyFactory.instance()
factory.registry("derived", create_derived)
# 调用 MyRun 执行 go 方法
runner = example.MyRun()
runner.run("derived") # 输出 "PyDerivedA go called!"
Is this a regression? Put the last known working version here if it is.
not a regression
Compile: c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)
Python 3.9.21 (main, Mar 12 2025, 20:46:55) [GCC 9.4.0] on linux Type "help", "copyright", "credits" or "license" for more information.
class PyA : public A, public py::trampoline_self_life_support{
public:
using A::A;
void go() override {
PYBIND11_OVERLOAD_PURE(
void, // 返回类型
A, // 父类
go // 方法名
);
}
};
I change my class using trampoline_self_life_support, but it still "RuntimeError: Tried to call pure virtual function "A::go""
@rwgk Very thanks for your work! But for this issue type, can you support it! I find many this type issue question.
I solve it!
#include <pybind11/pybind11.h>
#include <pybind11/functional.h> // 支持 std::function 类型
#include <pybind11/trampoline_self_life_support.h>
#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::unique_ptr<A>(const std::string&)>;
class PyA : public A, public py::trampoline_self_life_support{
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::unique_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) {
py::class_<A, PyA,py::smart_holder>(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); // 关键点在这里
py::class_<MyRun>(m, "MyRun")
.def(py::init<>())
.def("run", &MyRun::Run);
}