mirror of
https://github.com/pybind/pybind11.git
synced 2026-03-14 20:27:47 +00:00
test_potentially_slicing_shared_ptr.cpp,py (for smart_holder only)
This commit is contained in:
@@ -130,11 +130,9 @@ set(PYBIND11_TEST_FILES
|
||||
test_class_sh_trampoline_shared_from_this
|
||||
test_class_sh_trampoline_shared_ptr_cpp_arg
|
||||
test_class_sh_trampoline_unique_ptr
|
||||
test_class_sh_trampoline_weak_ptr
|
||||
test_class_sh_unique_ptr_custom_deleter
|
||||
test_class_sh_unique_ptr_member
|
||||
test_class_sh_virtual_py_cpp_mix
|
||||
test_class_sp_trampoline_weak_ptr
|
||||
test_const_name
|
||||
test_constants_and_functions
|
||||
test_copy_move
|
||||
@@ -163,6 +161,7 @@ set(PYBIND11_TEST_FILES
|
||||
test_opaque_types
|
||||
test_operator_overloading
|
||||
test_pickling
|
||||
test_potentially_slicing_shared_ptr
|
||||
test_python_multiple_inheritance
|
||||
test_pytypes
|
||||
test_sequences_and_iterators
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright (c) 2025 The Pybind Development Team.
|
||||
// All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace pybind11_tests {
|
||||
namespace class_sh_trampoline_weak_ptr {
|
||||
|
||||
struct VirtBase {
|
||||
virtual ~VirtBase() = default;
|
||||
virtual int get_code() { return 100; }
|
||||
};
|
||||
|
||||
struct PyVirtBase : VirtBase /*, py::trampoline_self_life_support */ {
|
||||
using VirtBase::VirtBase;
|
||||
int get_code() override { PYBIND11_OVERRIDE(int, VirtBase, get_code); }
|
||||
|
||||
~PyVirtBase() override {
|
||||
fflush(stderr);
|
||||
printf("\nLOOOK ~PyVirtBase()\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
};
|
||||
|
||||
struct WpOwner {
|
||||
void set_wp(const std::shared_ptr<VirtBase> &sp) { wp = sp; }
|
||||
|
||||
int get_code() {
|
||||
auto sp = wp.lock();
|
||||
if (!sp) {
|
||||
return -999;
|
||||
}
|
||||
return sp->get_code();
|
||||
}
|
||||
|
||||
private:
|
||||
std::weak_ptr<VirtBase> wp;
|
||||
};
|
||||
|
||||
std::shared_ptr<VirtBase> pass_through_sp_VirtBase(const std::shared_ptr<VirtBase> &sp) {
|
||||
return sp;
|
||||
}
|
||||
|
||||
} // namespace class_sh_trampoline_weak_ptr
|
||||
} // namespace pybind11_tests
|
||||
|
||||
using namespace pybind11_tests::class_sh_trampoline_weak_ptr;
|
||||
|
||||
TEST_SUBMODULE(class_sh_trampoline_weak_ptr, m) {
|
||||
py::classh<VirtBase, PyVirtBase>(m, "VirtBase")
|
||||
.def(py::init<>())
|
||||
.def("get_code", &VirtBase::get_code);
|
||||
|
||||
py::classh<WpOwner>(m, "WpOwner")
|
||||
.def(py::init<>())
|
||||
.def("set_wp",
|
||||
[](WpOwner &self, py::handle obj) {
|
||||
self.set_wp(py::potentially_slicing_shared_ptr<VirtBase>(obj));
|
||||
})
|
||||
.def("get_code", &WpOwner::get_code);
|
||||
|
||||
m.def("pass_through_sp_VirtBase", pass_through_sp_VirtBase);
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import gc
|
||||
|
||||
import pytest
|
||||
|
||||
import env
|
||||
import pybind11_tests.class_sh_trampoline_weak_ptr as m
|
||||
|
||||
|
||||
class PyDrvd(m.VirtBase):
|
||||
def get_code(self):
|
||||
return 200
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("vtype", "expected_code"), [(m.VirtBase, 100), (PyDrvd, 200)])
|
||||
def test_weak_ptr_owner(vtype, expected_code):
|
||||
wpo = m.WpOwner()
|
||||
assert wpo.get_code() == -999
|
||||
|
||||
obj = vtype()
|
||||
assert obj.get_code() == expected_code
|
||||
|
||||
wpo.set_wp(obj)
|
||||
assert wpo.get_code() == expected_code
|
||||
|
||||
del obj
|
||||
if env.PYPY or env.GRAALPY:
|
||||
pytest.skip("Cannot reliably trigger GC")
|
||||
assert wpo.get_code() == -999
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("vtype", "expected_code"), [(m.VirtBase, 100), (PyDrvd, 200)])
|
||||
def test_pass_through_sp_VirtBase(vtype, expected_code):
|
||||
obj = vtype()
|
||||
ptr = m.pass_through_sp_VirtBase(obj)
|
||||
print("\nLOOOK BEFORE del obj", flush=True)
|
||||
del obj
|
||||
print("\nLOOOK AFTER del obj", flush=True)
|
||||
gc.collect()
|
||||
print("\nLOOOK AFTER gc.collect()", flush=True)
|
||||
assert ptr.get_code() == expected_code
|
||||
print("\nLOOOK AFTER ptr.get_code()", flush=True)
|
||||
|
||||
|
||||
def test_potentially_slicing_shared_ptr_not_convertible_error():
|
||||
wpo = m.WpOwner()
|
||||
with pytest.raises(Exception) as excinfo:
|
||||
wpo.set_wp("")
|
||||
assert str(excinfo.value) == (
|
||||
'"str" object is not convertible to std::shared_ptr<T>'
|
||||
" (with T = pybind11_tests::class_sh_trampoline_weak_ptr::VirtBase)"
|
||||
)
|
||||
@@ -1,86 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import gc
|
||||
|
||||
import pytest
|
||||
|
||||
import env
|
||||
import pybind11_tests.class_sp_trampoline_weak_ptr as m
|
||||
|
||||
|
||||
class PyDrvd(m.VirtBase):
|
||||
def get_code(self):
|
||||
return 200
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("vtype", "expected_code"), [(m.VirtBase, 100), (PyDrvd, 200)])
|
||||
def test_with_wp_owner(vtype, expected_code):
|
||||
wpo = m.WpOwner()
|
||||
assert wpo.get_code() == -999
|
||||
|
||||
obj = vtype()
|
||||
assert obj.get_code() == expected_code
|
||||
|
||||
wpo.set_wp(obj)
|
||||
assert wpo.get_code() == expected_code
|
||||
|
||||
del obj
|
||||
if env.PYPY or env.GRAALPY:
|
||||
pytest.skip("Cannot reliably trigger GC")
|
||||
assert wpo.get_code() == -999
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("vtype", "expected_code"), [(m.VirtBase, 100), (PyDrvd, 200)])
|
||||
def test_with_sp_owner(vtype, expected_code):
|
||||
spo = m.SpOwner()
|
||||
assert spo.get_code() == -888
|
||||
|
||||
obj = vtype()
|
||||
assert obj.get_code() == expected_code
|
||||
|
||||
spo.set_sp(obj)
|
||||
assert spo.get_code() == expected_code
|
||||
|
||||
del obj
|
||||
if env.PYPY or env.GRAALPY:
|
||||
pytest.skip("Cannot reliably trigger GC")
|
||||
print("\nLOOOK BEFORE spo.get_code() AFTER del obj", flush=True)
|
||||
assert spo.get_code() == 100 # Inheritance slicing (issue #1333)
|
||||
print("\nLOOOK AFTER spo.get_code() AFTER del obj", flush=True)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("vtype", "expected_code"), [(m.VirtBase, 100), (PyDrvd, 200)])
|
||||
def test_with_sp_and_wp_owners(vtype, expected_code):
|
||||
spo = m.SpOwner()
|
||||
wpo = m.WpOwner()
|
||||
|
||||
obj = vtype()
|
||||
spo.set_sp(obj)
|
||||
wpo.set_wp(obj)
|
||||
|
||||
assert spo.get_code() == expected_code
|
||||
assert wpo.get_code() == expected_code
|
||||
|
||||
del obj
|
||||
if env.PYPY or env.GRAALPY:
|
||||
pytest.skip("Cannot reliably trigger GC")
|
||||
|
||||
# Inheritance slicing (issue #1333)
|
||||
assert spo.get_code() == 100
|
||||
assert wpo.get_code() == 100
|
||||
|
||||
del spo
|
||||
assert wpo.get_code() == -999
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("vtype", "expected_code"), [(m.VirtBase, 100), (PyDrvd, 200)])
|
||||
def test_pass_through_sp_VirtBase(vtype, expected_code):
|
||||
obj = vtype()
|
||||
ptr = m.pass_through_sp_VirtBase(obj)
|
||||
print("\nLOOOK BEFORE del obj", flush=True)
|
||||
del obj
|
||||
print("\nLOOOK AFTER del obj", flush=True)
|
||||
gc.collect()
|
||||
print("\nLOOOK AFTER gc.collect()", flush=True)
|
||||
assert ptr.get_code() == expected_code
|
||||
print("\nLOOOK AFTER ptr.get_code()", flush=True)
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <memory>
|
||||
|
||||
namespace pybind11_tests {
|
||||
namespace class_sp_trampoline_weak_ptr {
|
||||
namespace potentially_slicing_shared_ptr {
|
||||
|
||||
struct VirtBase {
|
||||
virtual ~VirtBase() = default;
|
||||
@@ -17,12 +17,28 @@ struct VirtBase {
|
||||
struct PyVirtBase : VirtBase, py::trampoline_self_life_support {
|
||||
using VirtBase::VirtBase;
|
||||
int get_code() override { PYBIND11_OVERRIDE(int, VirtBase, get_code); }
|
||||
};
|
||||
|
||||
~PyVirtBase() override {
|
||||
fflush(stderr);
|
||||
printf("\nLOOOK ~PyVirtBase()\n");
|
||||
fflush(stdout);
|
||||
std::shared_ptr<VirtBase> rtrn_obj_cast_shared_ptr(py::handle obj) {
|
||||
return obj.cast<std::shared_ptr<VirtBase>>();
|
||||
}
|
||||
|
||||
std::shared_ptr<VirtBase> rtrn_potentially_slicing_shared_ptr(py::handle obj) {
|
||||
return py::potentially_slicing_shared_ptr<VirtBase>(obj);
|
||||
}
|
||||
|
||||
struct SpOwner {
|
||||
void set_sp(const std::shared_ptr<VirtBase> &sp_) { sp = sp_; }
|
||||
|
||||
int get_code() {
|
||||
if (!sp) {
|
||||
return -888;
|
||||
}
|
||||
return sp->get_code();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<VirtBase> sp;
|
||||
};
|
||||
|
||||
struct WpOwner {
|
||||
@@ -40,43 +56,30 @@ private:
|
||||
std::weak_ptr<VirtBase> wp;
|
||||
};
|
||||
|
||||
struct SpOwner {
|
||||
void set_sp(const std::shared_ptr<VirtBase> &sp_) { sp = sp_; }
|
||||
|
||||
int get_code() {
|
||||
if (!sp) {
|
||||
return -888;
|
||||
}
|
||||
return sp->get_code();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<VirtBase> sp;
|
||||
};
|
||||
|
||||
std::shared_ptr<VirtBase> pass_through_sp_VirtBase(const std::shared_ptr<VirtBase> &sp) {
|
||||
return sp;
|
||||
}
|
||||
|
||||
} // namespace class_sp_trampoline_weak_ptr
|
||||
} // namespace potentially_slicing_shared_ptr
|
||||
} // namespace pybind11_tests
|
||||
|
||||
using namespace pybind11_tests::class_sp_trampoline_weak_ptr;
|
||||
using namespace pybind11_tests::potentially_slicing_shared_ptr;
|
||||
|
||||
TEST_SUBMODULE(class_sp_trampoline_weak_ptr, m) {
|
||||
py::class_<VirtBase, std::shared_ptr<VirtBase>, PyVirtBase>(m, "VirtBase")
|
||||
TEST_SUBMODULE(potentially_slicing_shared_ptr, m) {
|
||||
py::classh<VirtBase, PyVirtBase>(m, "VirtBase")
|
||||
.def(py::init<>())
|
||||
.def("get_code", &VirtBase::get_code);
|
||||
|
||||
py::class_<WpOwner>(m, "WpOwner")
|
||||
.def(py::init<>())
|
||||
.def("set_wp", &WpOwner::set_wp)
|
||||
.def("get_code", &WpOwner::get_code);
|
||||
m.def("rtrn_obj_cast_shared_ptr", rtrn_obj_cast_shared_ptr);
|
||||
m.def("rtrn_potentially_slicing_shared_ptr", rtrn_potentially_slicing_shared_ptr);
|
||||
|
||||
py::class_<SpOwner>(m, "SpOwner")
|
||||
py::classh<SpOwner>(m, "SpOwner")
|
||||
.def(py::init<>())
|
||||
.def("set_sp", &SpOwner::set_sp)
|
||||
.def("get_code", &SpOwner::get_code);
|
||||
|
||||
m.def("pass_through_sp_VirtBase", pass_through_sp_VirtBase);
|
||||
py::classh<WpOwner>(m, "WpOwner")
|
||||
.def(py::init<>())
|
||||
.def("set_wp", &WpOwner::set_wp)
|
||||
.def("set_wp_potentially_slicing",
|
||||
[](WpOwner &self, py::handle obj) {
|
||||
self.set_wp(py::potentially_slicing_shared_ptr<VirtBase>(obj));
|
||||
})
|
||||
.def("get_code", &WpOwner::get_code);
|
||||
}
|
||||
69
tests/test_potentially_slicing_shared_ptr.py
Normal file
69
tests/test_potentially_slicing_shared_ptr.py
Normal file
@@ -0,0 +1,69 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import gc
|
||||
import weakref
|
||||
|
||||
import pytest
|
||||
|
||||
import env
|
||||
import pybind11_tests.potentially_slicing_shared_ptr as m
|
||||
|
||||
|
||||
class PyDrvd(m.VirtBase):
|
||||
def get_code(self):
|
||||
return 200
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("vtype", "expected_code"), [(m.VirtBase, 100), (PyDrvd, 200)])
|
||||
@pytest.mark.parametrize(
|
||||
"rtrn_meth", ["rtrn_obj_cast_shared_ptr", "rtrn_potentially_slicing_shared_ptr"]
|
||||
)
|
||||
def test_rtrn_obj_cast_shared_ptr(vtype, rtrn_meth, expected_code):
|
||||
obj = vtype()
|
||||
ptr = getattr(m, rtrn_meth)(obj)
|
||||
assert ptr.get_code() == expected_code
|
||||
objref = weakref.ref(obj)
|
||||
del obj
|
||||
gc.collect()
|
||||
assert ptr.get_code() == expected_code # the ptr Python object keeps obj alive
|
||||
assert objref() is not None
|
||||
del ptr
|
||||
gc.collect()
|
||||
assert objref() is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("vtype", "expected_code"), [(m.VirtBase, 100), (PyDrvd, 200)])
|
||||
def test_with_sp_owner(vtype, expected_code):
|
||||
spo = m.SpOwner()
|
||||
assert spo.get_code() == -888
|
||||
|
||||
obj = vtype()
|
||||
assert obj.get_code() == expected_code
|
||||
|
||||
spo.set_sp(obj)
|
||||
assert spo.get_code() == expected_code
|
||||
|
||||
del obj
|
||||
gc.collect()
|
||||
assert spo.get_code() == expected_code
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("vtype", "expected_code"), [(m.VirtBase, 100), (PyDrvd, 200)])
|
||||
@pytest.mark.parametrize("set_meth", ["set_wp", "set_wp_potentially_slicing"])
|
||||
def test_with_wp_owner(vtype, set_meth, expected_code):
|
||||
wpo = m.WpOwner()
|
||||
assert wpo.get_code() == -999
|
||||
|
||||
obj = vtype()
|
||||
assert obj.get_code() == expected_code
|
||||
|
||||
getattr(wpo, set_meth)(obj)
|
||||
if vtype is m.VirtBase or set_meth == "set_wp_potentially_slicing":
|
||||
assert wpo.get_code() == expected_code
|
||||
else:
|
||||
assert wpo.get_code() == -999 # see PR #5624
|
||||
|
||||
del obj
|
||||
gc.collect()
|
||||
if not (env.PYPY or env.GRAALPY):
|
||||
assert wpo.get_code() == -999
|
||||
Reference in New Issue
Block a user