mirror of
https://github.com/pybind/pybind11.git
synced 2026-04-20 06:49:25 +00:00
* Pure `git merge --squash smart_holder` (no manual interventions). * Remove ubench/ directory. * Remove include/pybind11/smart_holder.h * [ci skip] smart_ptrs.rst updates [WIP/unfinished] * [ci skip] smart_ptrs.rst updates continued; also updating classes.rst, advanced/classes.rst * Remove README_smart_holder.rst * Restore original README.rst from master * [ci skip] Minimal change to README.rst, to leave a hint that this is pybind11v3 * [ci skip] Work in ChatGPT suggestions. * Change macro name to PYBIND11_RUN_TESTING_WITH_SMART_HOLDER_AS_DEFAULT_BUT_NEVER_USE_IN_PRODUCTION_PLEASE * Add a note pointing to the holder reinterpret_cast. * Incorporate suggestion by @virtuald: https://github.com/pybind/pybind11/pull/5542#discussion_r1967000989 * Systematically change most py::class_ to py::classh under docs/ * Remove references to README_smart_holder.rst This should have been part of commiteb550d03d3. * [ci skip] Fix minor oversight (``class_`` -> ``py::class_``) noticed by chance. * [ci skip] Resolve suggestion by @virtuald https://github.com/pybind/pybind11/pull/5542#discussion_r1969940605 * [ci skip] Apply suggestions by @timohl (thanks!) * https://github.com/pybind/pybind11/pull/5542#discussion_r1970714551 * https://github.com/pybind/pybind11/pull/5542#discussion_r1971315329 * https://github.com/pybind/pybind11/pull/5542#discussion_r1971322821 * Replace `classh : class_` inhertance with `using`, as suggested by @henryiii https://github.com/pybind/pybind11/pull/5542#issuecomment-2689034104 * Revert "Systematically change most py::class_ to py::classh under docs/" This reverts commitac9d31e13f. * docs: focus on py::smart_holder instead of py::classh Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com> * Restore minor general fixes that got lost whenac9d31e13fwas reverted. * Remove `- smart_holder` from list of branches in all .github/workflows * Extend classh note to explain whitespace noise motivation. * Suggest `py::smart_holder` for "most situations for safety" * Add back PYBIND11_HAS_INTERNALS_WITH_SMART_HOLDER_SUPPORT This define was * introduced with https://github.com/pybind/pybind11/pull/5286 * removed with https://github.com/pybind/pybind11/pull/5531 It is has been in use here: *f02a2b7653/pybind11_protobuf/native_proto_caster.h (L89-L101)Currently pybind11 unit tests for the two holder caster backwards compatibility traits * `copyable_holder_caster_shared_ptr_with_smart_holder_support_enabled` * `move_only_holder_caster_unique_ptr_with_smart_holder_support_enabled` are missing. * Add py::trampoline_self_life_support to all trampoline examples under docs/. Address suggestion by @timohl: * https://github.com/pybind/pybind11/pull/5542#issuecomment-2686452062 Add to the "please think twice" note: the overhead for safety is likely in the noise. Also fix a two-fold inconsistency introduced by revert-commit1e646c91b4: 1. py::trampoline_self_life_support is mentioned in a note, but is missing in the example right before. 2. The section starting with To enable safely passing a ``std::unique_ptr`` to a trampoline object between is obsolete. * Fix whitespace accident (indentation) introduced with1e646c91b4Apparently the mis-indentation was introduced when resolving merge conflicts for what became1e646c91b4* WHITESPACE CHANGES ONLY in README.rst (list of people that made significant contributions) * Add Ethan Steinberg to list of people that made significant contributions (for completeness, unrelated to smart_holder work). * [ci skip] Add to list of people that made significant contributions: major and/or influential contributors to smart_holder branch * #2904 by @rhaschke was merged on Mar 16, 2021 * #3012 by @rhaschke was merged on May 28, 2021 * #3039 by @jakobandersen was merged on Jun 29, 2021 * #3048 by @Skylion007 was merged on Jun 18, 2021 * #3588 by @virtuald was merged on Jan 3, 2022 * #3633 by @wangxf123456 was merged on Jan 25, 2022 * #3635 by @virtuald was merged on Jan 26, 2022 * #3645 by @wangxf123456 was merged on Jan 25, 2022 * #3796 by @wangxf123456 was merged on Mar 10, 2022 * #3807 by @wangxf123456 was merged on Mar 18, 2022 * #3838 by @wangxf123456 was merged on Apr 15, 2022 * #3929 by @tomba was merged on May 7, 2022 * #4031 by @wangxf123456 was merged on Jun 27, 2022 * #4343 by @wangxf123456 was merged on Nov 18, 2022 * #4381 by @wangxf123456 was merged on Dec 5, 2022 * #4539 by @wangxf123456 was merged on Feb 28, 2023 * #4609 by @wangxf123456 was merged on Apr 6, 2023 * #4775 by @wangxf123456 was merged on Aug 3, 2023 * #4921 by @iwanders was merged on Nov 7, 2023 * #4924 by @iwanders was merged on Nov 6, 2023 * #5401 by @msimacek was merged on Oct 8, 2024 Co-authored-by: Aaron Gokaslan <aaronGokaslan@gmail.com> Co-authored-by: Dustin Spicuzza <dustin@virtualroadside.com> Co-authored-by: Ivor Wanders <iwanders@users.noreply.github.com> Co-authored-by: Jakob Lykke Andersen <Jakob@caput.dk> Co-authored-by: Michael Šimáček <michael.simacek@oracle.com> Co-authored-by: Robert Haschke <rhaschke@users.noreply.github.com> Co-authored-by: Tomi Valkeinen <tomi.valkeinen@iki.fi> Co-authored-by: Xiaofei Wang <6218006+wangxf123456@users.noreply.github.com> --------- Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com> Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com> Co-authored-by: Aaron Gokaslan <aaronGokaslan@gmail.com> Co-authored-by: Dustin Spicuzza <dustin@virtualroadside.com> Co-authored-by: Ivor Wanders <iwanders@users.noreply.github.com> Co-authored-by: Jakob Lykke Andersen <Jakob@caput.dk> Co-authored-by: Michael Šimáček <michael.simacek@oracle.com> Co-authored-by: Robert Haschke <rhaschke@users.noreply.github.com> Co-authored-by: Tomi Valkeinen <tomi.valkeinen@iki.fi> Co-authored-by: Xiaofei Wang <6218006+wangxf123456@users.noreply.github.com>
234 lines
8.5 KiB
ReStructuredText
234 lines
8.5 KiB
ReStructuredText
Smart pointers & ``py::class_``
|
|
###############################
|
|
|
|
The binding generator for classes, ``py::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 ``T`` is ``std::unique_ptr<T>``.
|
|
|
|
.. note::
|
|
|
|
A ``py::class_`` for a given C++ type ``T`` — and all its derived types —
|
|
can only use a single holder type.
|
|
|
|
|
|
.. _smart_holder:
|
|
|
|
``py::smart_holder``
|
|
====================
|
|
|
|
Starting with pybind11v3, ``py::smart_holder`` is built into pybind11. It is
|
|
the recommended ``py::class_`` holder for most situations. However, for
|
|
backward compatibility it is **not** the default holder, and there are no
|
|
plans to make it the default holder in the future.
|
|
|
|
It is extremely easy to use the safer and more versatile ``py::smart_holder``:
|
|
simply add ``py::smart_holder`` to ``py::class_``:
|
|
|
|
* ``py::class_<T>`` to
|
|
|
|
* ``py::class_<T, py::smart_holder>``.
|
|
|
|
.. note::
|
|
|
|
A shorthand, ``py::classh<T>``, is provided for
|
|
``py::class_<T, py::smart_holder>``. The ``h`` in ``py::classh`` stands
|
|
for **smart_holder** but is shortened for brevity, ensuring it has the
|
|
same number of characters as ``py::class_``. This design choice facilitates
|
|
easy experimentation with ``py::smart_holder`` without introducing
|
|
distracting whitespace noise in diffs.
|
|
|
|
The ``py::smart_holder`` functionality includes the following:
|
|
|
|
* Support for **two-way** Python/C++ conversions for both
|
|
``std::unique_ptr<T>`` and ``std::shared_ptr<T>`` **simultaneously**.
|
|
|
|
* Passing a Python object back to C++ via ``std::unique_ptr<T>``, safely
|
|
**disowning** the Python object.
|
|
|
|
* Safely passing "trampoline" objects (objects with C++ virtual function
|
|
overrides implemented in Python, see :ref:`overriding_virtuals`) via
|
|
``std::unique_ptr<T>`` or ``std::shared_ptr<T>`` back to C++:
|
|
associated Python objects are automatically kept alive for the lifetime
|
|
of the smart-pointer.
|
|
|
|
* Full support for ``std::enable_shared_from_this`` (`cppreference
|
|
<http://en.cppreference.com/w/cpp/memory/enable_shared_from_this>`_).
|
|
|
|
|
|
``std::unique_ptr``
|
|
===================
|
|
|
|
This is the default ``py::class_`` holder and works as expected in
|
|
most situations. However, handling base-and-derived classes involves a
|
|
``reinterpret_cast``, which is, strictly speaking, undefined behavior.
|
|
Also note that the ``std::unique_ptr`` holder only supports passing a
|
|
``std::unique_ptr`` from C++ to Python, but not the other way around.
|
|
For example, the following code works as expected with ``py::class_<Example>``:
|
|
|
|
.. 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);
|
|
|
|
However, this will fail with ``py::class_<Example>`` (but works with
|
|
``py::class_<Example, py::smart_holder>``):
|
|
|
|
.. code-block:: cpp
|
|
|
|
void do_something_with_example(std::unique_ptr<Example> ex) { ... }
|
|
|
|
.. note::
|
|
|
|
The ``reinterpret_cast`` mentioned above is `here
|
|
<https://github.com/pybind/pybind11/blob/30eb39ed79d1e2eeff15219ac00773034300a5e6/include/pybind11/cast.h#L235>`_.
|
|
For completeness: The same cast is also applied to ``py::smart_holder``,
|
|
but that is safe, because ``py::smart_holder`` is not templated.
|
|
|
|
|
|
``std::shared_ptr``
|
|
===================
|
|
|
|
It is possible to use ``std::shared_ptr`` as the holder, for example:
|
|
|
|
.. code-block:: cpp
|
|
|
|
py::class_<Example, std::shared_ptr<Example> /* <- holder type */>(m, "Example");
|
|
|
|
Compared to using ``py::class_<Example, py::smart_holder>``, there are two noteworthy disadvantages:
|
|
|
|
* Because a ``py::class_`` for a given C++ type ``T`` can only use a
|
|
single holder type, ``std::unique_ptr<T>`` cannot even be passed from C++
|
|
to Python. This will become apparent only at runtime, often through a
|
|
segmentation fault.
|
|
|
|
* Similar to the ``std::unique_ptr`` holder, the handling of base-and-derived
|
|
classes involves a ``reinterpret_cast`` that has strictly speaking undefined
|
|
behavior, although it works as expected in most situations.
|
|
|
|
|
|
.. _smart_pointers:
|
|
|
|
Custom smart pointers
|
|
=====================
|
|
|
|
For custom smart pointers (e.g. ``c10::intrusive_ptr`` in pytorch), transparent
|
|
conversions can be enabled using a macro invocation similar to the following.
|
|
It must be declared at the top namespace 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.
|
|
|
|
The macro also accepts a third optional boolean parameter that is set to false
|
|
by default. Specify
|
|
|
|
.. code-block:: cpp
|
|
|
|
PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>, true)
|
|
|
|
if ``SmartPtr<T>`` can always be initialized from a ``T*`` pointer without the
|
|
risk of inconsistencies (such as multiple independent ``SmartPtr`` instances
|
|
believing that they are the sole owner of the ``T*`` pointer). A common
|
|
situation where ``true`` should be passed is when the ``T`` instances use
|
|
*intrusive* reference counting.
|
|
|
|
Please take a look at the :ref:`macro_notes` before using this feature.
|
|
|
|
By default, pybind11 assumes that your custom smart pointer has a standard
|
|
interface, i.e. provides a ``.get()`` member function to access the underlying
|
|
raw pointer. If this is not the case, pybind11's ``holder_helper`` must be
|
|
specialized:
|
|
|
|
.. code-block:: cpp
|
|
|
|
// Always needed for custom holder types
|
|
PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr<T>)
|
|
|
|
// Only needed if the type's `.get()` goes by another name
|
|
namespace PYBIND11_NAMESPACE { namespace detail {
|
|
template <typename T>
|
|
struct holder_helper<SmartPtr<T>> { // <-- specialization
|
|
static const T *get(const SmartPtr<T> &p) { return p.getPointer(); }
|
|
};
|
|
}}
|
|
|
|
The above specialization informs pybind11 that the custom ``SmartPtr`` class
|
|
provides ``.get()`` functionality via ``.getPointer()``.
|
|
|
|
.. note::
|
|
|
|
The two noteworthy disadvantages mentioned under the ``std::shared_ptr``
|
|
section apply similarly to custom smart pointer holders, but there is no
|
|
established safe alternative in this case.
|
|
|
|
.. 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.
|
|
|
|
|
|
Be careful not to accidentally undermine automatic lifetime management
|
|
======================================================================
|
|
|
|
``py::class_``-wrapped objects automatically manage the lifetime of the
|
|
wrapped C++ object, in collaboration with the chosen holder type.
|
|
When wrapping C++ functions involving raw pointers, care needs to be taken
|
|
to not inadvertently transfer ownership, resulting in multiple Python
|
|
objects acting as owners, causing heap-use-after-free or double-free errors.
|
|
For example:
|
|
|
|
.. code-block:: cpp
|
|
|
|
class Child { };
|
|
|
|
class Parent {
|
|
public:
|
|
Parent() : child(std::make_shared<Child>()) { }
|
|
Child *get_child() { return child.get(); } /* DANGER */
|
|
private:
|
|
std::shared_ptr<Child> child;
|
|
};
|
|
|
|
PYBIND11_MODULE(example, m) {
|
|
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); /* PROBLEM */
|
|
}
|
|
|
|
The following Python code leads to undefined behavior, likely resulting in
|
|
a segmentation fault.
|
|
|
|
.. code-block:: python
|
|
|
|
from example import Parent
|
|
|
|
print(Parent().get_child())
|
|
|
|
Part of the ``/* PROBLEM */`` here is that pybind11 falls back to using
|
|
``return_value_policy::take_ownership`` as the default (see
|
|
:ref:`return_value_policies`). The fact that the ``Child`` instance is
|
|
already managed by ``std::shared_ptr<Child>`` is lost. Therefore pybind11
|
|
will create a second independent ``std::shared_ptr<Child>`` that also
|
|
claims ownership of the pointer, eventually leading to heap-use-after-free
|
|
or double-free errors.
|
|
|
|
There are various ways to resolve this issue, either by changing
|
|
the ``Child`` or ``Parent`` C++ implementations (e.g. using
|
|
``std::enable_shared_from_this<Child>`` as a base class for
|
|
``Child``, or adding a member function to ``Parent`` that returns
|
|
``std::shared_ptr<Child>``), or if that is not feasible, by using
|
|
``return_value_policy::reference_internal``. What is the best approach
|
|
depends on the exact situation.
|