Skip to content

Commit 076c89f

Browse files
authored
tests: test recursive dispatch using visitor pattern (#3365)
1 parent 606f81a commit 076c89f

File tree

2 files changed

+73
-0
lines changed

2 files changed

+73
-0
lines changed

tests/test_virtual_functions.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,25 @@ struct DispatchIssue : Base {
174174
}
175175
};
176176

177+
// An abstract adder class that uses visitor pattern to add two data
178+
// objects and send the result to the visitor functor
179+
struct AdderBase {
180+
struct Data {};
181+
using DataVisitor = std::function<void (const Data&)>;
182+
183+
virtual void operator()(const Data& first, const Data& second, const DataVisitor& visitor) const = 0;
184+
virtual ~AdderBase() = default;
185+
AdderBase() = default;
186+
AdderBase(const AdderBase&) = delete;
187+
};
188+
189+
struct Adder : AdderBase {
190+
void operator()(const Data& first, const Data& second, const DataVisitor& visitor) const override {
191+
PYBIND11_OVERRIDE_PURE_NAME(void, AdderBase, "__call__", operator(), first, second, visitor);
192+
}
193+
};
194+
195+
177196
static void test_gil() {
178197
{
179198
py::gil_scoped_acquire lock;
@@ -295,6 +314,27 @@ TEST_SUBMODULE(virtual_functions, m) {
295314

296315
m.def("dispatch_issue_go", [](const Base * b) { return b->dispatch(); });
297316

317+
// test_recursive_dispatch_issue
318+
// #3357: Recursive dispatch fails to find python function override
319+
pybind11::class_<AdderBase, Adder>(m, "Adder")
320+
.def(pybind11::init<>())
321+
.def("__call__", &AdderBase::operator());
322+
323+
pybind11::class_<AdderBase::Data>(m, "Data")
324+
.def(pybind11::init<>());
325+
326+
m.def("add2", [](const AdderBase::Data& first, const AdderBase::Data& second,
327+
const AdderBase& adder, const AdderBase::DataVisitor& visitor) {
328+
adder(first, second, visitor);
329+
});
330+
331+
m.def("add3", [](const AdderBase::Data& first, const AdderBase::Data& second, const AdderBase::Data& third,
332+
const AdderBase& adder, const AdderBase::DataVisitor& visitor) {
333+
adder(first, second, [&] (const AdderBase::Data& first_plus_second) {
334+
adder(first_plus_second, third, visitor);
335+
});
336+
});
337+
298338
// test_override_ref
299339
// #392/397: overriding reference-returning functions
300340
class OverrideTest {

tests/test_virtual_functions.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,39 @@ def dispatch(self):
257257
assert m.dispatch_issue_go(b) == "Yay.."
258258

259259

260+
def test_recursive_dispatch_issue(msg):
261+
"""#3357: Recursive dispatch fails to find python function override"""
262+
263+
class Data(m.Data):
264+
def __init__(self, value):
265+
super(Data, self).__init__()
266+
self.value = value
267+
268+
class Adder(m.Adder):
269+
def __call__(self, first, second, visitor):
270+
# lambda is a workaround, which adds extra frame to the
271+
# current CPython thread. Removing lambda reveals the bug
272+
# [https://github.com/pybind/pybind11/issues/3357]
273+
(lambda: visitor(Data(first.value + second.value)))()
274+
275+
class StoreResultVisitor:
276+
def __init__(self):
277+
self.result = None
278+
279+
def __call__(self, data):
280+
self.result = data.value
281+
282+
store = StoreResultVisitor()
283+
284+
m.add2(Data(1), Data(2), Adder(), store)
285+
assert store.result == 3
286+
287+
# without lambda in Adder class, this function fails with
288+
# RuntimeError: Tried to call pure virtual function "AdderBase::__call__"
289+
m.add3(Data(1), Data(2), Data(3), Adder(), store)
290+
assert store.result == 6
291+
292+
260293
def test_override_ref():
261294
"""#392/397: overriding reference-returning functions"""
262295
o = m.OverrideTest("asdf")

0 commit comments

Comments
 (0)