test_smart_ptr.cpp revision 12391
111986Sandreas.sandberg@arm.com/*
211986Sandreas.sandberg@arm.com    tests/test_smart_ptr.cpp -- binding classes with custom reference counting,
311986Sandreas.sandberg@arm.com    implicit conversions between types
411986Sandreas.sandberg@arm.com
511986Sandreas.sandberg@arm.com    Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
611986Sandreas.sandberg@arm.com
711986Sandreas.sandberg@arm.com    All rights reserved. Use of this source code is governed by a
811986Sandreas.sandberg@arm.com    BSD-style license that can be found in the LICENSE file.
911986Sandreas.sandberg@arm.com*/
1011986Sandreas.sandberg@arm.com
1112391Sjason@lowepower.com#if defined(_MSC_VER) && _MSC_VER < 1910
1212391Sjason@lowepower.com#  pragma warning(disable: 4702) // unreachable code in system header
1312391Sjason@lowepower.com#endif
1412391Sjason@lowepower.com
1511986Sandreas.sandberg@arm.com#include "pybind11_tests.h"
1611986Sandreas.sandberg@arm.com#include "object.h"
1711986Sandreas.sandberg@arm.com
1812391Sjason@lowepower.com// Make pybind aware of the ref-counted wrapper type (s):
1912037Sandreas.sandberg@arm.com
2012037Sandreas.sandberg@arm.com// ref<T> is a wrapper for 'Object' which uses intrusive reference counting
2112037Sandreas.sandberg@arm.com// It is always possible to construct a ref<T> from an Object* pointer without
2212037Sandreas.sandberg@arm.com// possible incosistencies, hence the 'true' argument at the end.
2312037Sandreas.sandberg@arm.comPYBIND11_DECLARE_HOLDER_TYPE(T, ref<T>, true);
2412037Sandreas.sandberg@arm.com// Make pybind11 aware of the non-standard getter member function
2512037Sandreas.sandberg@arm.comnamespace pybind11 { namespace detail {
2612037Sandreas.sandberg@arm.com    template <typename T>
2712037Sandreas.sandberg@arm.com    struct holder_helper<ref<T>> {
2812037Sandreas.sandberg@arm.com        static const T *get(const ref<T> &p) { return p.get_ptr(); }
2912037Sandreas.sandberg@arm.com    };
3012037Sandreas.sandberg@arm.com}}
3112037Sandreas.sandberg@arm.com
3212391Sjason@lowepower.com// The following is not required anymore for std::shared_ptr, but it should compile without error:
3312391Sjason@lowepower.comPYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>);
3411986Sandreas.sandberg@arm.com
3512391Sjason@lowepower.com// This is just a wrapper around unique_ptr, but with extra fields to deliberately bloat up the
3612391Sjason@lowepower.com// holder size to trigger the non-simple-layout internal instance layout for single inheritance with
3712391Sjason@lowepower.com// large holder type:
3812391Sjason@lowepower.comtemplate <typename T> class huge_unique_ptr {
3912391Sjason@lowepower.com    std::unique_ptr<T> ptr;
4012391Sjason@lowepower.com    uint64_t padding[10];
4112391Sjason@lowepower.compublic:
4212391Sjason@lowepower.com    huge_unique_ptr(T *p) : ptr(p) {};
4312391Sjason@lowepower.com    T *get() { return ptr.get(); }
4412391Sjason@lowepower.com};
4512391Sjason@lowepower.comPYBIND11_DECLARE_HOLDER_TYPE(T, huge_unique_ptr<T>);
4611986Sandreas.sandberg@arm.com
4712391Sjason@lowepower.com// Simple custom holder that works like unique_ptr
4812391Sjason@lowepower.comtemplate <typename T>
4912391Sjason@lowepower.comclass custom_unique_ptr {
5012391Sjason@lowepower.com    std::unique_ptr<T> impl;
5112391Sjason@lowepower.compublic:
5212391Sjason@lowepower.com    custom_unique_ptr(T* p) : impl(p) { }
5312391Sjason@lowepower.com    T* get() const { return impl.get(); }
5412391Sjason@lowepower.com    T* release_ptr() { return impl.release(); }
5512391Sjason@lowepower.com};
5612391Sjason@lowepower.comPYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr<T>);
5711986Sandreas.sandberg@arm.com
5811986Sandreas.sandberg@arm.com
5912391Sjason@lowepower.comTEST_SUBMODULE(smart_ptr, m) {
6011986Sandreas.sandberg@arm.com
6112391Sjason@lowepower.com    // test_smart_ptr
6211986Sandreas.sandberg@arm.com
6312391Sjason@lowepower.com    // Object implementation in `object.h`
6411986Sandreas.sandberg@arm.com    py::class_<Object, ref<Object>> obj(m, "Object");
6511986Sandreas.sandberg@arm.com    obj.def("getRefCount", &Object::getRefCount);
6611986Sandreas.sandberg@arm.com
6712391Sjason@lowepower.com    // Custom object with builtin reference counting (see 'object.h' for the implementation)
6812391Sjason@lowepower.com    class MyObject1 : public Object {
6912391Sjason@lowepower.com    public:
7012391Sjason@lowepower.com        MyObject1(int value) : value(value) { print_created(this, toString()); }
7112391Sjason@lowepower.com        std::string toString() const { return "MyObject1[" + std::to_string(value) + "]"; }
7212391Sjason@lowepower.com    protected:
7312391Sjason@lowepower.com        virtual ~MyObject1() { print_destroyed(this); }
7412391Sjason@lowepower.com    private:
7512391Sjason@lowepower.com        int value;
7612391Sjason@lowepower.com    };
7711986Sandreas.sandberg@arm.com    py::class_<MyObject1, ref<MyObject1>>(m, "MyObject1", obj)
7811986Sandreas.sandberg@arm.com        .def(py::init<int>());
7912391Sjason@lowepower.com    py::implicitly_convertible<py::int_, MyObject1>();
8011986Sandreas.sandberg@arm.com
8112391Sjason@lowepower.com    m.def("make_object_1", []() -> Object * { return new MyObject1(1); });
8212391Sjason@lowepower.com    m.def("make_object_2", []() -> ref<Object> { return new MyObject1(2); });
8312391Sjason@lowepower.com    m.def("make_myobject1_1", []() -> MyObject1 * { return new MyObject1(4); });
8412391Sjason@lowepower.com    m.def("make_myobject1_2", []() -> ref<MyObject1> { return new MyObject1(5); });
8512391Sjason@lowepower.com    m.def("print_object_1", [](const Object *obj) { py::print(obj->toString()); });
8612391Sjason@lowepower.com    m.def("print_object_2", [](ref<Object> obj) { py::print(obj->toString()); });
8712391Sjason@lowepower.com    m.def("print_object_3", [](const ref<Object> &obj) { py::print(obj->toString()); });
8812391Sjason@lowepower.com    m.def("print_object_4", [](const ref<Object> *obj) { py::print((*obj)->toString()); });
8912391Sjason@lowepower.com    m.def("print_myobject1_1", [](const MyObject1 *obj) { py::print(obj->toString()); });
9012391Sjason@lowepower.com    m.def("print_myobject1_2", [](ref<MyObject1> obj) { py::print(obj->toString()); });
9112391Sjason@lowepower.com    m.def("print_myobject1_3", [](const ref<MyObject1> &obj) { py::print(obj->toString()); });
9212391Sjason@lowepower.com    m.def("print_myobject1_4", [](const ref<MyObject1> *obj) { py::print((*obj)->toString()); });
9312037Sandreas.sandberg@arm.com
9412391Sjason@lowepower.com    // Expose constructor stats for the ref type
9512391Sjason@lowepower.com    m.def("cstats_ref", &ConstructorStats::get<ref_tag>);
9611986Sandreas.sandberg@arm.com
9712391Sjason@lowepower.com
9812391Sjason@lowepower.com    // Object managed by a std::shared_ptr<>
9912391Sjason@lowepower.com    class MyObject2 {
10012391Sjason@lowepower.com    public:
10112391Sjason@lowepower.com        MyObject2(int value) : value(value) { print_created(this, toString()); }
10212391Sjason@lowepower.com        std::string toString() const { return "MyObject2[" + std::to_string(value) + "]"; }
10312391Sjason@lowepower.com        virtual ~MyObject2() { print_destroyed(this); }
10412391Sjason@lowepower.com    private:
10512391Sjason@lowepower.com        int value;
10612391Sjason@lowepower.com    };
10711986Sandreas.sandberg@arm.com    py::class_<MyObject2, std::shared_ptr<MyObject2>>(m, "MyObject2")
10811986Sandreas.sandberg@arm.com        .def(py::init<int>());
10912391Sjason@lowepower.com    m.def("make_myobject2_1", []() { return new MyObject2(6); });
11012391Sjason@lowepower.com    m.def("make_myobject2_2", []() { return std::make_shared<MyObject2>(7); });
11112391Sjason@lowepower.com    m.def("print_myobject2_1", [](const MyObject2 *obj) { py::print(obj->toString()); });
11212391Sjason@lowepower.com    m.def("print_myobject2_2", [](std::shared_ptr<MyObject2> obj) { py::print(obj->toString()); });
11312391Sjason@lowepower.com    m.def("print_myobject2_3", [](const std::shared_ptr<MyObject2> &obj) { py::print(obj->toString()); });
11412391Sjason@lowepower.com    m.def("print_myobject2_4", [](const std::shared_ptr<MyObject2> *obj) { py::print((*obj)->toString()); });
11511986Sandreas.sandberg@arm.com
11612391Sjason@lowepower.com    // Object managed by a std::shared_ptr<>, additionally derives from std::enable_shared_from_this<>
11712391Sjason@lowepower.com    class MyObject3 : public std::enable_shared_from_this<MyObject3> {
11812391Sjason@lowepower.com    public:
11912391Sjason@lowepower.com        MyObject3(int value) : value(value) { print_created(this, toString()); }
12012391Sjason@lowepower.com        std::string toString() const { return "MyObject3[" + std::to_string(value) + "]"; }
12112391Sjason@lowepower.com        virtual ~MyObject3() { print_destroyed(this); }
12212391Sjason@lowepower.com    private:
12312391Sjason@lowepower.com        int value;
12412391Sjason@lowepower.com    };
12511986Sandreas.sandberg@arm.com    py::class_<MyObject3, std::shared_ptr<MyObject3>>(m, "MyObject3")
12611986Sandreas.sandberg@arm.com        .def(py::init<int>());
12712391Sjason@lowepower.com    m.def("make_myobject3_1", []() { return new MyObject3(8); });
12812391Sjason@lowepower.com    m.def("make_myobject3_2", []() { return std::make_shared<MyObject3>(9); });
12912391Sjason@lowepower.com    m.def("print_myobject3_1", [](const MyObject3 *obj) { py::print(obj->toString()); });
13012391Sjason@lowepower.com    m.def("print_myobject3_2", [](std::shared_ptr<MyObject3> obj) { py::print(obj->toString()); });
13112391Sjason@lowepower.com    m.def("print_myobject3_3", [](const std::shared_ptr<MyObject3> &obj) { py::print(obj->toString()); });
13212391Sjason@lowepower.com    m.def("print_myobject3_4", [](const std::shared_ptr<MyObject3> *obj) { py::print((*obj)->toString()); });
13311986Sandreas.sandberg@arm.com
13412391Sjason@lowepower.com    // test_smart_ptr_refcounting
13512391Sjason@lowepower.com    m.def("test_object1_refcounting", []() {
13612391Sjason@lowepower.com        ref<MyObject1> o = new MyObject1(0);
13712391Sjason@lowepower.com        bool good = o->getRefCount() == 1;
13812391Sjason@lowepower.com        py::object o2 = py::cast(o, py::return_value_policy::reference);
13912391Sjason@lowepower.com        // always request (partial) ownership for objects with intrusive
14012391Sjason@lowepower.com        // reference counting even when using the 'reference' RVP
14112391Sjason@lowepower.com        good &= o->getRefCount() == 2;
14212391Sjason@lowepower.com        return good;
14312391Sjason@lowepower.com    });
14412391Sjason@lowepower.com
14512391Sjason@lowepower.com    // test_unique_nodelete
14612391Sjason@lowepower.com    // Object with a private destructor
14712391Sjason@lowepower.com    class MyObject4 {
14812391Sjason@lowepower.com    public:
14912391Sjason@lowepower.com        MyObject4(int value) : value{value} { print_created(this); }
15012391Sjason@lowepower.com        int value;
15112391Sjason@lowepower.com    private:
15212391Sjason@lowepower.com        ~MyObject4() { print_destroyed(this); }
15312391Sjason@lowepower.com    };
15411986Sandreas.sandberg@arm.com    py::class_<MyObject4, std::unique_ptr<MyObject4, py::nodelete>>(m, "MyObject4")
15511986Sandreas.sandberg@arm.com        .def(py::init<int>())
15611986Sandreas.sandberg@arm.com        .def_readwrite("value", &MyObject4::value);
15711986Sandreas.sandberg@arm.com
15812391Sjason@lowepower.com    // test_large_holder
15912391Sjason@lowepower.com    class MyObject5 { // managed by huge_unique_ptr
16012391Sjason@lowepower.com    public:
16112391Sjason@lowepower.com        MyObject5(int value) : value{value} { print_created(this); }
16212391Sjason@lowepower.com        ~MyObject5() { print_destroyed(this); }
16312391Sjason@lowepower.com        int value;
16412391Sjason@lowepower.com    };
16512391Sjason@lowepower.com    py::class_<MyObject5, huge_unique_ptr<MyObject5>>(m, "MyObject5")
16612391Sjason@lowepower.com        .def(py::init<int>())
16712391Sjason@lowepower.com        .def_readwrite("value", &MyObject5::value);
16811986Sandreas.sandberg@arm.com
16912391Sjason@lowepower.com    // test_shared_ptr_and_references
17012391Sjason@lowepower.com    struct SharedPtrRef {
17112391Sjason@lowepower.com        struct A {
17212391Sjason@lowepower.com            A() { print_created(this); }
17312391Sjason@lowepower.com            A(const A &) { print_copy_created(this); }
17412391Sjason@lowepower.com            A(A &&) { print_move_created(this); }
17512391Sjason@lowepower.com            ~A() { print_destroyed(this); }
17612391Sjason@lowepower.com        };
17711986Sandreas.sandberg@arm.com
17812391Sjason@lowepower.com        A value = {};
17912391Sjason@lowepower.com        std::shared_ptr<A> shared = std::make_shared<A>();
18011986Sandreas.sandberg@arm.com    };
18111986Sandreas.sandberg@arm.com    using A = SharedPtrRef::A;
18211986Sandreas.sandberg@arm.com    py::class_<A, std::shared_ptr<A>>(m, "A");
18311986Sandreas.sandberg@arm.com    py::class_<SharedPtrRef>(m, "SharedPtrRef")
18411986Sandreas.sandberg@arm.com        .def(py::init<>())
18511986Sandreas.sandberg@arm.com        .def_readonly("ref", &SharedPtrRef::value)
18611986Sandreas.sandberg@arm.com        .def_property_readonly("copy", [](const SharedPtrRef &s) { return s.value; },
18711986Sandreas.sandberg@arm.com                               py::return_value_policy::copy)
18811986Sandreas.sandberg@arm.com        .def_readonly("holder_ref", &SharedPtrRef::shared)
18911986Sandreas.sandberg@arm.com        .def_property_readonly("holder_copy", [](const SharedPtrRef &s) { return s.shared; },
19011986Sandreas.sandberg@arm.com                               py::return_value_policy::copy)
19111986Sandreas.sandberg@arm.com        .def("set_ref", [](SharedPtrRef &, const A &) { return true; })
19211986Sandreas.sandberg@arm.com        .def("set_holder", [](SharedPtrRef &, std::shared_ptr<A>) { return true; });
19311986Sandreas.sandberg@arm.com
19412391Sjason@lowepower.com    // test_shared_ptr_from_this_and_references
19512391Sjason@lowepower.com    struct SharedFromThisRef {
19612391Sjason@lowepower.com        struct B : std::enable_shared_from_this<B> {
19712391Sjason@lowepower.com            B() { print_created(this); }
19812391Sjason@lowepower.com            B(const B &) : std::enable_shared_from_this<B>() { print_copy_created(this); }
19912391Sjason@lowepower.com            B(B &&) : std::enable_shared_from_this<B>() { print_move_created(this); }
20012391Sjason@lowepower.com            ~B() { print_destroyed(this); }
20112391Sjason@lowepower.com        };
20212391Sjason@lowepower.com
20312391Sjason@lowepower.com        B value = {};
20412391Sjason@lowepower.com        std::shared_ptr<B> shared = std::make_shared<B>();
20512391Sjason@lowepower.com    };
20611986Sandreas.sandberg@arm.com    using B = SharedFromThisRef::B;
20711986Sandreas.sandberg@arm.com    py::class_<B, std::shared_ptr<B>>(m, "B");
20811986Sandreas.sandberg@arm.com    py::class_<SharedFromThisRef>(m, "SharedFromThisRef")
20911986Sandreas.sandberg@arm.com        .def(py::init<>())
21011986Sandreas.sandberg@arm.com        .def_readonly("bad_wp", &SharedFromThisRef::value)
21111986Sandreas.sandberg@arm.com        .def_property_readonly("ref", [](const SharedFromThisRef &s) -> const B & { return *s.shared; })
21211986Sandreas.sandberg@arm.com        .def_property_readonly("copy", [](const SharedFromThisRef &s) { return s.value; },
21311986Sandreas.sandberg@arm.com                               py::return_value_policy::copy)
21411986Sandreas.sandberg@arm.com        .def_readonly("holder_ref", &SharedFromThisRef::shared)
21511986Sandreas.sandberg@arm.com        .def_property_readonly("holder_copy", [](const SharedFromThisRef &s) { return s.shared; },
21611986Sandreas.sandberg@arm.com                               py::return_value_policy::copy)
21711986Sandreas.sandberg@arm.com        .def("set_ref", [](SharedFromThisRef &, const B &) { return true; })
21811986Sandreas.sandberg@arm.com        .def("set_holder", [](SharedFromThisRef &, std::shared_ptr<B>) { return true; });
21912037Sandreas.sandberg@arm.com
22012391Sjason@lowepower.com    // Issue #865: shared_from_this doesn't work with virtual inheritance
22112391Sjason@lowepower.com    struct SharedFromThisVBase : std::enable_shared_from_this<SharedFromThisVBase> {
22212391Sjason@lowepower.com        virtual ~SharedFromThisVBase() = default;
22312391Sjason@lowepower.com    };
22412391Sjason@lowepower.com    struct SharedFromThisVirt : virtual SharedFromThisVBase {};
22512391Sjason@lowepower.com    static std::shared_ptr<SharedFromThisVirt> sft(new SharedFromThisVirt());
22612391Sjason@lowepower.com    py::class_<SharedFromThisVirt, std::shared_ptr<SharedFromThisVirt>>(m, "SharedFromThisVirt")
22712391Sjason@lowepower.com        .def_static("get", []() { return sft.get(); });
22812391Sjason@lowepower.com
22912391Sjason@lowepower.com    // test_move_only_holder
23012037Sandreas.sandberg@arm.com    struct C {
23112037Sandreas.sandberg@arm.com        C() { print_created(this); }
23212037Sandreas.sandberg@arm.com        ~C() { print_destroyed(this); }
23312037Sandreas.sandberg@arm.com    };
23412391Sjason@lowepower.com    py::class_<C, custom_unique_ptr<C>>(m, "TypeWithMoveOnlyHolder")
23512391Sjason@lowepower.com        .def_static("make", []() { return custom_unique_ptr<C>(new C); });
23612037Sandreas.sandberg@arm.com
23712391Sjason@lowepower.com    // test_smart_ptr_from_default
23812037Sandreas.sandberg@arm.com    struct HeldByDefaultHolder { };
23912037Sandreas.sandberg@arm.com    py::class_<HeldByDefaultHolder>(m, "HeldByDefaultHolder")
24012037Sandreas.sandberg@arm.com        .def(py::init<>())
24112037Sandreas.sandberg@arm.com        .def_static("load_shared_ptr", [](std::shared_ptr<HeldByDefaultHolder>) {});
24212391Sjason@lowepower.com
24312391Sjason@lowepower.com    // test_shared_ptr_gc
24412391Sjason@lowepower.com    // #187: issue involving std::shared_ptr<> return value policy & garbage collection
24512391Sjason@lowepower.com    struct ElementBase { virtual void foo() { } /* Force creation of virtual table */ };
24612391Sjason@lowepower.com    py::class_<ElementBase, std::shared_ptr<ElementBase>>(m, "ElementBase");
24712391Sjason@lowepower.com
24812391Sjason@lowepower.com    struct ElementA : ElementBase {
24912391Sjason@lowepower.com        ElementA(int v) : v(v) { }
25012391Sjason@lowepower.com        int value() { return v; }
25112391Sjason@lowepower.com        int v;
25212391Sjason@lowepower.com    };
25312391Sjason@lowepower.com    py::class_<ElementA, ElementBase, std::shared_ptr<ElementA>>(m, "ElementA")
25412391Sjason@lowepower.com        .def(py::init<int>())
25512391Sjason@lowepower.com        .def("value", &ElementA::value);
25612391Sjason@lowepower.com
25712391Sjason@lowepower.com    struct ElementList {
25812391Sjason@lowepower.com        void add(std::shared_ptr<ElementBase> e) { l.push_back(e); }
25912391Sjason@lowepower.com        std::vector<std::shared_ptr<ElementBase>> l;
26012391Sjason@lowepower.com    };
26112391Sjason@lowepower.com    py::class_<ElementList, std::shared_ptr<ElementList>>(m, "ElementList")
26212391Sjason@lowepower.com        .def(py::init<>())
26312391Sjason@lowepower.com        .def("add", &ElementList::add)
26412391Sjason@lowepower.com        .def("get", [](ElementList &el) {
26512391Sjason@lowepower.com            py::list list;
26612391Sjason@lowepower.com            for (auto &e : el.l)
26712391Sjason@lowepower.com                list.append(py::cast(e));
26812391Sjason@lowepower.com            return list;
26912391Sjason@lowepower.com        });
27012391Sjason@lowepower.com}
271