summaryrefslogtreecommitdiff
path: root/ext/pybind11/docs/advanced/smart_ptrs.rst
diff options
context:
space:
mode:
Diffstat (limited to 'ext/pybind11/docs/advanced/smart_ptrs.rst')
-rw-r--r--ext/pybind11/docs/advanced/smart_ptrs.rst143
1 files changed, 143 insertions, 0 deletions
diff --git a/ext/pybind11/docs/advanced/smart_ptrs.rst b/ext/pybind11/docs/advanced/smart_ptrs.rst
new file mode 100644
index 000000000..23072b6bf
--- /dev/null
+++ b/ext/pybind11/docs/advanced/smart_ptrs.rst
@@ -0,0 +1,143 @@
+Smart pointers
+##############
+
+std::unique_ptr
+===============
+
+Given a class ``Example`` with Python bindings, it's possible to return
+instances wrapped in C++11 unique pointers, like so
+
+.. code-block:: cpp
+
+ std::unique_ptr<Example> create_example() { return std::unique_ptr<Example>(new Example()); }
+
+.. code-block:: cpp
+
+ m.def("create_example", &create_example);
+
+In other words, there is nothing special that needs to be done. While returning
+unique pointers in this way is allowed, it is *illegal* to use them as function
+arguments. For instance, the following function signature cannot be processed
+by pybind11.
+
+.. code-block:: cpp
+
+ void do_something_with_example(std::unique_ptr<Example> ex) { ... }
+
+The above signature would imply that Python needs to give up ownership of an
+object that is passed to this function, which is generally not possible (for
+instance, the object might be referenced elsewhere).
+
+std::shared_ptr
+===============
+
+The binding generator for classes, :class:`class_`, can be passed a template
+type that denotes a special *holder* type that is used to manage references to
+the object. If no such holder type template argument is given, the default for
+a type named ``Type`` is ``std::unique_ptr<Type>``, which means that the object
+is deallocated when Python's reference count goes to zero.
+
+It is possible to switch to other types of reference counting wrappers or smart
+pointers, which is useful in codebases that rely on them. For instance, the
+following snippet causes ``std::shared_ptr`` to be used instead.
+
+.. code-block:: cpp
+
+ py::class_<Example, std::shared_ptr<Example> /* <- holder type */> obj(m, "Example");
+
+Note that any particular class can only be associated with a single holder type.
+
+One potential stumbling block when using holder types is that they need to be
+applied consistently. Can you guess what's broken about the following binding
+code?
+
+.. code-block:: cpp
+
+ class Child { };
+
+ class Parent {
+ public:
+ Parent() : child(std::make_shared<Child>()) { }
+ Child *get_child() { return child.get(); } /* Hint: ** DON'T DO THIS ** */
+ private:
+ std::shared_ptr<Child> child;
+ };
+
+ PYBIND11_PLUGIN(example) {
+ py::module m("example");
+
+ py::class_<Child, std::shared_ptr<Child>>(m, "Child");
+
+ py::class_<Parent, std::shared_ptr<Parent>>(m, "Parent")
+ .def(py::init<>())
+ .def("get_child", &Parent::get_child);
+
+ return m.ptr();
+ }
+
+The following Python code will cause undefined behavior (and likely a
+segmentation fault).
+
+.. code-block:: python
+
+ from example import Parent
+ print(Parent().get_child())
+
+The problem is that ``Parent::get_child()`` returns a pointer to an instance of
+``Child``, but the fact that this instance is already managed by
+``std::shared_ptr<...>`` is lost when passing raw pointers. In this case,
+pybind11 will create a second independent ``std::shared_ptr<...>`` that also
+claims ownership of the pointer. In the end, the object will be freed **twice**
+since these shared pointers have no way of knowing about each other.
+
+There are two ways to resolve this issue:
+
+1. For types that are managed by a smart pointer class, never use raw pointers
+ in function arguments or return values. In other words: always consistently
+ wrap pointers into their designated holder types (such as
+ ``std::shared_ptr<...>``). In this case, the signature of ``get_child()``
+ should be modified as follows:
+
+.. code-block:: cpp
+
+ std::shared_ptr<Child> get_child() { return child; }
+
+2. Adjust the definition of ``Child`` by specifying
+ ``std::enable_shared_from_this<T>`` (see cppreference_ for details) as a
+ base class. This adds a small bit of information to ``Child`` that allows
+ pybind11 to realize that there is already an existing
+ ``std::shared_ptr<...>`` and communicate with it. In this case, the
+ declaration of ``Child`` should look as follows:
+
+.. _cppreference: http://en.cppreference.com/w/cpp/memory/enable_shared_from_this
+
+.. code-block:: cpp
+
+ class Child : public std::enable_shared_from_this<Child> { };
+
+.. _smart_pointers:
+
+Custom smart pointers
+=====================
+
+pybind11 supports ``std::unique_ptr`` and ``std::shared_ptr`` right out of the
+box. For any other custom smart pointer, transparent conversions can be enabled
+using a macro invocation similar to the following. It must be declared at the
+level before any binding code:
+
+.. code-block:: cpp
+
+ PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>);
+
+The first argument of :func:`PYBIND11_DECLARE_HOLDER_TYPE` should be a
+placeholder name that is used as a template parameter of the second argument.
+Thus, feel free to use any identifier, but use it consistently on both sides;
+also, don't use the name of a type that already exists in your codebase.
+
+Please take a look at the :ref:`macro_notes` before using this feature.
+
+.. seealso::
+
+ The file :file:`tests/test_smart_ptr.cpp` contains a complete example
+ that demonstrates how to work with custom reference-counting holder types
+ in more detail.