test_smart_ptr.cpp revision 12391
1/* 2 tests/test_smart_ptr.cpp -- binding classes with custom reference counting, 3 implicit conversions between types 4 5 Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch> 6 7 All rights reserved. Use of this source code is governed by a 8 BSD-style license that can be found in the LICENSE file. 9*/ 10 11#if defined(_MSC_VER) && _MSC_VER < 1910 12# pragma warning(disable: 4702) // unreachable code in system header 13#endif 14 15#include "pybind11_tests.h" 16#include "object.h" 17 18// Make pybind aware of the ref-counted wrapper type (s): 19 20// ref<T> is a wrapper for 'Object' which uses intrusive reference counting 21// It is always possible to construct a ref<T> from an Object* pointer without 22// possible incosistencies, hence the 'true' argument at the end. 23PYBIND11_DECLARE_HOLDER_TYPE(T, ref<T>, true); 24// Make pybind11 aware of the non-standard getter member function 25namespace pybind11 { namespace detail { 26 template <typename T> 27 struct holder_helper<ref<T>> { 28 static const T *get(const ref<T> &p) { return p.get_ptr(); } 29 }; 30}} 31 32// The following is not required anymore for std::shared_ptr, but it should compile without error: 33PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>); 34 35// This is just a wrapper around unique_ptr, but with extra fields to deliberately bloat up the 36// holder size to trigger the non-simple-layout internal instance layout for single inheritance with 37// large holder type: 38template <typename T> class huge_unique_ptr { 39 std::unique_ptr<T> ptr; 40 uint64_t padding[10]; 41public: 42 huge_unique_ptr(T *p) : ptr(p) {}; 43 T *get() { return ptr.get(); } 44}; 45PYBIND11_DECLARE_HOLDER_TYPE(T, huge_unique_ptr<T>); 46 47// Simple custom holder that works like unique_ptr 48template <typename T> 49class custom_unique_ptr { 50 std::unique_ptr<T> impl; 51public: 52 custom_unique_ptr(T* p) : impl(p) { } 53 T* get() const { return impl.get(); } 54 T* release_ptr() { return impl.release(); } 55}; 56PYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr<T>); 57 58 59TEST_SUBMODULE(smart_ptr, m) { 60 61 // test_smart_ptr 62 63 // Object implementation in `object.h` 64 py::class_<Object, ref<Object>> obj(m, "Object"); 65 obj.def("getRefCount", &Object::getRefCount); 66 67 // Custom object with builtin reference counting (see 'object.h' for the implementation) 68 class MyObject1 : public Object { 69 public: 70 MyObject1(int value) : value(value) { print_created(this, toString()); } 71 std::string toString() const { return "MyObject1[" + std::to_string(value) + "]"; } 72 protected: 73 virtual ~MyObject1() { print_destroyed(this); } 74 private: 75 int value; 76 }; 77 py::class_<MyObject1, ref<MyObject1>>(m, "MyObject1", obj) 78 .def(py::init<int>()); 79 py::implicitly_convertible<py::int_, MyObject1>(); 80 81 m.def("make_object_1", []() -> Object * { return new MyObject1(1); }); 82 m.def("make_object_2", []() -> ref<Object> { return new MyObject1(2); }); 83 m.def("make_myobject1_1", []() -> MyObject1 * { return new MyObject1(4); }); 84 m.def("make_myobject1_2", []() -> ref<MyObject1> { return new MyObject1(5); }); 85 m.def("print_object_1", [](const Object *obj) { py::print(obj->toString()); }); 86 m.def("print_object_2", [](ref<Object> obj) { py::print(obj->toString()); }); 87 m.def("print_object_3", [](const ref<Object> &obj) { py::print(obj->toString()); }); 88 m.def("print_object_4", [](const ref<Object> *obj) { py::print((*obj)->toString()); }); 89 m.def("print_myobject1_1", [](const MyObject1 *obj) { py::print(obj->toString()); }); 90 m.def("print_myobject1_2", [](ref<MyObject1> obj) { py::print(obj->toString()); }); 91 m.def("print_myobject1_3", [](const ref<MyObject1> &obj) { py::print(obj->toString()); }); 92 m.def("print_myobject1_4", [](const ref<MyObject1> *obj) { py::print((*obj)->toString()); }); 93 94 // Expose constructor stats for the ref type 95 m.def("cstats_ref", &ConstructorStats::get<ref_tag>); 96 97 98 // Object managed by a std::shared_ptr<> 99 class MyObject2 { 100 public: 101 MyObject2(int value) : value(value) { print_created(this, toString()); } 102 std::string toString() const { return "MyObject2[" + std::to_string(value) + "]"; } 103 virtual ~MyObject2() { print_destroyed(this); } 104 private: 105 int value; 106 }; 107 py::class_<MyObject2, std::shared_ptr<MyObject2>>(m, "MyObject2") 108 .def(py::init<int>()); 109 m.def("make_myobject2_1", []() { return new MyObject2(6); }); 110 m.def("make_myobject2_2", []() { return std::make_shared<MyObject2>(7); }); 111 m.def("print_myobject2_1", [](const MyObject2 *obj) { py::print(obj->toString()); }); 112 m.def("print_myobject2_2", [](std::shared_ptr<MyObject2> obj) { py::print(obj->toString()); }); 113 m.def("print_myobject2_3", [](const std::shared_ptr<MyObject2> &obj) { py::print(obj->toString()); }); 114 m.def("print_myobject2_4", [](const std::shared_ptr<MyObject2> *obj) { py::print((*obj)->toString()); }); 115 116 // Object managed by a std::shared_ptr<>, additionally derives from std::enable_shared_from_this<> 117 class MyObject3 : public std::enable_shared_from_this<MyObject3> { 118 public: 119 MyObject3(int value) : value(value) { print_created(this, toString()); } 120 std::string toString() const { return "MyObject3[" + std::to_string(value) + "]"; } 121 virtual ~MyObject3() { print_destroyed(this); } 122 private: 123 int value; 124 }; 125 py::class_<MyObject3, std::shared_ptr<MyObject3>>(m, "MyObject3") 126 .def(py::init<int>()); 127 m.def("make_myobject3_1", []() { return new MyObject3(8); }); 128 m.def("make_myobject3_2", []() { return std::make_shared<MyObject3>(9); }); 129 m.def("print_myobject3_1", [](const MyObject3 *obj) { py::print(obj->toString()); }); 130 m.def("print_myobject3_2", [](std::shared_ptr<MyObject3> obj) { py::print(obj->toString()); }); 131 m.def("print_myobject3_3", [](const std::shared_ptr<MyObject3> &obj) { py::print(obj->toString()); }); 132 m.def("print_myobject3_4", [](const std::shared_ptr<MyObject3> *obj) { py::print((*obj)->toString()); }); 133 134 // test_smart_ptr_refcounting 135 m.def("test_object1_refcounting", []() { 136 ref<MyObject1> o = new MyObject1(0); 137 bool good = o->getRefCount() == 1; 138 py::object o2 = py::cast(o, py::return_value_policy::reference); 139 // always request (partial) ownership for objects with intrusive 140 // reference counting even when using the 'reference' RVP 141 good &= o->getRefCount() == 2; 142 return good; 143 }); 144 145 // test_unique_nodelete 146 // Object with a private destructor 147 class MyObject4 { 148 public: 149 MyObject4(int value) : value{value} { print_created(this); } 150 int value; 151 private: 152 ~MyObject4() { print_destroyed(this); } 153 }; 154 py::class_<MyObject4, std::unique_ptr<MyObject4, py::nodelete>>(m, "MyObject4") 155 .def(py::init<int>()) 156 .def_readwrite("value", &MyObject4::value); 157 158 // test_large_holder 159 class MyObject5 { // managed by huge_unique_ptr 160 public: 161 MyObject5(int value) : value{value} { print_created(this); } 162 ~MyObject5() { print_destroyed(this); } 163 int value; 164 }; 165 py::class_<MyObject5, huge_unique_ptr<MyObject5>>(m, "MyObject5") 166 .def(py::init<int>()) 167 .def_readwrite("value", &MyObject5::value); 168 169 // test_shared_ptr_and_references 170 struct SharedPtrRef { 171 struct A { 172 A() { print_created(this); } 173 A(const A &) { print_copy_created(this); } 174 A(A &&) { print_move_created(this); } 175 ~A() { print_destroyed(this); } 176 }; 177 178 A value = {}; 179 std::shared_ptr<A> shared = std::make_shared<A>(); 180 }; 181 using A = SharedPtrRef::A; 182 py::class_<A, std::shared_ptr<A>>(m, "A"); 183 py::class_<SharedPtrRef>(m, "SharedPtrRef") 184 .def(py::init<>()) 185 .def_readonly("ref", &SharedPtrRef::value) 186 .def_property_readonly("copy", [](const SharedPtrRef &s) { return s.value; }, 187 py::return_value_policy::copy) 188 .def_readonly("holder_ref", &SharedPtrRef::shared) 189 .def_property_readonly("holder_copy", [](const SharedPtrRef &s) { return s.shared; }, 190 py::return_value_policy::copy) 191 .def("set_ref", [](SharedPtrRef &, const A &) { return true; }) 192 .def("set_holder", [](SharedPtrRef &, std::shared_ptr<A>) { return true; }); 193 194 // test_shared_ptr_from_this_and_references 195 struct SharedFromThisRef { 196 struct B : std::enable_shared_from_this<B> { 197 B() { print_created(this); } 198 B(const B &) : std::enable_shared_from_this<B>() { print_copy_created(this); } 199 B(B &&) : std::enable_shared_from_this<B>() { print_move_created(this); } 200 ~B() { print_destroyed(this); } 201 }; 202 203 B value = {}; 204 std::shared_ptr<B> shared = std::make_shared<B>(); 205 }; 206 using B = SharedFromThisRef::B; 207 py::class_<B, std::shared_ptr<B>>(m, "B"); 208 py::class_<SharedFromThisRef>(m, "SharedFromThisRef") 209 .def(py::init<>()) 210 .def_readonly("bad_wp", &SharedFromThisRef::value) 211 .def_property_readonly("ref", [](const SharedFromThisRef &s) -> const B & { return *s.shared; }) 212 .def_property_readonly("copy", [](const SharedFromThisRef &s) { return s.value; }, 213 py::return_value_policy::copy) 214 .def_readonly("holder_ref", &SharedFromThisRef::shared) 215 .def_property_readonly("holder_copy", [](const SharedFromThisRef &s) { return s.shared; }, 216 py::return_value_policy::copy) 217 .def("set_ref", [](SharedFromThisRef &, const B &) { return true; }) 218 .def("set_holder", [](SharedFromThisRef &, std::shared_ptr<B>) { return true; }); 219 220 // Issue #865: shared_from_this doesn't work with virtual inheritance 221 struct SharedFromThisVBase : std::enable_shared_from_this<SharedFromThisVBase> { 222 virtual ~SharedFromThisVBase() = default; 223 }; 224 struct SharedFromThisVirt : virtual SharedFromThisVBase {}; 225 static std::shared_ptr<SharedFromThisVirt> sft(new SharedFromThisVirt()); 226 py::class_<SharedFromThisVirt, std::shared_ptr<SharedFromThisVirt>>(m, "SharedFromThisVirt") 227 .def_static("get", []() { return sft.get(); }); 228 229 // test_move_only_holder 230 struct C { 231 C() { print_created(this); } 232 ~C() { print_destroyed(this); } 233 }; 234 py::class_<C, custom_unique_ptr<C>>(m, "TypeWithMoveOnlyHolder") 235 .def_static("make", []() { return custom_unique_ptr<C>(new C); }); 236 237 // test_smart_ptr_from_default 238 struct HeldByDefaultHolder { }; 239 py::class_<HeldByDefaultHolder>(m, "HeldByDefaultHolder") 240 .def(py::init<>()) 241 .def_static("load_shared_ptr", [](std::shared_ptr<HeldByDefaultHolder>) {}); 242 243 // test_shared_ptr_gc 244 // #187: issue involving std::shared_ptr<> return value policy & garbage collection 245 struct ElementBase { virtual void foo() { } /* Force creation of virtual table */ }; 246 py::class_<ElementBase, std::shared_ptr<ElementBase>>(m, "ElementBase"); 247 248 struct ElementA : ElementBase { 249 ElementA(int v) : v(v) { } 250 int value() { return v; } 251 int v; 252 }; 253 py::class_<ElementA, ElementBase, std::shared_ptr<ElementA>>(m, "ElementA") 254 .def(py::init<int>()) 255 .def("value", &ElementA::value); 256 257 struct ElementList { 258 void add(std::shared_ptr<ElementBase> e) { l.push_back(e); } 259 std::vector<std::shared_ptr<ElementBase>> l; 260 }; 261 py::class_<ElementList, std::shared_ptr<ElementList>>(m, "ElementList") 262 .def(py::init<>()) 263 .def("add", &ElementList::add) 264 .def("get", [](ElementList &el) { 265 py::list list; 266 for (auto &e : el.l) 267 list.append(py::cast(e)); 268 return list; 269 }); 270} 271