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 inconsistencies, 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// Simple custom holder that works like shared_ptr and has operator& overload 59// To obtain address of an instance of this holder pybind should use std::addressof 60// Attempt to get address via operator& may leads to segmentation fault 61template <typename T> 62class shared_ptr_with_addressof_operator { 63 std::shared_ptr<T> impl; 64public: 65 shared_ptr_with_addressof_operator( ) = default; 66 shared_ptr_with_addressof_operator(T* p) : impl(p) { } 67 T* get() const { return impl.get(); } 68 T** operator&() { throw std::logic_error("Call of overloaded operator& is not expected"); } 69}; 70PYBIND11_DECLARE_HOLDER_TYPE(T, shared_ptr_with_addressof_operator<T>); 71 72// Simple custom holder that works like unique_ptr and has operator& overload 73// To obtain address of an instance of this holder pybind should use std::addressof 74// Attempt to get address via operator& may leads to segmentation fault 75template <typename T> 76class unique_ptr_with_addressof_operator { 77 std::unique_ptr<T> impl; 78public: 79 unique_ptr_with_addressof_operator() = default; 80 unique_ptr_with_addressof_operator(T* p) : impl(p) { } 81 T* get() const { return impl.get(); } 82 T* release_ptr() { return impl.release(); } 83 T** operator&() { throw std::logic_error("Call of overloaded operator& is not expected"); } 84}; 85PYBIND11_DECLARE_HOLDER_TYPE(T, unique_ptr_with_addressof_operator<T>); 86 87 88TEST_SUBMODULE(smart_ptr, m) { 89 90 // test_smart_ptr 91 92 // Object implementation in `object.h` 93 py::class_<Object, ref<Object>> obj(m, "Object"); 94 obj.def("getRefCount", &Object::getRefCount); 95 96 // Custom object with builtin reference counting (see 'object.h' for the implementation) 97 class MyObject1 : public Object { 98 public: 99 MyObject1(int value) : value(value) { print_created(this, toString()); } 100 std::string toString() const { return "MyObject1[" + std::to_string(value) + "]"; } 101 protected: 102 virtual ~MyObject1() { print_destroyed(this); } 103 private: 104 int value; 105 }; 106 py::class_<MyObject1, ref<MyObject1>>(m, "MyObject1", obj) 107 .def(py::init<int>()); 108 py::implicitly_convertible<py::int_, MyObject1>(); 109 110 m.def("make_object_1", []() -> Object * { return new MyObject1(1); }); 111 m.def("make_object_2", []() -> ref<Object> { return new MyObject1(2); }); 112 m.def("make_myobject1_1", []() -> MyObject1 * { return new MyObject1(4); }); 113 m.def("make_myobject1_2", []() -> ref<MyObject1> { return new MyObject1(5); }); 114 m.def("print_object_1", [](const Object *obj) { py::print(obj->toString()); }); 115 m.def("print_object_2", [](ref<Object> obj) { py::print(obj->toString()); }); 116 m.def("print_object_3", [](const ref<Object> &obj) { py::print(obj->toString()); }); 117 m.def("print_object_4", [](const ref<Object> *obj) { py::print((*obj)->toString()); }); 118 m.def("print_myobject1_1", [](const MyObject1 *obj) { py::print(obj->toString()); }); 119 m.def("print_myobject1_2", [](ref<MyObject1> obj) { py::print(obj->toString()); }); 120 m.def("print_myobject1_3", [](const ref<MyObject1> &obj) { py::print(obj->toString()); }); 121 m.def("print_myobject1_4", [](const ref<MyObject1> *obj) { py::print((*obj)->toString()); }); 122 123 // Expose constructor stats for the ref type 124 m.def("cstats_ref", &ConstructorStats::get<ref_tag>); 125 126 127 // Object managed by a std::shared_ptr<> 128 class MyObject2 { 129 public: 130 MyObject2(const MyObject2 &) = default; 131 MyObject2(int value) : value(value) { print_created(this, toString()); } 132 std::string toString() const { return "MyObject2[" + std::to_string(value) + "]"; } 133 virtual ~MyObject2() { print_destroyed(this); } 134 private: 135 int value; 136 }; 137 py::class_<MyObject2, std::shared_ptr<MyObject2>>(m, "MyObject2") 138 .def(py::init<int>()); 139 m.def("make_myobject2_1", []() { return new MyObject2(6); }); 140 m.def("make_myobject2_2", []() { return std::make_shared<MyObject2>(7); }); 141 m.def("print_myobject2_1", [](const MyObject2 *obj) { py::print(obj->toString()); }); 142 m.def("print_myobject2_2", [](std::shared_ptr<MyObject2> obj) { py::print(obj->toString()); }); 143 m.def("print_myobject2_3", [](const std::shared_ptr<MyObject2> &obj) { py::print(obj->toString()); }); 144 m.def("print_myobject2_4", [](const std::shared_ptr<MyObject2> *obj) { py::print((*obj)->toString()); }); 145 146 // Object managed by a std::shared_ptr<>, additionally derives from std::enable_shared_from_this<> 147 class MyObject3 : public std::enable_shared_from_this<MyObject3> { 148 public: 149 MyObject3(const MyObject3 &) = default; 150 MyObject3(int value) : value(value) { print_created(this, toString()); } 151 std::string toString() const { return "MyObject3[" + std::to_string(value) + "]"; } 152 virtual ~MyObject3() { print_destroyed(this); } 153 private: 154 int value; 155 }; 156 py::class_<MyObject3, std::shared_ptr<MyObject3>>(m, "MyObject3") 157 .def(py::init<int>()); 158 m.def("make_myobject3_1", []() { return new MyObject3(8); }); 159 m.def("make_myobject3_2", []() { return std::make_shared<MyObject3>(9); }); 160 m.def("print_myobject3_1", [](const MyObject3 *obj) { py::print(obj->toString()); }); 161 m.def("print_myobject3_2", [](std::shared_ptr<MyObject3> obj) { py::print(obj->toString()); }); 162 m.def("print_myobject3_3", [](const std::shared_ptr<MyObject3> &obj) { py::print(obj->toString()); }); 163 m.def("print_myobject3_4", [](const std::shared_ptr<MyObject3> *obj) { py::print((*obj)->toString()); }); 164 165 // test_smart_ptr_refcounting 166 m.def("test_object1_refcounting", []() { 167 ref<MyObject1> o = new MyObject1(0); 168 bool good = o->getRefCount() == 1; 169 py::object o2 = py::cast(o, py::return_value_policy::reference); 170 // always request (partial) ownership for objects with intrusive 171 // reference counting even when using the 'reference' RVP 172 good &= o->getRefCount() == 2; 173 return good; 174 }); 175 176 // test_unique_nodelete 177 // Object with a private destructor 178 class MyObject4 { 179 public: 180 MyObject4(int value) : value{value} { print_created(this); } 181 int value; 182 private: 183 ~MyObject4() { print_destroyed(this); } 184 }; 185 py::class_<MyObject4, std::unique_ptr<MyObject4, py::nodelete>>(m, "MyObject4") 186 .def(py::init<int>()) 187 .def_readwrite("value", &MyObject4::value); 188 189 // test_unique_deleter 190 // Object with std::unique_ptr<T, D> where D is not matching the base class 191 // Object with a protected destructor 192 class MyObject4a { 193 public: 194 MyObject4a(int i) { 195 value = i; 196 print_created(this); 197 }; 198 int value; 199 protected: 200 virtual ~MyObject4a() { print_destroyed(this); } 201 }; 202 py::class_<MyObject4a, std::unique_ptr<MyObject4a, py::nodelete>>(m, "MyObject4a") 203 .def(py::init<int>()) 204 .def_readwrite("value", &MyObject4a::value); 205 206 // Object derived but with public destructor and no Deleter in default holder 207 class MyObject4b : public MyObject4a { 208 public: 209 MyObject4b(int i) : MyObject4a(i) { print_created(this); } 210 ~MyObject4b() { print_destroyed(this); } 211 }; 212 py::class_<MyObject4b, MyObject4a>(m, "MyObject4b") 213 .def(py::init<int>()); 214 215 // test_large_holder 216 class MyObject5 { // managed by huge_unique_ptr 217 public: 218 MyObject5(int value) : value{value} { print_created(this); } 219 ~MyObject5() { print_destroyed(this); } 220 int value; 221 }; 222 py::class_<MyObject5, huge_unique_ptr<MyObject5>>(m, "MyObject5") 223 .def(py::init<int>()) 224 .def_readwrite("value", &MyObject5::value); 225 226 // test_shared_ptr_and_references 227 struct SharedPtrRef { 228 struct A { 229 A() { print_created(this); } 230 A(const A &) { print_copy_created(this); } 231 A(A &&) { print_move_created(this); } 232 ~A() { print_destroyed(this); } 233 }; 234 235 A value = {}; 236 std::shared_ptr<A> shared = std::make_shared<A>(); 237 }; 238 using A = SharedPtrRef::A; 239 py::class_<A, std::shared_ptr<A>>(m, "A"); 240 py::class_<SharedPtrRef>(m, "SharedPtrRef") 241 .def(py::init<>()) 242 .def_readonly("ref", &SharedPtrRef::value) 243 .def_property_readonly("copy", [](const SharedPtrRef &s) { return s.value; }, 244 py::return_value_policy::copy) 245 .def_readonly("holder_ref", &SharedPtrRef::shared) 246 .def_property_readonly("holder_copy", [](const SharedPtrRef &s) { return s.shared; }, 247 py::return_value_policy::copy) 248 .def("set_ref", [](SharedPtrRef &, const A &) { return true; }) 249 .def("set_holder", [](SharedPtrRef &, std::shared_ptr<A>) { return true; }); 250 251 // test_shared_ptr_from_this_and_references 252 struct SharedFromThisRef { 253 struct B : std::enable_shared_from_this<B> { 254 B() { print_created(this); } 255 B(const B &) : std::enable_shared_from_this<B>() { print_copy_created(this); } 256 B(B &&) : std::enable_shared_from_this<B>() { print_move_created(this); } 257 ~B() { print_destroyed(this); } 258 }; 259 260 B value = {}; 261 std::shared_ptr<B> shared = std::make_shared<B>(); 262 }; 263 using B = SharedFromThisRef::B; 264 py::class_<B, std::shared_ptr<B>>(m, "B"); 265 py::class_<SharedFromThisRef>(m, "SharedFromThisRef") 266 .def(py::init<>()) 267 .def_readonly("bad_wp", &SharedFromThisRef::value) 268 .def_property_readonly("ref", [](const SharedFromThisRef &s) -> const B & { return *s.shared; }) 269 .def_property_readonly("copy", [](const SharedFromThisRef &s) { return s.value; }, 270 py::return_value_policy::copy) 271 .def_readonly("holder_ref", &SharedFromThisRef::shared) 272 .def_property_readonly("holder_copy", [](const SharedFromThisRef &s) { return s.shared; }, 273 py::return_value_policy::copy) 274 .def("set_ref", [](SharedFromThisRef &, const B &) { return true; }) 275 .def("set_holder", [](SharedFromThisRef &, std::shared_ptr<B>) { return true; }); 276 277 // Issue #865: shared_from_this doesn't work with virtual inheritance 278 struct SharedFromThisVBase : std::enable_shared_from_this<SharedFromThisVBase> { 279 SharedFromThisVBase() = default; 280 SharedFromThisVBase(const SharedFromThisVBase &) = default; 281 virtual ~SharedFromThisVBase() = default; 282 }; 283 struct SharedFromThisVirt : virtual SharedFromThisVBase {}; 284 static std::shared_ptr<SharedFromThisVirt> sft(new SharedFromThisVirt()); 285 py::class_<SharedFromThisVirt, std::shared_ptr<SharedFromThisVirt>>(m, "SharedFromThisVirt") 286 .def_static("get", []() { return sft.get(); }); 287 288 // test_move_only_holder 289 struct C { 290 C() { print_created(this); } 291 ~C() { print_destroyed(this); } 292 }; 293 py::class_<C, custom_unique_ptr<C>>(m, "TypeWithMoveOnlyHolder") 294 .def_static("make", []() { return custom_unique_ptr<C>(new C); }); 295 296 // test_holder_with_addressof_operator 297 struct TypeForHolderWithAddressOf { 298 TypeForHolderWithAddressOf() { print_created(this); } 299 TypeForHolderWithAddressOf(const TypeForHolderWithAddressOf &) { print_copy_created(this); } 300 TypeForHolderWithAddressOf(TypeForHolderWithAddressOf &&) { print_move_created(this); } 301 ~TypeForHolderWithAddressOf() { print_destroyed(this); } 302 std::string toString() const { 303 return "TypeForHolderWithAddressOf[" + std::to_string(value) + "]"; 304 } 305 int value = 42; 306 }; 307 using HolderWithAddressOf = shared_ptr_with_addressof_operator<TypeForHolderWithAddressOf>; 308 py::class_<TypeForHolderWithAddressOf, HolderWithAddressOf>(m, "TypeForHolderWithAddressOf") 309 .def_static("make", []() { return HolderWithAddressOf(new TypeForHolderWithAddressOf); }) 310 .def("get", [](const HolderWithAddressOf &self) { return self.get(); }) 311 .def("print_object_1", [](const TypeForHolderWithAddressOf *obj) { py::print(obj->toString()); }) 312 .def("print_object_2", [](HolderWithAddressOf obj) { py::print(obj.get()->toString()); }) 313 .def("print_object_3", [](const HolderWithAddressOf &obj) { py::print(obj.get()->toString()); }) 314 .def("print_object_4", [](const HolderWithAddressOf *obj) { py::print((*obj).get()->toString()); }); 315 316 // test_move_only_holder_with_addressof_operator 317 struct TypeForMoveOnlyHolderWithAddressOf { 318 TypeForMoveOnlyHolderWithAddressOf(int value) : value{value} { print_created(this); } 319 ~TypeForMoveOnlyHolderWithAddressOf() { print_destroyed(this); } 320 std::string toString() const { 321 return "MoveOnlyHolderWithAddressOf[" + std::to_string(value) + "]"; 322 } 323 int value; 324 }; 325 using MoveOnlyHolderWithAddressOf = unique_ptr_with_addressof_operator<TypeForMoveOnlyHolderWithAddressOf>; 326 py::class_<TypeForMoveOnlyHolderWithAddressOf, MoveOnlyHolderWithAddressOf>(m, "TypeForMoveOnlyHolderWithAddressOf") 327 .def_static("make", []() { return MoveOnlyHolderWithAddressOf(new TypeForMoveOnlyHolderWithAddressOf(0)); }) 328 .def_readwrite("value", &TypeForMoveOnlyHolderWithAddressOf::value) 329 .def("print_object", [](const TypeForMoveOnlyHolderWithAddressOf *obj) { py::print(obj->toString()); }); 330 331 // test_smart_ptr_from_default 332 struct HeldByDefaultHolder { }; 333 py::class_<HeldByDefaultHolder>(m, "HeldByDefaultHolder") 334 .def(py::init<>()) 335 .def_static("load_shared_ptr", [](std::shared_ptr<HeldByDefaultHolder>) {}); 336 337 // test_shared_ptr_gc 338 // #187: issue involving std::shared_ptr<> return value policy & garbage collection 339 struct ElementBase { 340 virtual ~ElementBase() { } /* Force creation of virtual table */ 341 }; 342 py::class_<ElementBase, std::shared_ptr<ElementBase>>(m, "ElementBase"); 343 344 struct ElementA : ElementBase { 345 ElementA(int v) : v(v) { } 346 int value() { return v; } 347 int v; 348 }; 349 py::class_<ElementA, ElementBase, std::shared_ptr<ElementA>>(m, "ElementA") 350 .def(py::init<int>()) 351 .def("value", &ElementA::value); 352 353 struct ElementList { 354 void add(std::shared_ptr<ElementBase> e) { l.push_back(e); } 355 std::vector<std::shared_ptr<ElementBase>> l; 356 }; 357 py::class_<ElementList, std::shared_ptr<ElementList>>(m, "ElementList") 358 .def(py::init<>()) 359 .def("add", &ElementList::add) 360 .def("get", [](ElementList &el) { 361 py::list list; 362 for (auto &e : el.l) 363 list.append(py::cast(e)); 364 return list; 365 }); 366} 367