From 553054b26bdd9cb1eedb99aedb78d2da75a46841 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 20 Jun 2021 08:35:51 -0700 Subject: [PATCH] Adding tests involving stashing `shared_ptr`s. WIP. --- ...t_class_sh_trampoline_shared_from_this.cpp | 78 ++++++++++++++++--- ...st_class_sh_trampoline_shared_from_this.py | 73 +++++++++++++++-- 2 files changed, 132 insertions(+), 19 deletions(-) diff --git a/tests/test_class_sh_trampoline_shared_from_this.cpp b/tests/test_class_sh_trampoline_shared_from_this.cpp index dac776515..fd3df5f97 100644 --- a/tests/test_class_sh_trampoline_shared_from_this.cpp +++ b/tests/test_class_sh_trampoline_shared_from_this.cpp @@ -10,27 +10,81 @@ namespace { -struct WithSft : std::enable_shared_from_this { - virtual ~WithSft() = default; +struct Sft : std::enable_shared_from_this { + std::string history; + explicit Sft(const std::string &history) : history{history} {} + long use_count() const { return this->shared_from_this().use_count(); } + virtual ~Sft() = default; + + // "Group of 4" begin. + // This group is not meant to be used, but will leave a trace in the + // history in case something goes wrong. + Sft(const Sft &other) { history = other.history + "_CpCtor"; } + + Sft(Sft &&other) { history = other.history + "_MvCtor"; } + + Sft &operator=(const Sft &other) { + history = other.history + "_OpEqLv"; + return *this; + } + + Sft &operator=(Sft &&other) { + history = other.history + "_OpEqRv"; + return *this; + } + // "Group of 4" end. }; -struct WithSftTrampoline : WithSft { - using WithSft::WithSft; +struct SftSharedPtrStash { + int ser_no; + std::vector> stash; + explicit SftSharedPtrStash(int ser_no) : ser_no{ser_no} {} + void Add(const std::shared_ptr &obj) { + obj->history += "_Stash" + std::to_string(ser_no) + "Add"; + stash.push_back(obj); + } + void AddSharedFromThis(Sft *obj) { + auto sft = obj->shared_from_this(); + sft->history += "_Stash" + std::to_string(ser_no) + "AddSharedFromThis"; + stash.push_back(sft); + } + std::string history(unsigned i) { + if (i < stash.size()) + return stash[i]->history; + return "OutOfRange"; + } + long use_count(unsigned i) { + if (i < stash.size()) + return stash[i].use_count(); + return -1; + } }; -void pass_shared_ptr(const std::shared_ptr &obj) { - to_cout("LOOOK pass_shared_ptr entry"); - to_cout("LOOOK obj->shared_from_this();"); - (void) obj->shared_from_this(); - to_cout("LOOOK pass_shared_ptr return"); +struct SftTrampoline : Sft { + using Sft::Sft; +}; + +void pass_shared_ptr(const std::shared_ptr &obj) { + obj->shared_from_this()->history += "_PassSharedPtr"; } } // namespace -PYBIND11_SMART_HOLDER_TYPE_CASTERS(WithSft) +PYBIND11_SMART_HOLDER_TYPE_CASTERS(Sft) +PYBIND11_SMART_HOLDER_TYPE_CASTERS(SftSharedPtrStash) TEST_SUBMODULE(class_sh_trampoline_shared_from_this, m) { - py::classh(m, "WithSft").def(py::init<>()); + py::classh(m, "Sft") + .def(py::init()) + .def_readonly("history", &Sft::history) + .def("use_count", &Sft::use_count); + + py::classh(m, "SftSharedPtrStash") + .def(py::init()) + .def("Add", &SftSharedPtrStash::Add) + .def("AddSharedFromThis", &SftSharedPtrStash::AddSharedFromThis) + .def("history", &SftSharedPtrStash::history) + .def("use_count", &SftSharedPtrStash::use_count); + m.def("pass_shared_ptr", pass_shared_ptr); - m.def("to_cout", to_cout); } diff --git a/tests/test_class_sh_trampoline_shared_from_this.py b/tests/test_class_sh_trampoline_shared_from_this.py index f7840a382..2187ddd57 100644 --- a/tests/test_class_sh_trampoline_shared_from_this.py +++ b/tests/test_class_sh_trampoline_shared_from_this.py @@ -2,18 +2,77 @@ import pybind11_tests.class_sh_trampoline_shared_from_this as m +import gc +import weakref -class PyWithSft(m.WithSft): + +class PySft(m.Sft): pass def test_pass_shared_ptr(): - m.to_cout("") - m.to_cout(">>> obj = PyWithSft()") - obj = PyWithSft() - m.to_cout(">>> m.pass_shared_ptr(obj) #1") + obj = PySft("PySft") + assert obj.history == "PySft" + assert obj.use_count() == 2 m.pass_shared_ptr(obj) - m.to_cout(">>> m.pass_shared_ptr(obj) #2") + assert obj.history == "PySft_PassSharedPtr" + assert obj.use_count() == 2 m.pass_shared_ptr(obj) - m.to_cout(">>> del obj") + assert obj.history == "PySft_PassSharedPtr_PassSharedPtr" + assert obj.use_count() == 2 + + +def test_pass_shared_ptr_while_stashed(): + obj = PySft("PySft") + obj_wr = weakref.ref(obj) + stash1 = m.SftSharedPtrStash(1) + stash1.Add(obj) + assert obj.history == "PySft_Stash1Add" + assert obj.use_count() == 2 + m.pass_shared_ptr(obj) + assert obj.history == "PySft_Stash1Add_PassSharedPtr" + assert obj.use_count() == 2 + stash2 = m.SftSharedPtrStash(2) + stash2.Add(obj) + assert obj.history == "PySft_Stash1Add_PassSharedPtr_Stash2Add" + assert obj.use_count() == 2 + assert stash2.history(0) == "PySft_Stash1Add_PassSharedPtr_Stash2Add" + assert stash2.use_count(0) == 1 # TODO: this is not great. + stash2.Add(obj) + assert obj.history == "PySft_Stash1Add_PassSharedPtr_Stash2Add_Stash2Add" + assert obj.use_count() == 2 + assert stash1.use_count(0) == 1 + assert stash1.history(0) == "PySft_Stash1Add_PassSharedPtr_Stash2Add_Stash2Add" + assert stash2.use_count(0) == 1 + assert stash2.history(0) == "PySft_Stash1Add_PassSharedPtr_Stash2Add_Stash2Add" + assert stash2.use_count(1) == 1 + assert stash2.history(1) == "PySft_Stash1Add_PassSharedPtr_Stash2Add_Stash2Add" del obj + assert stash2.use_count(0) == 1 + assert stash2.history(0) == "PySft_Stash1Add_PassSharedPtr_Stash2Add_Stash2Add" + assert stash2.use_count(1) == 1 + assert stash2.history(1) == "PySft_Stash1Add_PassSharedPtr_Stash2Add_Stash2Add" + del stash2 + gc.collect() + assert obj_wr() is not None + assert stash1.history(0) == "PySft_Stash1Add_PassSharedPtr_Stash2Add_Stash2Add" + del stash1 + gc.collect() + assert obj_wr() is None + + +def test_pass_shared_ptr_while_stashed_with_shared_from_this(): + obj = PySft("PySft") + obj_wr = weakref.ref(obj) + stash1 = m.SftSharedPtrStash(1) + stash1.AddSharedFromThis(obj) + assert obj.history == "PySft_Stash1AddSharedFromThis" + assert stash1.use_count(0) == 2 + stash1.AddSharedFromThis(obj) + assert obj.history == "PySft_Stash1AddSharedFromThis_Stash1AddSharedFromThis" + assert stash1.use_count(0) == 3 + assert stash1.use_count(1) == 3 + del obj + del stash1 + gc.collect() + assert obj_wr() is None