diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 563bc8daf..ac7212f3c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -106,7 +106,6 @@ set(PYBIND11_TEST_FILES test_class_sh_disowning_mi.cpp test_class_sh_factory_constructors.cpp test_class_sh_inheritance.cpp - test_class_sh_shared_from_this.cpp test_class_sh_trampoline_basic.cpp test_class_sh_trampoline_self_life_support.cpp test_class_sh_trampoline_shared_from_this.cpp diff --git a/tests/test_class_sh_shared_from_this.cpp b/tests/test_class_sh_shared_from_this.cpp deleted file mode 100644 index 490e39fe7..000000000 --- a/tests/test_class_sh_shared_from_this.cpp +++ /dev/null @@ -1,239 +0,0 @@ -#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(); -#if defined(_MSC_VER) && _MSC_VER < 1912 - std::cout << "Preempting \"Windows fatal exception: access violation\": " - "(*obj_ign)->shared_from_this()" - << std::endl; -#else - bool got_bad_weak_ptr = false; - try { - static_cast((*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: - static_cast((*obj_ign)->shared_from_this()); -#endif -} - -} // namespace shared_from_this_custom_deleters - -namespace shared_ptr_reset_and_rescue_pointee_model { - -struct ToBeWrapped : std::enable_shared_from_this {}; - -struct RescuingDeleter; - -struct PyWrapper { - std::unique_ptr rdel; - std::shared_ptr wobj; - std::shared_ptr self; -}; - -struct RescuingDeleter { - PyWrapper *pyw; - explicit RescuingDeleter(PyWrapper *pyw) : pyw{pyw} {} - void operator()(ToBeWrapped *raw_ptr) { - if (pyw->self != nullptr) { -#if defined(__cpp_lib_enable_shared_from_this) && (!defined(_MSC_VER) || _MSC_VER >= 1912) - assert(raw_ptr->weak_from_this().expired()); // CRITICAL -#endif - pyw->wobj = std::shared_ptr(raw_ptr, *this); - pyw->self.reset(); - } else { - delete raw_ptr; - } - } -}; - -std::shared_ptr release_to_cpp(const std::shared_ptr &pyw) { - std::shared_ptr return_value = pyw->wobj; - pyw->wobj.reset(); - pyw->self = pyw; - return return_value; -} - -void proof_of_concept() { - std::shared_ptr pyw(new PyWrapper); - pyw->rdel = std::unique_ptr(new RescuingDeleter(pyw.get())); - pyw->wobj = std::shared_ptr(new ToBeWrapped, *pyw->rdel); - std::shared_ptr cpp_owner = release_to_cpp(pyw); - assert(pyw->wobj.get() == nullptr); - assert(cpp_owner.use_count() == 1); - { - std::shared_ptr sft = cpp_owner->shared_from_this(); - assert(cpp_owner.use_count() == 2); - } - assert(cpp_owner.use_count() == 1); - cpp_owner.reset(); - assert(pyw->wobj.get() != nullptr); - assert(pyw->wobj.use_count() == 1); - { - std::shared_ptr sft = pyw->wobj->shared_from_this(); - assert(pyw->wobj.use_count() == 2); - } -} - -} // namespace shared_ptr_reset_and_rescue_pointee_model - -namespace test_class_sh_shared_from_this { - -// clang-format off - -class MyObject3 : public std::enable_shared_from_this { -public: - MyObject3(const MyObject3 &) = default; - MyObject3(int value) : value(value) { print_created(this, toString()); } - std::string toString() const { return "MyObject3[" + std::to_string(value) + "]"; } - virtual ~MyObject3() { print_destroyed(this); } -private: - int value; -}; - -struct SharedFromThisRef { - struct B : std::enable_shared_from_this { - B() { print_created(this); } - B(const B &) : std::enable_shared_from_this() { print_copy_created(this); } - B(B &&) noexcept : std::enable_shared_from_this() { print_move_created(this); } - ~B() { print_destroyed(this); } - }; - - B value = {}; - std::shared_ptr shared = std::make_shared(); -}; - -struct SharedFromThisVBase : std::enable_shared_from_this { - SharedFromThisVBase() = default; - SharedFromThisVBase(const SharedFromThisVBase &) = default; - virtual ~SharedFromThisVBase() = default; -}; - -struct SharedFromThisVirt : virtual SharedFromThisVBase {}; - -// clang-format on - -} // namespace test_class_sh_shared_from_this - -using namespace test_class_sh_shared_from_this; - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(MyObject3) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(SharedFromThisRef::B) -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); }); - m.def("make_myobject3_2", []() { return std::make_shared(9); }); - m.def("print_myobject3_1", [](const MyObject3 *obj) { py::print(obj->toString()); }); - m.def("print_myobject3_2", [](std::shared_ptr obj) { py::print(obj->toString()); }); // NOLINT - m.def("print_myobject3_3", [](const std::shared_ptr &obj) { py::print(obj->toString()); }); - // m.def("print_myobject3_4", [](const std::shared_ptr *obj) { py::print((*obj)->toString()); }); - - using B = SharedFromThisRef::B; - py::classh(m, "B"); - py::classh(m, "SharedFromThisRef") - .def(py::init<>()) - .def_readonly("bad_wp", &SharedFromThisRef::value) - .def_property_readonly("ref", [](const SharedFromThisRef &s) -> const B & { return *s.shared; }) - .def_property_readonly("copy", [](const SharedFromThisRef &s) { return s.value; }, - py::return_value_policy::automatic) // XXX XXX XXX copy) - .def_readonly("holder_ref", &SharedFromThisRef::shared) - .def_property_readonly("holder_copy", [](const SharedFromThisRef &s) { return s.shared; }, - py::return_value_policy::automatic) // XXX XXX XXX copy) - .def("set_ref", [](SharedFromThisRef &, const B &) { return true; }) - .def("set_holder", [](SharedFromThisRef &, std::shared_ptr) { return true; }); // NOLINT - - 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); - - m.def("shared_ptr_reset_and_rescue_pointee_model_proof_of_concept", - shared_ptr_reset_and_rescue_pointee_model::proof_of_concept); -} diff --git a/tests/test_class_sh_shared_from_this.py b/tests/test_class_sh_shared_from_this.py deleted file mode 100644 index 6319029f7..000000000 --- a/tests/test_class_sh_shared_from_this.py +++ /dev/null @@ -1,132 +0,0 @@ -# -*- coding: utf-8 -*- -import pytest - -from pybind11_tests import class_sh_shared_from_this as m -from pybind11_tests import ConstructorStats - - -def test_smart_ptr(capture): - # Object3 - for i, o in zip( - [9, 8, 9], [m.MyObject3(9), m.make_myobject3_1(), m.make_myobject3_2()] - ): - print(o) - with capture: - m.print_myobject3_1(o) - m.print_myobject3_2(o) - m.print_myobject3_3(o) - m.print_myobject3_3(o) # XXX XXX XXX print_myobject3_4 - assert capture == "MyObject3[{i}]\n".format(i=i) * 4 - - cstats = ConstructorStats.get(m.MyObject3) - assert cstats.alive() == 1 - o = None - assert cstats.alive() == 0 - assert cstats.values() == ["MyObject3[9]", "MyObject3[8]", "MyObject3[9]"] - assert cstats.default_constructions == 0 - assert cstats.copy_constructions == 0 - # assert cstats.move_constructions >= 0 # Doesn't invoke any - assert cstats.copy_assignments == 0 - assert cstats.move_assignments == 0 - - -def test_shared_from_this_ref(): - s = m.SharedFromThisRef() - stats = ConstructorStats.get(m.B) - assert stats.alive() == 2 - - ref = s.ref # init_holder_helper(holder_ptr=false, owned=false, bad_wp=false) - assert stats.alive() == 2 - assert s.set_ref(ref) - assert s.set_holder( - ref - ) # std::enable_shared_from_this can create a holder from a reference - del ref, s - assert stats.alive() == 0 - - -def test_shared_from_this_bad_wp(): - s = m.SharedFromThisRef() - stats = ConstructorStats.get(m.B) - assert stats.alive() == 2 - - bad_wp = s.bad_wp # init_holder_helper(holder_ptr=false, owned=false, bad_wp=true) - assert stats.alive() == 2 - assert s.set_ref(bad_wp) - with pytest.raises(RuntimeError) as excinfo: - assert s.set_holder(bad_wp) - assert str(excinfo.value) == "Non-owning holder (loaded_as_shared_ptr)." - del bad_wp, s - assert stats.alive() == 0 - - -def test_shared_from_this_copy(): - s = m.SharedFromThisRef() - stats = ConstructorStats.get(m.B) - assert stats.alive() == 2 - - copy = s.copy # init_holder_helper(holder_ptr=false, owned=true, bad_wp=false) - # RuntimeError: Invalid return_value_policy for shared_ptr. - assert stats.alive() == 3 - assert s.set_ref(copy) - assert s.set_holder(copy) - del copy, s - assert stats.alive() == 0 - - -def test_shared_from_this_holder_ref(): - s = m.SharedFromThisRef() - stats = ConstructorStats.get(m.B) - assert stats.alive() == 2 - - holder_ref = ( - s.holder_ref - ) # init_holder_helper(holder_ptr=true, owned=false, bad_wp=false) - assert stats.alive() == 2 - assert s.set_ref(holder_ref) - assert s.set_holder(holder_ref) - del holder_ref, s - assert stats.alive() == 0 - - -def test_shared_from_this_holder_copy(): - s = m.SharedFromThisRef() - stats = ConstructorStats.get(m.B) - assert stats.alive() == 2 - - holder_copy = ( - # RuntimeError: Invalid return_value_policy for shared_ptr. - s.holder_copy - ) # init_holder_helper(holder_ptr=true, owned=true, bad_wp=false) - assert stats.alive() == 2 - assert s.set_ref(holder_copy) - assert s.set_holder(holder_copy) - del holder_copy, s - assert stats.alive() == 0 - - -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() - - -def test_shared_ptr_reset_and_rescue_pointee_model(loop_count_max=10 ** 6): - loop_count = 0 - while True: - m.shared_ptr_reset_and_rescue_pointee_model_proof_of_concept() - loop_count += 1 - if loop_count == loop_count_max: - break