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_from_this
|
||||||
test_class_sh_trampoline_shared_ptr_cpp_arg
|
test_class_sh_trampoline_shared_ptr_cpp_arg
|
||||||
test_class_sh_trampoline_unique_ptr
|
test_class_sh_trampoline_unique_ptr
|
||||||
test_class_sh_trampoline_weak_ptr
|
|
||||||
test_class_sh_unique_ptr_custom_deleter
|
test_class_sh_unique_ptr_custom_deleter
|
||||||
test_class_sh_unique_ptr_member
|
test_class_sh_unique_ptr_member
|
||||||
test_class_sh_virtual_py_cpp_mix
|
test_class_sh_virtual_py_cpp_mix
|
||||||
test_class_sp_trampoline_weak_ptr
|
|
||||||
test_const_name
|
test_const_name
|
||||||
test_constants_and_functions
|
test_constants_and_functions
|
||||||
test_copy_move
|
test_copy_move
|
||||||
@@ -163,6 +161,7 @@ set(PYBIND11_TEST_FILES
|
|||||||
test_opaque_types
|
test_opaque_types
|
||||||
test_operator_overloading
|
test_operator_overloading
|
||||||
test_pickling
|
test_pickling
|
||||||
|
test_potentially_slicing_shared_ptr
|
||||||
test_python_multiple_inheritance
|
test_python_multiple_inheritance
|
||||||
test_pytypes
|
test_pytypes
|
||||||
test_sequences_and_iterators
|
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>
|
#include <memory>
|
||||||
|
|
||||||
namespace pybind11_tests {
|
namespace pybind11_tests {
|
||||||
namespace class_sp_trampoline_weak_ptr {
|
namespace potentially_slicing_shared_ptr {
|
||||||
|
|
||||||
struct VirtBase {
|
struct VirtBase {
|
||||||
virtual ~VirtBase() = default;
|
virtual ~VirtBase() = default;
|
||||||
@@ -17,12 +17,28 @@ struct VirtBase {
|
|||||||
struct PyVirtBase : VirtBase, py::trampoline_self_life_support {
|
struct PyVirtBase : VirtBase, py::trampoline_self_life_support {
|
||||||
using VirtBase::VirtBase;
|
using VirtBase::VirtBase;
|
||||||
int get_code() override { PYBIND11_OVERRIDE(int, VirtBase, get_code); }
|
int get_code() override { PYBIND11_OVERRIDE(int, VirtBase, get_code); }
|
||||||
|
};
|
||||||
|
|
||||||
~PyVirtBase() override {
|
std::shared_ptr<VirtBase> rtrn_obj_cast_shared_ptr(py::handle obj) {
|
||||||
fflush(stderr);
|
return obj.cast<std::shared_ptr<VirtBase>>();
|
||||||
printf("\nLOOOK ~PyVirtBase()\n");
|
}
|
||||||
fflush(stdout);
|
|
||||||
|
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 {
|
struct WpOwner {
|
||||||
@@ -40,43 +56,30 @@ private:
|
|||||||
std::weak_ptr<VirtBase> wp;
|
std::weak_ptr<VirtBase> wp;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SpOwner {
|
} // namespace potentially_slicing_shared_ptr
|
||||||
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 pybind11_tests
|
} // 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) {
|
TEST_SUBMODULE(potentially_slicing_shared_ptr, m) {
|
||||||
py::class_<VirtBase, std::shared_ptr<VirtBase>, PyVirtBase>(m, "VirtBase")
|
py::classh<VirtBase, PyVirtBase>(m, "VirtBase")
|
||||||
.def(py::init<>())
|
.def(py::init<>())
|
||||||
.def("get_code", &VirtBase::get_code);
|
.def("get_code", &VirtBase::get_code);
|
||||||
|
|
||||||
py::class_<WpOwner>(m, "WpOwner")
|
m.def("rtrn_obj_cast_shared_ptr", rtrn_obj_cast_shared_ptr);
|
||||||
.def(py::init<>())
|
m.def("rtrn_potentially_slicing_shared_ptr", rtrn_potentially_slicing_shared_ptr);
|
||||||
.def("set_wp", &WpOwner::set_wp)
|
|
||||||
.def("get_code", &WpOwner::get_code);
|
|
||||||
|
|
||||||
py::class_<SpOwner>(m, "SpOwner")
|
py::classh<SpOwner>(m, "SpOwner")
|
||||||
.def(py::init<>())
|
.def(py::init<>())
|
||||||
.def("set_sp", &SpOwner::set_sp)
|
.def("set_sp", &SpOwner::set_sp)
|
||||||
.def("get_code", &SpOwner::get_code);
|
.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