mirror of
https://github.com/pybind/pybind11.git
synced 2026-04-19 22:39:09 +00:00
Fix template trampoline overload lookup failure
Problem ======= The template trampoline pattern documented in PR #322 has a problem with virtual method overloads in intermediate classes in the inheritance chain between the trampoline class and the base class. For example, consider the following inheritance structure, where `B` is the actual class, `PyB<B>` is the trampoline class, and `PyA<B>` is an intermediate class adding A's methods into the trampoline: PyB<B> -> PyA<B> -> B -> A Suppose PyA<B> has a method `some_method()` with a PYBIND11_OVERLOAD in it to overload the virtual `A::some_method()`. If a Python class `C` is defined that inherits from the pybind11-registered `B` and tries to provide an overriding `some_method()`, the PYBIND11_OVERLOADs declared in PyA<B> fails to find this overloaded method, and thus never invoke it (or, if pure virtual and not overridden in PyB<B>, raises an exception). This happens because the base (internal) `PYBIND11_OVERLOAD_INT` macro simply calls `get_overload(this, name)`; `get_overload()` then uses the inferred type of `this` to do a type lookup in `registered_types_cpp`. This is where it fails: `this` will be a `PyA<B> *`, but `PyA<B>` is neither the base type (`B`) nor the trampoline type (`PyB<B>`). As a result, the overload fails and we get a failed overload lookup. The fix ======= The fix is relatively simple: we can cast `this` passed to `get_overload()` to a `const B *`, which lets get_overload look up the correct class. Since trampoline classes should be derived from `B` classes anyway, this cast should be perfectly safe. This does require adding the class name as an argument to the PYBIND11_OVERLOAD_INT macro, but leaves the public macro signatures unchanged.
This commit is contained in:
@@ -145,6 +145,9 @@ public: \
|
||||
for (unsigned i = 0; i < times; ++i) \
|
||||
s += "hi"; \
|
||||
return s; \
|
||||
} \
|
||||
std::string say_everything() { \
|
||||
return say_something(1) + " " + std::to_string(unlucky_number()); \
|
||||
}
|
||||
A_METHODS
|
||||
};
|
||||
@@ -253,7 +256,8 @@ void initialize_inherited_virtuals(py::module &m) {
|
||||
py::class_<A_Repeat, std::unique_ptr<A_Repeat>, PyA_Repeat>(m, "A_Repeat")
|
||||
.def(py::init<>())
|
||||
.def("unlucky_number", &A_Repeat::unlucky_number)
|
||||
.def("say_something", &A_Repeat::say_something);
|
||||
.def("say_something", &A_Repeat::say_something)
|
||||
.def("say_everything", &A_Repeat::say_everything);
|
||||
py::class_<B_Repeat, std::unique_ptr<B_Repeat>, PyB_Repeat>(m, "B_Repeat", py::base<A_Repeat>())
|
||||
.def(py::init<>())
|
||||
.def("lucky_number", &B_Repeat::lucky_number);
|
||||
@@ -266,7 +270,8 @@ void initialize_inherited_virtuals(py::module &m) {
|
||||
py::class_<A_Tpl, std::unique_ptr<A_Tpl>, PyA_Tpl<>>(m, "A_Tpl")
|
||||
.def(py::init<>())
|
||||
.def("unlucky_number", &A_Tpl::unlucky_number)
|
||||
.def("say_something", &A_Tpl::say_something);
|
||||
.def("say_something", &A_Tpl::say_something)
|
||||
.def("say_everything", &A_Tpl::say_everything);
|
||||
py::class_<B_Tpl, std::unique_ptr<B_Tpl>, PyB_Tpl<>>(m, "B_Tpl", py::base<A_Tpl>())
|
||||
.def(py::init<>())
|
||||
.def("lucky_number", &B_Tpl::lucky_number);
|
||||
|
||||
Reference in New Issue
Block a user