From 4f00ffdc69482daa6c0a253ad5ee820cccf0bfb7 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 10 Jun 2021 07:17:32 -0700 Subject: [PATCH] Copying in shared_from_this_custom_deleters.cpp from github.com/rwgk, with adjustments. Base version: https://github.com/rwgk/stuff/blob/e5318faa6a8583b7c338e01398a4eea69abda08d/shared_from_this_custom_deleters.cpp --- tests/test_class_sh_shared_from_this.cpp | 98 ++++++++++++++++++++++++ tests/test_class_sh_shared_from_this.py | 12 +++ 2 files changed, 110 insertions(+) diff --git a/tests/test_class_sh_shared_from_this.cpp b/tests/test_class_sh_shared_from_this.cpp index b81d1fae5..b210ba27e 100644 --- a/tests/test_class_sh_shared_from_this.cpp +++ b/tests/test_class_sh_shared_from_this.cpp @@ -1,10 +1,98 @@ #include "pybind11_tests.h" + #include "object.h" #include +#include +#include + +namespace shared_from_this_custom_deleters { + +template +struct labeled_delete { + std::string label; + explicit labeled_delete(const std::string &label) : label{label} {} + void operator()(T *raw_ptr) { + std::cout << "labeled_delete::operator() " << label << std::endl; + if (label != "SkipDelete") { + delete raw_ptr; + } + } +}; + +struct Atype : std::enable_shared_from_this {}; + +#define SHOW_USE_COUNTS \ + std::cout << "obj1, obj2 use_counts: " << obj1.use_count() << ", " << obj2.use_count() \ + << std::endl; + +void obj1_owns() { + std::cout << "\nobj1_owns()" << std::endl; + std::shared_ptr obj1(new Atype, labeled_delete("1st")); + std::shared_ptr obj2(obj1.get(), labeled_delete("SkipDelete")); + SHOW_USE_COUNTS + auto sft1 = obj1->shared_from_this(); + SHOW_USE_COUNTS + auto sft2 = obj2->shared_from_this(); + SHOW_USE_COUNTS +} + +void obj2_owns() { + std::cout << "\nobj2_owns()" << std::endl; + std::shared_ptr obj1(new Atype, labeled_delete("SkipDelete")); + std::shared_ptr obj2(obj1.get(), labeled_delete("2nd")); + SHOW_USE_COUNTS + auto sft1 = obj1->shared_from_this(); + SHOW_USE_COUNTS + auto sft2 = obj2->shared_from_this(); + SHOW_USE_COUNTS +} + +void obj_sft_reset() { + std::cout << "\nobj_sft_reset()" << std::endl; + std::shared_ptr obj1(new Atype, labeled_delete("SkipDelete")); + std::shared_ptr obj2(obj1.get(), labeled_delete("ThisDeletes")); + std::shared_ptr *obj_sft = nullptr; + std::shared_ptr *obj_ign = nullptr; + { + auto sft1 = obj1->shared_from_this(); + auto sft2 = obj2->shared_from_this(); + long uc1 = obj1.use_count(); + long uc2 = obj2.use_count(); + if (uc1 == 3 && uc2 == 1) { + std::cout << "SHARED_FROM_THIS_REFERENT: 1" << std::endl; + obj_sft = &obj1; + obj_ign = &obj2; + } else if (uc1 == 1 && uc2 == 3) { + std::cout << "SHARED_FROM_THIS_REFERENT: 2" << std::endl; + obj_sft = &obj2; + obj_ign = &obj1; + } else { + std::cout << "SHARED_FROM_THIS_REFERENT: UNKNOWN" << std::endl; + } + } + if (obj_sft == nullptr) + throw std::runtime_error("Unexpected `use_count`s."); + (*obj_sft).reset(); + bool got_bad_weak_ptr = false; + try { + (*obj_ign)->shared_from_this(); + } catch (const std::bad_weak_ptr &) { + got_bad_weak_ptr = true; + } + std::cout << "got_bad_weak_ptr: " << got_bad_weak_ptr << std::endl; + std::shared_ptr obj3(obj2.get(), labeled_delete("SkipDelete")); + // Working again based on the shared_ptr that was created after obj_sft was reset: + (*obj_ign)->shared_from_this(); +} + +} // namespace shared_from_this_custom_deleters + namespace test_class_sh_shared_from_this { +// clang-format off + class MyObject3 : public std::enable_shared_from_this { public: MyObject3(const MyObject3 &) = default; @@ -35,6 +123,8 @@ struct SharedFromThisVBase : std::enable_shared_from_this { struct SharedFromThisVirt : virtual SharedFromThisVBase {}; +// clang-format on + } // namespace test_class_sh_shared_from_this using namespace test_class_sh_shared_from_this; @@ -45,6 +135,8 @@ PYBIND11_SMART_HOLDER_TYPE_CASTERS(SharedFromThisRef) PYBIND11_SMART_HOLDER_TYPE_CASTERS(SharedFromThisVirt) TEST_SUBMODULE(class_sh_shared_from_this, m) { + // clang-format off + py::classh(m, "MyObject3") .def(py::init()); m.def("make_myobject3_1", []() { return new MyObject3(8); }); @@ -71,4 +163,10 @@ TEST_SUBMODULE(class_sh_shared_from_this, m) { static std::shared_ptr sft(new SharedFromThisVirt()); py::classh(m, "SharedFromThisVirt") .def_static("get", []() { return sft.get(); }, py::return_value_policy::reference); + + // clang-format on + + m.def("obj1_owns", shared_from_this_custom_deleters::obj1_owns); + m.def("obj2_owns", shared_from_this_custom_deleters::obj2_owns); + m.def("obj_sft_reset", shared_from_this_custom_deleters::obj_sft_reset); } diff --git a/tests/test_class_sh_shared_from_this.py b/tests/test_class_sh_shared_from_this.py index 5dd0234d4..26501bf68 100644 --- a/tests/test_class_sh_shared_from_this.py +++ b/tests/test_class_sh_shared_from_this.py @@ -109,3 +109,15 @@ def test_shared_from_this_virt(): z = m.SharedFromThisVirt.get() y = m.SharedFromThisVirt.get() assert y is z + + +@pytest.mark.parametrize( + "test_func", + [ + m.obj1_owns, + m.obj2_owns, + m.obj_sft_reset, + ], +) +def test_shared_from_this_custom_deleters(test_func): + test_func()