mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-24 22:25:10 +00:00
[smart_holder] Bake smart_holder functionality into class_
and type_caster_base
(#5257)
* Put bakein branch @ 18b72c0ffa
on top of smart_holder branch:
Commands used:
```
git checkout bakein
git diff smart_holder > ~/zd
git checkout smart_holder
git checkout -b bakein_sh
patch -p 1 < ~/zd
git checkout smart_holder \
MANIFEST.in \
README.rst \
README_smart_holder.rst \
docs/advanced/smart_ptrs.rst \
ubench/holder_comparison.cpp \
ubench/holder_comparison.py \
ubench/holder_comparison_extract_sheet_data.py \
ubench/number_bucket.h \
ubench/python/number_bucket.clif
git add -A
```
* Add back README_smart_holder.rst in tests/extra_python_package/test_files.py
* Restore smart_holder_poc.h as-is on smart_holder branch (i.e. undo `PYBIND11_SMART_HOLDER_PADDING`, which was meant for stress-testing only).
* Insert `std::move()` as suggested by @laramiel
* `property_cpp_function_sh_*` named specializations, as suggested by @laramiel (https://github.com/pybind/pybind11/pull/5257#discussion_r1688346807)
* Call `property_cpp_function_classic` member functions, rather than inlining the implementations.
* Use `PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT` in holder_comparison.cpp (holder_comparison.py is NOT changed accordingly in this commit, i.e. can still only be run if the smart_holder functionality is available).
* Systematically rename `loaded_as` to `load_as` (`shared_ptr`, `unique_ptr`) as suggested by @laramiel
* Make change as suggested by @laramiel. This makes it much more obvious that the latest implementation of `smart_holder_from_unique_ptr()` accepts all existing `return_value_policy` enum values except `copy`.
* Resolve `BAKEIN_WIP: Rewrite comment.` for `property_cpp_function_*` specializations.
* Resolve `BAKEIN_WIP: Add comment to explain: This is meant for stress-testing only.`
* Resolve all remaining BAKEIN_WIP (in pybind11/cast.h).
Leave only two pairs of SMART_HOLDER_BAKEIN_FOLLOW_ON comments: refactoring of copyable_holder_caster, move_only_holder_caster. This is best left until after the smart_holder branch is merged into the master branch.
* Remove obsolete `using holder_type = smart_holder;` in `load_helper`
* Add SMART_HOLDER_BAKEIN_FOLLOW_ON comment for `internals::default_holder`
* README_smart_holder.rst update (line count reduced from 356 to 123).
This commit is contained in:
parent
6765d4e814
commit
48f25275c4
@ -154,10 +154,9 @@ set(PYBIND11_HEADERS
|
||||
include/pybind11/detail/init.h
|
||||
include/pybind11/detail/internals.h
|
||||
include/pybind11/detail/smart_holder_poc.h
|
||||
include/pybind11/detail/smart_holder_sfinae_hooks_only.h
|
||||
include/pybind11/detail/smart_holder_type_casters.h
|
||||
include/pybind11/detail/type_caster_base.h
|
||||
include/pybind11/detail/typeid.h
|
||||
include/pybind11/detail/using_smart_holder.h
|
||||
include/pybind11/detail/value_and_holder.h
|
||||
include/pybind11/attr.h
|
||||
include/pybind11/buffer_info.h
|
||||
|
@ -6,58 +6,71 @@ pybind11 — smart_holder branch
|
||||
Overview
|
||||
========
|
||||
|
||||
- The smart_holder git branch is a strict superset of the master
|
||||
branch. Everything that works on master is expected to work exactly the same
|
||||
with the smart_holder branch.
|
||||
- The smart_holder branch is a strict superset of the pybind11 master branch.
|
||||
Everything that works with the master branch is expected to work exactly the
|
||||
same with the smart_holder branch.
|
||||
|
||||
- **Smart-pointer interoperability** (``std::unique_ptr``, ``std::shared_ptr``)
|
||||
is implemented as an **add-on**.
|
||||
- Activating the smart_holder functionality for a given C++ type ``T`` is as
|
||||
easy as changing ``py::class_<T>`` to ``py::classh<T>`` in client code.
|
||||
|
||||
- The add-on also supports
|
||||
* passing a Python object back to C++ via ``std::unique_ptr``, safely
|
||||
- The ``py::classh<T>`` functionality includes
|
||||
|
||||
* support for **two-way** Python/C++ conversions for both
|
||||
``std::unique_ptr<T>`` and ``std::shared_ptr<T>`` **simultaneously**.
|
||||
— In contrast, ``py::class_<T>`` only supports one-way C++-to-Python
|
||||
conversions for ``std::unique_ptr<T>``, or alternatively two-way
|
||||
Python/C++ conversions for ``std::shared_ptr<T>``, which then excludes
|
||||
the one-way C++-to-Python ``std::unique_ptr<T>`` conversions (this
|
||||
manifests itself through undefined runtime behavior).
|
||||
|
||||
* passing a Python object back to C++ via ``std::unique_ptr<T>``, safely
|
||||
**disowning** the Python object.
|
||||
|
||||
* safely passing `"trampoline"
|
||||
<https://pybind11.readthedocs.io/en/stable/advanced/classes.html#overriding-virtual-functions-in-python>`_
|
||||
objects (objects with C++ virtual function overrides implemented in
|
||||
Python) via ``std::unique_ptr`` or ``std::shared_ptr`` back to C++:
|
||||
Python) 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.
|
||||
|
||||
- The smart_holder branch can be used in two modes:
|
||||
* **Conservative mode**: ``py::class_`` works exactly as on master.
|
||||
``py::classh`` uses ``py::smart_holder``.
|
||||
* **Progressive mode**: ``py::class_`` uses ``py::smart_holder``
|
||||
(i.e. ``py::smart_holder`` is the default holder).
|
||||
Note: As of `PR #5257 <https://github.com/pybind/pybind11/pull/5257>`_
|
||||
the smart_holder functionality is fully baked into pybind11.
|
||||
Prior to PR #5257 the smart_holder implementation was an "add-on", which made
|
||||
it necessary to use a ``PYBIND11_SMART_HOLDER_TYPE_CASTERS`` macro. This macro
|
||||
still exists for backward compatibility, but is now a no-op. The trade-off
|
||||
for this convenience is that the ``PYBIND11_INTERNALS_VERSION`` needed to be
|
||||
changed. Consequently, Python extension modules built with the smart_holder
|
||||
branch no longer interoperate with extension modules built with the pybind11
|
||||
master branch. If cross-extension-module interoperability is required, all
|
||||
extension modules involved must be built with the smart_holder branch.
|
||||
— Probably, most extension modules do not require cross-extension-module
|
||||
interoperability, but exceptions to this are quite common.
|
||||
|
||||
|
||||
What is fundamentally different?
|
||||
--------------------------------
|
||||
|
||||
- Classic pybind11 has the concept of "smart-pointer is holder".
|
||||
Interoperability between smart-pointers is completely missing. For
|
||||
example, when using ``std::shared_ptr`` as holder, ``return``-ing
|
||||
a ``std::unique_ptr`` leads to undefined runtime behavior
|
||||
(`#1138 <https://github.com/pybind/pybind11/issues/1138>`_). A
|
||||
`systematic analysis is here <https://github.com/pybind/pybind11/pull/2672#issuecomment-748392993>`_.
|
||||
Interoperability between smart-pointers is completely missing. For example,
|
||||
with ``py::class_<T, std::shared_ptr<T>>``, ``return``-ing a
|
||||
``std::unique_ptr<T>`` leads to undefined runtime behavior
|
||||
(`#1138 <https://github.com/pybind/pybind11/issues/1138>`_).
|
||||
A `systematic analysis can be found here
|
||||
<https://github.com/pybind/pybind11/pull/2672#issuecomment-748392993>`_.
|
||||
|
||||
- ``py::smart_holder`` has a richer concept in comparison, with well-defined
|
||||
runtime behavior. The holder "knows" about both ``std::unique_ptr`` and
|
||||
``std::shared_ptr`` and how they interoperate.
|
||||
|
||||
- Caveat (#HelpAppreciated): currently the ``smart_holder`` branch does
|
||||
not have a well-lit path for including interoperability with custom
|
||||
smart-pointers. It is expected to be a fairly obvious extension of the
|
||||
``smart_holder`` implementation, but will depend on the exact specifications
|
||||
of each custom smart-pointer type (generalizations are very likely possible).
|
||||
runtime behavior in all situations. ``py::smart_holder`` "knows" about both
|
||||
``std::unique_ptr<T>`` and ``std::shared_ptr<T>``, and how they interoperate.
|
||||
|
||||
|
||||
What motivated the development of the smart_holder code?
|
||||
--------------------------------------------------------
|
||||
|
||||
- Necessity is the mother. The bigger context is the ongoing retooling of
|
||||
`PyCLIF <https://github.com/google/clif/>`_, to use pybind11 underneath
|
||||
instead of directly targeting the Python C API. Essentially, the smart_holder
|
||||
branch is porting established PyCLIF functionality into pybind11.
|
||||
- The original context was retooling of `PyCLIF
|
||||
<https://github.com/google/clif/>`_, to use pybind11 underneath,
|
||||
instead of directly targeting the Python C API. Essentially the smart_holder
|
||||
branch is porting established PyCLIF functionality into pybind11. (However,
|
||||
this work also led to bug fixes in PyCLIF.)
|
||||
|
||||
|
||||
Installation
|
||||
@ -72,225 +85,6 @@ Currently ``git clone`` is the only option. We do not have released packages.
|
||||
Everything else is exactly identical to using the default (master) branch.
|
||||
|
||||
|
||||
Conservative or Progressive mode?
|
||||
=================================
|
||||
|
||||
It depends. To a first approximation, for a stand-alone, new project, the
|
||||
Progressive mode will be easiest to use. For larger projects or projects
|
||||
that integrate with third-party pybind11-based projects, the Conservative
|
||||
mode may be more practical, at least initially, although it comes with the
|
||||
disadvantage of having to use the ``PYBIND11_SMART_HOLDER_TYPE_CASTERS`` macro.
|
||||
|
||||
|
||||
Conservative mode
|
||||
-----------------
|
||||
|
||||
Here is a minimal example for wrapping a C++ type with ``py::smart_holder`` as
|
||||
holder:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <pybind11/smart_holder.h>
|
||||
|
||||
struct Foo {};
|
||||
|
||||
PYBIND11_SMART_HOLDER_TYPE_CASTERS(Foo)
|
||||
|
||||
PYBIND11_MODULE(example_bindings, m) {
|
||||
namespace py = pybind11;
|
||||
py::classh<Foo>(m, "Foo");
|
||||
}
|
||||
|
||||
There are three small differences compared to Classic pybind11:
|
||||
|
||||
- ``#include <pybind11/smart_holder.h>`` is used instead of
|
||||
``#include <pybind11/pybind11.h>``.
|
||||
|
||||
- The ``PYBIND11_SMART_HOLDER_TYPE_CASTERS(Foo)`` macro is needed.
|
||||
— NOTE: This macro needs to be in the global namespace.
|
||||
|
||||
- ``py::classh`` is used instead of ``py::class_``.
|
||||
|
||||
To the 2nd bullet point, the ``PYBIND11_SMART_HOLDER_TYPE_CASTERS`` macro
|
||||
needs to appear in all translation units with pybind11 bindings that involve
|
||||
Python⇄C++ conversions for ``Foo``. This is the biggest inconvenience of the
|
||||
Conservative mode. Practically, at a larger scale it is best to work with a
|
||||
pair of ``.h`` and ``.cpp`` files for the bindings code, with the macros in
|
||||
the ``.h`` files.
|
||||
|
||||
To the 3rd bullet point, ``py::classh<Foo>`` is simply a shortcut for
|
||||
``py::class_<Foo, py::smart_holder>``. The shortcut makes it possible to
|
||||
switch to using ``py::smart_holder`` without disturbing the indentation of
|
||||
existing code.
|
||||
|
||||
When migrating code that uses ``py::class_<Bar, std::shared_ptr<Bar>>``
|
||||
there are two alternatives. The first one is to use ``py::classh<Bar>``:
|
||||
|
||||
.. code-block:: diff
|
||||
|
||||
- py::class_<Bar, std::shared_ptr<Bar>>(m, "Bar");
|
||||
+ py::classh<Bar>(m, "Bar");
|
||||
|
||||
This is clean and simple, but makes it difficult to fall back to Classic
|
||||
mode if needed. The second alternative is to replace ``std::shared_ptr<Bar>``
|
||||
with ``PYBIND11_SH_AVL(Bar)``:
|
||||
|
||||
.. code-block:: diff
|
||||
|
||||
- py::class_<Bar, std::shared_ptr<Bar>>(m, "Bar");
|
||||
+ py::class_<Bar, PYBIND11_SH_AVL(Bar)>(m, "Bar");
|
||||
|
||||
The ``PYBIND11_SH_AVL`` macro substitutes ``py::smart_holder``
|
||||
in Conservative mode, or ``std::shared_ptr<Bar>`` in Classic mode.
|
||||
See tests/test_classh_mock.cpp for an example. Note that the macro is also
|
||||
designed to not disturb the indentation of existing code.
|
||||
|
||||
|
||||
Progressive mode
|
||||
----------------
|
||||
|
||||
To work in Progressive mode:
|
||||
|
||||
- Add ``-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT`` to the compilation commands.
|
||||
|
||||
- Remove or replace (see below) ``std::shared_ptr<...>`` holders.
|
||||
|
||||
- Only if custom smart-pointers are used: the
|
||||
``PYBIND11_TYPE_CASTER_BASE_HOLDER`` macro is needed (see
|
||||
tests/test_smart_ptr.cpp for examples).
|
||||
|
||||
Overall this is probably easier to work with than the Conservative mode, but
|
||||
|
||||
- the macro inconvenience is shifted from ``py::smart_holder`` to custom
|
||||
smart-pointer holders (which are probably much more rare).
|
||||
|
||||
- it will not interoperate with other extensions built against master or
|
||||
stable, or extensions built in Conservative mode (see the cross-module
|
||||
compatibility section below).
|
||||
|
||||
When migrating code that uses ``py::class_<Bar, std::shared_ptr<Bar>>`` there
|
||||
are the same alternatives as for the Conservative mode (see previous section).
|
||||
An additional alternative is to use the ``PYBIND11_SH_DEF(...)`` macro:
|
||||
|
||||
.. code-block:: diff
|
||||
|
||||
- py::class_<Bar, std::shared_ptr<Bar>>(m, "Bar");
|
||||
+ py::class_<Bar, PYBIND11_SH_DEF(Bar)>(m, "Bar");
|
||||
|
||||
The ``PYBIND11_SH_DEF`` macro substitutes ``py::smart_holder`` only in
|
||||
Progressive mode, or ``std::shared_ptr<Bar>`` in Classic or Conservative
|
||||
mode. See tests/test_classh_mock.cpp for an example. Note that the
|
||||
``PYBIND11_SMART_HOLDER_TYPE_CASTERS`` macro is never needed in combination
|
||||
with the ``PYBIND11_SH_DEF`` macro, which is an advantage compared to the
|
||||
``PYBIND11_SH_AVL`` macro. Please review tests/test_classh_mock.cpp for a
|
||||
concise overview of all available options.
|
||||
|
||||
|
||||
Transition from Classic to Progressive mode
|
||||
-------------------------------------------
|
||||
|
||||
This still has to be tried out more in practice, but in small-scale situations
|
||||
it may be feasible to switch directly to Progressive mode in a break-fix
|
||||
fashion. In large-scale situations it seems more likely that an incremental
|
||||
approach is needed, which could mean incrementally converting ``py::class_``
|
||||
to ``py::classh`` and using the family of related macros, then flip the switch
|
||||
to Progressive mode, and convert ``py::classh`` back to ``py:class_`` combined
|
||||
with removal of the macros if desired (at that point it will work equivalently
|
||||
either way). It may be smart to delay the final cleanup step until all
|
||||
third-party projects of interest have made the switch, because then the code
|
||||
will continue to work in all modes.
|
||||
|
||||
|
||||
Using py::smart_holder but with fallback to Classic pybind11
|
||||
------------------------------------------------------------
|
||||
|
||||
For situations in which compatibility with Classic pybind11
|
||||
(without smart_holder) is needed for some period of time, fallback
|
||||
to Classic mode can be enabled by copying the ``BOILERPLATE`` code
|
||||
block from tests/test_classh_mock.cpp. This code block provides mock
|
||||
implementations of ``py::classh`` and the family of related macros
|
||||
(e.g. ``PYBIND11_SMART_HOLDER_TYPE_CASTERS``).
|
||||
|
||||
|
||||
Classic / Conservative / Progressive cross-module compatibility
|
||||
---------------------------------------------------------------
|
||||
|
||||
Currently there are essentially three modes for building a pybind11 extension
|
||||
module:
|
||||
|
||||
- Classic: pybind11 stable (e.g. v2.6.2) or current master branch.
|
||||
|
||||
- Conservative: pybind11 smart_holder branch.
|
||||
|
||||
- Progressive: pybind11 smart_holder branch with
|
||||
``-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT``.
|
||||
|
||||
In environments that mix extension modules built with different modes,
|
||||
this is the compatibility matrix for ``py::class_``-wrapped types:
|
||||
|
||||
.. list-table:: Compatibility matrix
|
||||
:widths: auto
|
||||
:header-rows: 2
|
||||
|
||||
* -
|
||||
-
|
||||
-
|
||||
- Module 2
|
||||
-
|
||||
* -
|
||||
-
|
||||
- Classic
|
||||
- Conservative
|
||||
- Progressive
|
||||
* -
|
||||
- **Classic**
|
||||
- full
|
||||
- one-and-a-half-way
|
||||
- isolated
|
||||
* - **Module 1**
|
||||
- **Conservative**
|
||||
- one-and-a-half-way
|
||||
- full
|
||||
- isolated
|
||||
* -
|
||||
- **Progressive**
|
||||
- isolated
|
||||
- isolated
|
||||
- full
|
||||
|
||||
Mixing Classic+Progressive or Conservative+Progressive is very easy to
|
||||
understand: the extension modules are essentially completely isolated from
|
||||
each other. This is in fact just the same as using pybind11 versions with
|
||||
differing `"internals version"
|
||||
<https://github.com/pybind/pybind11/blob/114be7f4ade0ad798cd4c7f5d65ebe4ba8bd892d/include/pybind11/detail/internals.h#L95>`_
|
||||
in the past. While this is easy to understand, there is also no incremental
|
||||
transition path between Classic and Progressive.
|
||||
|
||||
The Conservative mode enables incremental transitions, but at the cost of
|
||||
more complexity. Types wrapped in a Classic module are fully compatible with
|
||||
a Conservative module. However, a type wrapped in a Conservative module is
|
||||
compatible with a Classic module only if ``py::smart_holder`` is **not** used
|
||||
(for that type). A type wrapped with ``py::smart_holder`` is incompatible with
|
||||
a Classic module. This is an important pitfall to keep in mind: attempts to use
|
||||
``py::smart_holder``-wrapped types in a Classic module will lead to undefined
|
||||
runtime behavior, such as a SEGFAULT. This is a more general flavor of the
|
||||
long-standing issue `#1138 <https://github.com/pybind/pybind11/issues/1138>`_,
|
||||
often referred to as "holder mismatch". It is important to note that the
|
||||
pybind11 smart_holder branch solves the smart-pointer interoperability issue,
|
||||
but not the more general holder mismatch issue. — Unfortunately the existing
|
||||
pybind11 internals do not track holder runtime type information, therefore
|
||||
the holder mismatch issue cannot be solved in a fashion that would allow
|
||||
an incremental transition, which is the whole point of the Conservative
|
||||
mode. Please proceed with caution. (See `PR #2644
|
||||
<https://github.com/pybind/pybind11/pull/2644>`_ for background, which is
|
||||
labeled with "abi break".)
|
||||
|
||||
Another pitfall worth pointing out specifically, although it follows
|
||||
from the previous: mixing base and derived classes between Classic and
|
||||
Conservative modules means that neither the base nor the derived class can
|
||||
use ``py::smart_holder``.
|
||||
|
||||
|
||||
Trampolines and std::unique_ptr
|
||||
-------------------------------
|
||||
|
||||
@ -307,37 +101,10 @@ inherit from ``py::trampoline_self_life_support``, for example:
|
||||
...
|
||||
};
|
||||
|
||||
This is the only difference compared to Classic pybind11. A fairly
|
||||
This is the only difference compared to classic pybind11. A fairly
|
||||
minimal but complete example is tests/test_class_sh_trampoline_unique_ptr.cpp.
|
||||
|
||||
|
||||
Ideas for the long-term
|
||||
-----------------------
|
||||
|
||||
The macros are clearly an inconvenience in many situations. Highly
|
||||
speculative: to avoid the need for the macros, a potential approach would
|
||||
be to combine the Classic implementation (``type_caster_base``) with
|
||||
the ``smart_holder_type_caster``, but this will probably be very messy and
|
||||
not great as a long-term solution. The ``type_caster_base`` code is very
|
||||
complex already. A more maintainable approach long-term could be to work
|
||||
out and document a smart_holder-based solution for custom smart-pointers
|
||||
in pybind11 version ``N``, then purge ``type_caster_base`` in version
|
||||
``N+1``. #HelpAppreciated.
|
||||
|
||||
|
||||
Testing of PRs against the smart_holder branch
|
||||
----------------------------------------------
|
||||
|
||||
In the pybind11 GitHub Actions, PRs against the smart_holder branch are
|
||||
automatically tested in both modes (Conservative, Progressive), with the
|
||||
only difference that ``PYBIND11_USE_SMART_HOLDER_AS_DEFAULT`` is defined
|
||||
for Progressive mode testing.
|
||||
|
||||
For interactive testing, the ``PYBIND11_USE_SMART_HOLDER_AS_DEFAULT``
|
||||
define needs to be manually added to the cmake command. See
|
||||
.github/workflows/ci_sh.yml for examples.
|
||||
|
||||
|
||||
Related links
|
||||
=============
|
||||
|
||||
@ -353,4 +120,4 @@ Related links
|
||||
* Small `slide deck
|
||||
<https://docs.google.com/presentation/d/1r7auDN0x-b6uf-XCvUnZz6z09raasRcCHBMVDh7PsnQ/>`_
|
||||
presented in meeting with pybind11 maintainers on Feb 22, 2021. Slides 5
|
||||
and 6 show performance comparisons.
|
||||
and 6 show performance comparisons. (These are outdated but probably not far off.)
|
||||
|
@ -331,6 +331,10 @@ struct type_record {
|
||||
/// Is the class inheritable from python classes?
|
||||
bool is_final : 1;
|
||||
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
holder_enum_t holder_enum_v = holder_enum_t::undefined;
|
||||
#endif
|
||||
|
||||
PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *) ) {
|
||||
auto *base_info = detail::get_type_info(base, false);
|
||||
if (!base_info) {
|
||||
|
@ -12,7 +12,6 @@
|
||||
|
||||
#include "detail/common.h"
|
||||
#include "detail/descr.h"
|
||||
#include "detail/smart_holder_sfinae_hooks_only.h"
|
||||
#include "detail/type_caster_base.h"
|
||||
#include "detail/typeid.h"
|
||||
#include "pytypes.h"
|
||||
@ -29,33 +28,17 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifdef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
|
||||
# include "detail/smart_holder_type_casters.h"
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4127)
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
#ifndef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
|
||||
template <typename T>
|
||||
class type_caster_for_class_ : public type_caster_base<T> {};
|
||||
#endif
|
||||
|
||||
template <typename type, typename SFINAE = void>
|
||||
class type_caster : public type_caster_for_class_<type> {};
|
||||
|
||||
class type_caster : public type_caster_base<type> {};
|
||||
template <typename type>
|
||||
using make_caster = type_caster<intrinsic_t<type>>;
|
||||
|
||||
template <typename T>
|
||||
struct type_uses_smart_holder_type_caster {
|
||||
static constexpr bool value
|
||||
= std::is_base_of<smart_holder_type_caster_base_tag, make_caster<T>>::value;
|
||||
};
|
||||
|
||||
// Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T
|
||||
template <typename T>
|
||||
typename make_caster<T>::template cast_op_type<T> cast_op(make_caster<T> &caster) {
|
||||
@ -771,6 +754,7 @@ struct holder_helper {
|
||||
static auto get(const T &p) -> decltype(p.get()) { return p.get(); }
|
||||
};
|
||||
|
||||
// SMART_HOLDER_BAKEIN_FOLLOW_ON: Rewrite comment, with reference to shared_ptr specialization.
|
||||
/// Type caster for holder types like std::shared_ptr, etc.
|
||||
/// The SFINAE hook is provided to help work around the current lack of support
|
||||
/// for smart-pointer interoperability. Please consider it an implementation
|
||||
@ -852,12 +836,140 @@ protected:
|
||||
holder_type holder;
|
||||
};
|
||||
|
||||
#ifndef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
|
||||
template <typename, typename SFINAE = void>
|
||||
struct copyable_holder_caster_shared_ptr_with_smart_holder_support_enabled : std::true_type {};
|
||||
|
||||
// SMART_HOLDER_BAKEIN_FOLLOW_ON: Refactor copyable_holder_caster to reduce code duplication.
|
||||
template <typename type>
|
||||
struct copyable_holder_caster<
|
||||
type,
|
||||
std::shared_ptr<type>,
|
||||
enable_if_t<copyable_holder_caster_shared_ptr_with_smart_holder_support_enabled<type>::value>>
|
||||
: public type_caster_base<type> {
|
||||
public:
|
||||
using base = type_caster_base<type>;
|
||||
static_assert(std::is_base_of<base, type_caster<type>>::value,
|
||||
"Holder classes are only supported for custom types");
|
||||
using base::base;
|
||||
using base::cast;
|
||||
using base::typeinfo;
|
||||
using base::value;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
return base::template load_impl<copyable_holder_caster<type, std::shared_ptr<type>>>(
|
||||
src, convert);
|
||||
}
|
||||
|
||||
explicit operator std::shared_ptr<type> *() {
|
||||
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
|
||||
pybind11_fail("Passing `std::shared_ptr<T> *` from Python to C++ is not supported "
|
||||
"(inherently unsafe).");
|
||||
}
|
||||
return std::addressof(shared_ptr_holder);
|
||||
}
|
||||
|
||||
explicit operator std::shared_ptr<type> &() {
|
||||
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
|
||||
shared_ptr_holder = sh_load_helper.load_as_shared_ptr(value);
|
||||
}
|
||||
return shared_ptr_holder;
|
||||
}
|
||||
|
||||
static handle
|
||||
cast(const std::shared_ptr<type> &src, return_value_policy policy, handle parent) {
|
||||
const auto *ptr = src.get();
|
||||
auto st = type_caster_base<type>::src_and_type(ptr);
|
||||
if (st.second == nullptr) {
|
||||
return handle(); // no type info: error will be set already
|
||||
}
|
||||
if (st.second->holder_enum_v == detail::holder_enum_t::smart_holder) {
|
||||
return smart_holder_type_caster_support::smart_holder_from_shared_ptr(
|
||||
src, policy, parent, st);
|
||||
}
|
||||
return type_caster_base<type>::cast_holder(ptr, &src);
|
||||
}
|
||||
|
||||
// This function will succeed even if the `responsible_parent` does not own the
|
||||
// wrapped C++ object directly.
|
||||
// It is the responsibility of the caller to ensure that the `responsible_parent`
|
||||
// has a `keep_alive` relationship with the owner of the wrapped C++ object, or
|
||||
// that the wrapped C++ object lives for the duration of the process.
|
||||
static std::shared_ptr<type> shared_ptr_with_responsible_parent(handle responsible_parent) {
|
||||
copyable_holder_caster loader;
|
||||
loader.load(responsible_parent, /*convert=*/false);
|
||||
assert(loader.typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder);
|
||||
return loader.sh_load_helper.load_as_shared_ptr(loader.value, responsible_parent);
|
||||
}
|
||||
|
||||
protected:
|
||||
friend class type_caster_generic;
|
||||
void check_holder_compat() {
|
||||
if (typeinfo->default_holder) {
|
||||
throw cast_error("Unable to load a custom holder type from a default-holder instance");
|
||||
}
|
||||
}
|
||||
|
||||
void load_value(value_and_holder &&v_h) {
|
||||
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
|
||||
sh_load_helper.loaded_v_h = v_h;
|
||||
value = sh_load_helper.get_void_ptr_or_nullptr();
|
||||
return;
|
||||
}
|
||||
if (v_h.holder_constructed()) {
|
||||
value = v_h.value_ptr();
|
||||
shared_ptr_holder = v_h.template holder<std::shared_ptr<type>>();
|
||||
return;
|
||||
}
|
||||
throw cast_error("Unable to cast from non-held to held instance (T& to Holder<T>) "
|
||||
# if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
||||
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for "
|
||||
"type information)");
|
||||
# else
|
||||
"of type '"
|
||||
+ type_id<std::shared_ptr<type>>() + "''");
|
||||
# endif
|
||||
}
|
||||
|
||||
template <typename T = std::shared_ptr<type>,
|
||||
detail::enable_if_t<!std::is_constructible<T, const T &, type *>::value, int> = 0>
|
||||
bool try_implicit_casts(handle, bool) {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T = std::shared_ptr<type>,
|
||||
detail::enable_if_t<std::is_constructible<T, const T &, type *>::value, int> = 0>
|
||||
bool try_implicit_casts(handle src, bool convert) {
|
||||
for (auto &cast : typeinfo->implicit_casts) {
|
||||
copyable_holder_caster sub_caster(*cast.first);
|
||||
if (sub_caster.load(src, convert)) {
|
||||
value = cast.second(sub_caster.value);
|
||||
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
|
||||
sh_load_helper.loaded_v_h = sub_caster.sh_load_helper.loaded_v_h;
|
||||
} else {
|
||||
shared_ptr_holder
|
||||
= std::shared_ptr<type>(sub_caster.shared_ptr_holder, (type *) value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool try_direct_conversions(handle) { return false; }
|
||||
|
||||
std::shared_ptr<type> shared_ptr_holder;
|
||||
smart_holder_type_caster_support::load_helper<remove_cv_t<type>> sh_load_helper; // Const2Mutbl
|
||||
};
|
||||
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
|
||||
/// Specialize for the common std::shared_ptr, so users don't need to
|
||||
template <typename T>
|
||||
class type_caster<std::shared_ptr<T>> : public copyable_holder_caster<T, std::shared_ptr<T>> {};
|
||||
#endif
|
||||
|
||||
// SMART_HOLDER_BAKEIN_FOLLOW_ON: Rewrite comment, with reference to unique_ptr specialization.
|
||||
/// Type caster for holder types like std::unique_ptr.
|
||||
/// Please consider the SFINAE hook an implementation detail, as explained
|
||||
/// in the comment for the copyable_holder_caster.
|
||||
@ -873,11 +985,115 @@ struct move_only_holder_caster {
|
||||
static constexpr auto name = type_caster_base<type>::name;
|
||||
};
|
||||
|
||||
#ifndef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
|
||||
template <typename, typename SFINAE = void>
|
||||
struct move_only_holder_caster_unique_ptr_with_smart_holder_support_enabled : std::true_type {};
|
||||
|
||||
// SMART_HOLDER_BAKEIN_FOLLOW_ON: Refactor move_only_holder_caster to reduce code duplication.
|
||||
template <typename type, typename deleter>
|
||||
struct move_only_holder_caster<
|
||||
type,
|
||||
std::unique_ptr<type, deleter>,
|
||||
enable_if_t<move_only_holder_caster_unique_ptr_with_smart_holder_support_enabled<type>::value>>
|
||||
: public type_caster_base<type> {
|
||||
public:
|
||||
using base = type_caster_base<type>;
|
||||
static_assert(std::is_base_of<base, type_caster<type>>::value,
|
||||
"Holder classes are only supported for custom types");
|
||||
using base::base;
|
||||
using base::cast;
|
||||
using base::typeinfo;
|
||||
using base::value;
|
||||
|
||||
static handle
|
||||
cast(std::unique_ptr<type, deleter> &&src, return_value_policy policy, handle parent) {
|
||||
auto *ptr = src.get();
|
||||
auto st = type_caster_base<type>::src_and_type(ptr);
|
||||
if (st.second == nullptr) {
|
||||
return handle(); // no type info: error will be set already
|
||||
}
|
||||
if (st.second->holder_enum_v == detail::holder_enum_t::smart_holder) {
|
||||
return smart_holder_type_caster_support::smart_holder_from_unique_ptr(
|
||||
std::move(src), policy, parent, st);
|
||||
}
|
||||
return type_caster_generic::cast(st.first,
|
||||
return_value_policy::take_ownership,
|
||||
{},
|
||||
st.second,
|
||||
nullptr,
|
||||
nullptr,
|
||||
std::addressof(src));
|
||||
}
|
||||
|
||||
static handle
|
||||
cast(const std::unique_ptr<type, deleter> &src, return_value_policy policy, handle parent) {
|
||||
if (!src) {
|
||||
return none().release();
|
||||
}
|
||||
if (policy == return_value_policy::automatic) {
|
||||
policy = return_value_policy::reference_internal;
|
||||
}
|
||||
if (policy != return_value_policy::reference_internal) {
|
||||
throw cast_error("Invalid return_value_policy for unique_ptr&");
|
||||
}
|
||||
return type_caster_base<type>::cast(src.get(), policy, parent);
|
||||
}
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
return base::template load_impl<
|
||||
move_only_holder_caster<type, std::unique_ptr<type, deleter>>>(src, convert);
|
||||
}
|
||||
|
||||
void load_value(value_and_holder &&v_h) {
|
||||
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
|
||||
sh_load_helper.loaded_v_h = v_h;
|
||||
sh_load_helper.loaded_v_h.type = typeinfo;
|
||||
value = sh_load_helper.get_void_ptr_or_nullptr();
|
||||
return;
|
||||
}
|
||||
pybind11_fail(
|
||||
"Passing `std::unique_ptr<T>` from Python to C++ requires `py::classh` (with T = "
|
||||
+ clean_type_id(typeinfo->cpptype->name()) + ")");
|
||||
}
|
||||
|
||||
template <typename>
|
||||
using cast_op_type = std::unique_ptr<type, deleter>;
|
||||
|
||||
explicit operator std::unique_ptr<type, deleter>() {
|
||||
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
|
||||
return sh_load_helper.template load_as_unique_ptr<deleter>(value);
|
||||
}
|
||||
pybind11_fail("Expected to be UNREACHABLE: " __FILE__ ":" PYBIND11_TOSTRING(__LINE__));
|
||||
}
|
||||
|
||||
bool try_implicit_casts(handle src, bool convert) {
|
||||
for (auto &cast : typeinfo->implicit_casts) {
|
||||
move_only_holder_caster sub_caster(*cast.first);
|
||||
if (sub_caster.load(src, convert)) {
|
||||
value = cast.second(sub_caster.value);
|
||||
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
|
||||
sh_load_helper.loaded_v_h = sub_caster.sh_load_helper.loaded_v_h;
|
||||
} else {
|
||||
pybind11_fail("Expected to be UNREACHABLE: " __FILE__
|
||||
":" PYBIND11_TOSTRING(__LINE__));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool try_direct_conversions(handle) { return false; }
|
||||
|
||||
smart_holder_type_caster_support::load_helper<remove_cv_t<type>> sh_load_helper; // Const2Mutbl
|
||||
};
|
||||
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
|
||||
template <typename type, typename deleter>
|
||||
class type_caster<std::unique_ptr<type, deleter>>
|
||||
: public move_only_holder_caster<type, std::unique_ptr<type, deleter>> {};
|
||||
#endif
|
||||
|
||||
template <typename type, typename holder_type>
|
||||
using type_caster_holder = conditional_t<is_copy_constructible<holder_type>::value,
|
||||
@ -906,10 +1122,16 @@ struct always_construct_holder {
|
||||
template <typename base, typename holder>
|
||||
struct is_holder_type
|
||||
: std::is_base_of<detail::type_caster_holder<base, holder>, detail::type_caster<holder>> {};
|
||||
// Specialization for always-supported unique_ptr holders:
|
||||
|
||||
// Specializations for always-supported holders:
|
||||
template <typename base, typename deleter>
|
||||
struct is_holder_type<base, std::unique_ptr<base, deleter>> : std::true_type {};
|
||||
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
template <typename base>
|
||||
struct is_holder_type<base, smart_holder> : std::true_type {};
|
||||
#endif
|
||||
|
||||
#ifdef PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION // See PR #4888
|
||||
|
||||
// This leads to compilation errors if a specialization is missing.
|
||||
@ -1141,7 +1363,6 @@ template <typename type>
|
||||
using cast_is_temporary_value_reference
|
||||
= bool_constant<(std::is_reference<type>::value || std::is_pointer<type>::value)
|
||||
&& !std::is_base_of<type_caster_generic, make_caster<type>>::value
|
||||
&& !type_uses_smart_holder_type_caster<intrinsic_t<type>>::value
|
||||
&& !std::is_same<intrinsic_t<type>, void>::value>;
|
||||
|
||||
// When a value returned from a C++ function is being cast back to Python, we almost always want to
|
||||
@ -1155,9 +1376,7 @@ struct return_value_policy_override {
|
||||
template <typename Return>
|
||||
struct return_value_policy_override<
|
||||
Return,
|
||||
detail::enable_if_t<std::is_base_of<type_caster_generic, make_caster<Return>>::value
|
||||
|| type_uses_smart_holder_type_caster<intrinsic_t<Return>>::value,
|
||||
void>> {
|
||||
detail::enable_if_t<std::is_base_of<type_caster_generic, make_caster<Return>>::value, void>> {
|
||||
static return_value_policy policy(return_value_policy p) {
|
||||
return !std::is_lvalue_reference<Return>::value && !std::is_pointer<Return>::value
|
||||
? return_value_policy::move
|
||||
@ -1857,9 +2076,7 @@ PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
template <typename T>
|
||||
handle type::handle_of() {
|
||||
static_assert(
|
||||
detail::any_of<std::is_base_of<detail::type_caster_generic, detail::make_caster<T>>,
|
||||
detail::type_uses_smart_holder_type_caster<T>>::value,
|
||||
static_assert(std::is_base_of<detail::type_caster_generic, detail::make_caster<T>>::value,
|
||||
"py::type::of<T> only supports the case where T is a registered C++ types.");
|
||||
|
||||
return detail::get_type_handle(typeid(T), true);
|
||||
@ -1869,7 +2086,7 @@ handle type::handle_of() {
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) \
|
||||
namespace detail { \
|
||||
template <> \
|
||||
class type_caster<__VA_ARGS__> : public type_caster_for_class_<__VA_ARGS__> {}; \
|
||||
class type_caster<__VA_ARGS__> : public type_caster_base<__VA_ARGS__> {}; \
|
||||
} \
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
|
@ -9,13 +9,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define PYBIND11_VERSION_MAJOR 2
|
||||
#define PYBIND11_VERSION_MINOR 14
|
||||
#define PYBIND11_VERSION_MAJOR 3
|
||||
#define PYBIND11_VERSION_MINOR 0
|
||||
#define PYBIND11_VERSION_PATCH 0.dev1
|
||||
|
||||
// Similar to Python's convention: https://docs.python.org/3/c-api/apiabiversion.html
|
||||
// Additional convention: 0xD = dev
|
||||
#define PYBIND11_VERSION_HEX 0x020E00D1
|
||||
#define PYBIND11_VERSION_HEX 0x030000D1
|
||||
|
||||
// Define some generic pybind11 helper macros for warning management.
|
||||
//
|
||||
|
@ -10,7 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "class.h"
|
||||
#include "smart_holder_sfinae_hooks_only.h"
|
||||
#include "using_smart_holder.h"
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
@ -156,9 +156,7 @@ void construct(value_and_holder &v_h, Alias<Class> *alias_ptr, bool) {
|
||||
// holder. This also handles types like std::shared_ptr<T> and std::unique_ptr<T> where T is a
|
||||
// derived type (through those holder's implicit conversion from derived class holder
|
||||
// constructors).
|
||||
template <typename Class,
|
||||
detail::enable_if_t<!detail::type_uses_smart_holder_type_caster<Cpp<Class>>::value, int>
|
||||
= 0>
|
||||
template <typename Class, detail::enable_if_t<!is_smart_holder<Holder<Class>>::value, int> = 0>
|
||||
void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
|
||||
auto *ptr = holder_helper<Holder<Class>>::get(holder);
|
||||
@ -200,10 +198,18 @@ void construct(value_and_holder &v_h, Alias<Class> &&result, bool) {
|
||||
v_h.value_ptr() = new Alias<Class>(std::move(result));
|
||||
}
|
||||
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
|
||||
template <typename T, typename D>
|
||||
smart_holder init_smart_holder_from_unique_ptr(std::unique_ptr<T, D> &&unq_ptr,
|
||||
bool void_cast_raw_ptr) {
|
||||
void *void_ptr = void_cast_raw_ptr ? static_cast<void *>(unq_ptr.get()) : nullptr;
|
||||
return smart_holder::from_unique_ptr(std::move(unq_ptr), void_ptr);
|
||||
}
|
||||
|
||||
template <typename Class,
|
||||
typename D = std::default_delete<Cpp<Class>>,
|
||||
detail::enable_if_t<detail::type_uses_smart_holder_type_caster<Cpp<Class>>::value, int>
|
||||
= 0>
|
||||
detail::enable_if_t<is_smart_holder<Holder<Class>>::value, int> = 0>
|
||||
void construct(value_and_holder &v_h, std::unique_ptr<Cpp<Class>, D> &&unq_ptr, bool need_alias) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
|
||||
auto *ptr = unq_ptr.get();
|
||||
@ -217,7 +223,7 @@ void construct(value_and_holder &v_h, std::unique_ptr<Cpp<Class>, D> &&unq_ptr,
|
||||
// trampoline Python object alive. For types that don't inherit from enable_shared_from_this
|
||||
// it does not matter if void_cast_raw_ptr is true or false, therefore it's not necessary
|
||||
// to also inspect the type.
|
||||
auto smhldr = type_caster<Cpp<Class>>::smart_holder_from_unique_ptr(
|
||||
auto smhldr = init_smart_holder_from_unique_ptr(
|
||||
std::move(unq_ptr), /*void_cast_raw_ptr*/ Class::has_alias && is_alias<Class>(ptr));
|
||||
v_h.value_ptr() = ptr;
|
||||
v_h.type->init_instance(v_h.inst, &smhldr);
|
||||
@ -225,22 +231,19 @@ void construct(value_and_holder &v_h, std::unique_ptr<Cpp<Class>, D> &&unq_ptr,
|
||||
|
||||
template <typename Class,
|
||||
typename D = std::default_delete<Alias<Class>>,
|
||||
detail::enable_if_t<detail::type_uses_smart_holder_type_caster<Alias<Class>>::value, int>
|
||||
= 0>
|
||||
detail::enable_if_t<is_smart_holder<Holder<Class>>::value, int> = 0>
|
||||
void construct(value_and_holder &v_h,
|
||||
std::unique_ptr<Alias<Class>, D> &&unq_ptr,
|
||||
bool /*need_alias*/) {
|
||||
auto *ptr = unq_ptr.get();
|
||||
no_nullptr(ptr);
|
||||
auto smhldr = type_caster<Alias<Class>>::smart_holder_from_unique_ptr(
|
||||
std::move(unq_ptr), /*void_cast_raw_ptr*/ true);
|
||||
auto smhldr
|
||||
= init_smart_holder_from_unique_ptr(std::move(unq_ptr), /*void_cast_raw_ptr*/ true);
|
||||
v_h.value_ptr() = ptr;
|
||||
v_h.type->init_instance(v_h.inst, &smhldr);
|
||||
}
|
||||
|
||||
template <typename Class,
|
||||
detail::enable_if_t<detail::type_uses_smart_holder_type_caster<Cpp<Class>>::value, int>
|
||||
= 0>
|
||||
template <typename Class, detail::enable_if_t<is_smart_holder<Holder<Class>>::value, int> = 0>
|
||||
void construct(value_and_holder &v_h, std::shared_ptr<Cpp<Class>> &&shd_ptr, bool need_alias) {
|
||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
|
||||
auto *ptr = shd_ptr.get();
|
||||
@ -249,24 +252,24 @@ void construct(value_and_holder &v_h, std::shared_ptr<Cpp<Class>> &&shd_ptr, boo
|
||||
throw type_error("pybind11::init(): construction failed: returned std::shared_ptr pointee "
|
||||
"is not an alias instance");
|
||||
}
|
||||
auto smhldr = type_caster<Cpp<Class>>::smart_holder_from_shared_ptr(shd_ptr);
|
||||
auto smhldr = smart_holder::from_shared_ptr(shd_ptr);
|
||||
v_h.value_ptr() = ptr;
|
||||
v_h.type->init_instance(v_h.inst, &smhldr);
|
||||
}
|
||||
|
||||
template <typename Class,
|
||||
detail::enable_if_t<detail::type_uses_smart_holder_type_caster<Alias<Class>>::value, int>
|
||||
= 0>
|
||||
template <typename Class, detail::enable_if_t<is_smart_holder<Holder<Class>>::value, int> = 0>
|
||||
void construct(value_and_holder &v_h,
|
||||
std::shared_ptr<Alias<Class>> &&shd_ptr,
|
||||
bool /*need_alias*/) {
|
||||
auto *ptr = shd_ptr.get();
|
||||
no_nullptr(ptr);
|
||||
auto smhldr = type_caster<Alias<Class>>::smart_holder_from_shared_ptr(shd_ptr);
|
||||
auto smhldr = smart_holder::from_shared_ptr(shd_ptr);
|
||||
v_h.value_ptr() = ptr;
|
||||
v_h.type->init_instance(v_h.inst, &smhldr);
|
||||
}
|
||||
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
|
||||
// Implementing class for py::init<...>()
|
||||
template <typename... Args>
|
||||
struct constructor {
|
||||
|
@ -16,7 +16,6 @@
|
||||
#endif
|
||||
|
||||
#include "../pytypes.h"
|
||||
#include "smart_holder_sfinae_hooks_only.h"
|
||||
|
||||
#include <exception>
|
||||
#include <mutex>
|
||||
@ -37,7 +36,9 @@
|
||||
/// further ABI-incompatible changes may be made before the ABI is officially
|
||||
/// changed to the new version.
|
||||
#ifndef PYBIND11_INTERNALS_VERSION
|
||||
# if PY_VERSION_HEX >= 0x030C0000 || defined(_MSC_VER)
|
||||
# if PYBIND11_VERSION_MAJOR >= 3
|
||||
# define PYBIND11_INTERNALS_VERSION 6
|
||||
# elif PY_VERSION_HEX >= 0x030C0000 || defined(_MSC_VER)
|
||||
// Version bump for Python 3.12+, before first 3.12 beta release.
|
||||
// Version bump for MSVC piggy-backed on PR #4779. See comments there.
|
||||
# define PYBIND11_INTERNALS_VERSION 5
|
||||
@ -237,6 +238,20 @@ struct internals {
|
||||
}
|
||||
};
|
||||
|
||||
#if PYBIND11_INTERNALS_VERSION >= 6
|
||||
|
||||
# define PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
|
||||
enum class holder_enum_t : uint8_t {
|
||||
undefined,
|
||||
std_unique_ptr, // Default, lacking interop with std::shared_ptr.
|
||||
std_shared_ptr, // Lacking interop with std::unique_ptr.
|
||||
smart_holder, // Full std::unique_ptr / std::shared_ptr interop.
|
||||
custom_holder,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/// Additional type information which does not fit into the PyTypeObject.
|
||||
/// Changes to this struct also require bumping `PYBIND11_INTERNALS_VERSION`.
|
||||
struct type_info {
|
||||
@ -260,9 +275,14 @@ struct type_info {
|
||||
/* True if there is no multiple inheritance in this type's inheritance tree */
|
||||
bool simple_ancestors : 1;
|
||||
/* for base vs derived holder_type checks */
|
||||
// SMART_HOLDER_BAKEIN_FOLLOW_ON: Remove default_holder member here and
|
||||
// produce better error messages in the places where it is currently used.
|
||||
bool default_holder : 1;
|
||||
/* true if this is a type registered with py::module_local */
|
||||
bool module_local : 1;
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
holder_enum_t holder_enum_v = holder_enum_t::undefined;
|
||||
#endif
|
||||
};
|
||||
|
||||
/// On MSVC, debug and release builds are not ABI-compatible!
|
||||
@ -322,25 +342,15 @@ struct type_info {
|
||||
# define PYBIND11_INTERNALS_KIND ""
|
||||
#endif
|
||||
|
||||
/// See README_smart_holder.rst:
|
||||
/// Classic / Conservative / Progressive cross-module compatibility
|
||||
#ifndef PYBIND11_INTERNALS_SH_DEF
|
||||
# if defined(PYBIND11_USE_SMART_HOLDER_AS_DEFAULT)
|
||||
# define PYBIND11_INTERNALS_SH_DEF "_sh_def"
|
||||
# else
|
||||
# define PYBIND11_INTERNALS_SH_DEF ""
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define PYBIND11_INTERNALS_ID \
|
||||
"__pybind11_internals_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \
|
||||
PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \
|
||||
PYBIND11_BUILD_TYPE PYBIND11_INTERNALS_SH_DEF "__"
|
||||
PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB \
|
||||
PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__"
|
||||
|
||||
#define PYBIND11_MODULE_LOCAL_ID \
|
||||
"__pybind11_module_local_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \
|
||||
PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \
|
||||
PYBIND11_BUILD_TYPE PYBIND11_INTERNALS_SH_DEF "__"
|
||||
PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB \
|
||||
PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__"
|
||||
|
||||
/// Each module locally stores a pointer to the `internals` data. The data
|
||||
/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`.
|
||||
|
@ -1,46 +0,0 @@
|
||||
// Copyright (c) 2021 The Pybind Development Team.
|
||||
// All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#ifndef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
|
||||
// #define PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
|
||||
// Currently the main purpose of this switch is to enable non-intrusive comprehensive testing. If
|
||||
// and when `smart_holder` will actually become the released default is currently open. In the
|
||||
// meantime, the full functionality is easily available by using `py::classh`, which is just a
|
||||
// handy shortcut for `py::class_<T, py::smart_holder>` (see `pybind11/smart_holder.h`). Classes
|
||||
// wrapped in this way are fully compatible with everything existing.
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <typename T>
|
||||
struct is_smart_holder_type : std::false_type {};
|
||||
|
||||
// Tag to be used as base class, inspected by type_uses_smart_holder_type_caster<T> test.
|
||||
struct smart_holder_type_caster_base_tag {};
|
||||
|
||||
template <typename T>
|
||||
struct type_uses_smart_holder_type_caster;
|
||||
|
||||
// Simple helpers that may eventually be a better fit for another header file:
|
||||
|
||||
template <typename T>
|
||||
struct is_std_unique_ptr : std::false_type {};
|
||||
template <typename T, typename D>
|
||||
struct is_std_unique_ptr<std::unique_ptr<T, D>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_std_shared_ptr : std::false_type {};
|
||||
template <typename T>
|
||||
struct is_std_shared_ptr<std::shared_ptr<T>> : std::true_type {};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
File diff suppressed because it is too large
Load Diff
@ -9,11 +9,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../gil.h"
|
||||
#include "../pytypes.h"
|
||||
#include "../trampoline_self_life_support.h"
|
||||
#include "common.h"
|
||||
#include "descr.h"
|
||||
#include "dynamic_raw_ptr_cast_if_possible.h"
|
||||
#include "internals.h"
|
||||
#include "typeid.h"
|
||||
#include "using_smart_holder.h"
|
||||
#include "value_and_holder.h"
|
||||
|
||||
#include <cstdint>
|
||||
@ -468,6 +472,359 @@ inline PyThreadState *get_thread_state_unchecked() {
|
||||
void keep_alive_impl(handle nurse, handle patient);
|
||||
inline PyObject *make_new_instance(PyTypeObject *type);
|
||||
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
|
||||
// SMART_HOLDER_WIP: Needs refactoring of existing pybind11 code.
|
||||
inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo);
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(smart_holder_type_caster_support)
|
||||
|
||||
struct value_and_holder_helper {
|
||||
value_and_holder loaded_v_h;
|
||||
|
||||
bool have_holder() const {
|
||||
return loaded_v_h.vh != nullptr && loaded_v_h.holder_constructed();
|
||||
}
|
||||
|
||||
smart_holder &holder() const { return loaded_v_h.holder<smart_holder>(); }
|
||||
|
||||
void throw_if_uninitialized_or_disowned_holder(const char *typeid_name) const {
|
||||
static const std::string missing_value_msg = "Missing value for wrapped C++ type `";
|
||||
if (!holder().is_populated) {
|
||||
throw value_error(missing_value_msg + clean_type_id(typeid_name)
|
||||
+ "`: Python instance is uninitialized.");
|
||||
}
|
||||
if (!holder().has_pointee()) {
|
||||
throw value_error(missing_value_msg + clean_type_id(typeid_name)
|
||||
+ "`: Python instance was disowned.");
|
||||
}
|
||||
}
|
||||
|
||||
void throw_if_uninitialized_or_disowned_holder(const std::type_info &type_info) const {
|
||||
throw_if_uninitialized_or_disowned_holder(type_info.name());
|
||||
}
|
||||
|
||||
// have_holder() must be true or this function will fail.
|
||||
void throw_if_instance_is_currently_owned_by_shared_ptr() const {
|
||||
auto *vptr_gd_ptr = std::get_deleter<pybindit::memory::guarded_delete>(holder().vptr);
|
||||
if (vptr_gd_ptr != nullptr && !vptr_gd_ptr->released_ptr.expired()) {
|
||||
throw value_error("Python instance is currently owned by a std::shared_ptr.");
|
||||
}
|
||||
}
|
||||
|
||||
void *get_void_ptr_or_nullptr() const {
|
||||
if (have_holder()) {
|
||||
auto &hld = holder();
|
||||
if (hld.is_populated && hld.has_pointee()) {
|
||||
return hld.template as_raw_ptr_unowned<void>();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename D>
|
||||
handle smart_holder_from_unique_ptr(std::unique_ptr<T, D> &&src,
|
||||
return_value_policy policy,
|
||||
handle parent,
|
||||
const std::pair<const void *, const type_info *> &st) {
|
||||
if (policy == return_value_policy::copy) {
|
||||
throw cast_error("return_value_policy::copy is invalid for unique_ptr.");
|
||||
}
|
||||
if (!src) {
|
||||
return none().release();
|
||||
}
|
||||
void *src_raw_void_ptr = const_cast<void *>(st.first);
|
||||
assert(st.second != nullptr);
|
||||
const detail::type_info *tinfo = st.second;
|
||||
if (handle existing_inst = find_registered_python_instance(src_raw_void_ptr, tinfo)) {
|
||||
auto *self_life_support
|
||||
= dynamic_raw_ptr_cast_if_possible<trampoline_self_life_support>(src.get());
|
||||
if (self_life_support != nullptr) {
|
||||
value_and_holder &v_h = self_life_support->v_h;
|
||||
if (v_h.inst != nullptr && v_h.vh != nullptr) {
|
||||
auto &holder = v_h.holder<smart_holder>();
|
||||
if (!holder.is_disowned) {
|
||||
pybind11_fail("smart_holder_from_unique_ptr: unexpected "
|
||||
"smart_holder.is_disowned failure.");
|
||||
}
|
||||
// Critical transfer-of-ownership section. This must stay together.
|
||||
self_life_support->deactivate_life_support();
|
||||
holder.reclaim_disowned();
|
||||
(void) src.release();
|
||||
// Critical section end.
|
||||
return existing_inst;
|
||||
}
|
||||
}
|
||||
throw cast_error("Invalid unique_ptr: another instance owns this pointer already.");
|
||||
}
|
||||
|
||||
auto inst = reinterpret_steal<object>(make_new_instance(tinfo->type));
|
||||
auto *inst_raw_ptr = reinterpret_cast<instance *>(inst.ptr());
|
||||
inst_raw_ptr->owned = true;
|
||||
void *&valueptr = values_and_holders(inst_raw_ptr).begin()->value_ptr();
|
||||
valueptr = src_raw_void_ptr;
|
||||
|
||||
if (static_cast<void *>(src.get()) == src_raw_void_ptr) {
|
||||
// This is a multiple-inheritance situation that is incompatible with the current
|
||||
// shared_from_this handling (see PR #3023).
|
||||
// SMART_HOLDER_WIP: IMPROVABLE: Is there a better solution?
|
||||
src_raw_void_ptr = nullptr;
|
||||
}
|
||||
auto smhldr = smart_holder::from_unique_ptr(std::move(src), src_raw_void_ptr);
|
||||
tinfo->init_instance(inst_raw_ptr, static_cast<const void *>(&smhldr));
|
||||
|
||||
if (policy == return_value_policy::reference_internal) {
|
||||
keep_alive_impl(inst, parent);
|
||||
}
|
||||
|
||||
return inst.release();
|
||||
}
|
||||
|
||||
template <typename T, typename D>
|
||||
handle smart_holder_from_unique_ptr(std::unique_ptr<T const, D> &&src,
|
||||
return_value_policy policy,
|
||||
handle parent,
|
||||
const std::pair<const void *, const type_info *> &st) {
|
||||
return smart_holder_from_unique_ptr(
|
||||
std::unique_ptr<T, D>(const_cast<T *>(src.release()),
|
||||
std::move(src.get_deleter())), // Const2Mutbl
|
||||
policy,
|
||||
parent,
|
||||
st);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
handle smart_holder_from_shared_ptr(const std::shared_ptr<T> &src,
|
||||
return_value_policy policy,
|
||||
handle parent,
|
||||
const std::pair<const void *, const type_info *> &st) {
|
||||
switch (policy) {
|
||||
case return_value_policy::automatic:
|
||||
case return_value_policy::automatic_reference:
|
||||
break;
|
||||
case return_value_policy::take_ownership:
|
||||
throw cast_error("Invalid return_value_policy for shared_ptr (take_ownership).");
|
||||
case return_value_policy::copy:
|
||||
case return_value_policy::move:
|
||||
break;
|
||||
case return_value_policy::reference:
|
||||
throw cast_error("Invalid return_value_policy for shared_ptr (reference).");
|
||||
case return_value_policy::reference_internal:
|
||||
break;
|
||||
}
|
||||
if (!src) {
|
||||
return none().release();
|
||||
}
|
||||
|
||||
auto src_raw_ptr = src.get();
|
||||
assert(st.second != nullptr);
|
||||
void *src_raw_void_ptr = static_cast<void *>(src_raw_ptr);
|
||||
const detail::type_info *tinfo = st.second;
|
||||
if (handle existing_inst = find_registered_python_instance(src_raw_void_ptr, tinfo)) {
|
||||
// SMART_HOLDER_WIP: MISSING: Enforcement of consistency with existing smart_holder.
|
||||
// SMART_HOLDER_WIP: MISSING: keep_alive.
|
||||
return existing_inst;
|
||||
}
|
||||
|
||||
auto inst = reinterpret_steal<object>(make_new_instance(tinfo->type));
|
||||
auto *inst_raw_ptr = reinterpret_cast<instance *>(inst.ptr());
|
||||
inst_raw_ptr->owned = true;
|
||||
void *&valueptr = values_and_holders(inst_raw_ptr).begin()->value_ptr();
|
||||
valueptr = src_raw_void_ptr;
|
||||
|
||||
auto smhldr
|
||||
= smart_holder::from_shared_ptr(std::shared_ptr<void>(src, const_cast<void *>(st.first)));
|
||||
tinfo->init_instance(inst_raw_ptr, static_cast<const void *>(&smhldr));
|
||||
|
||||
if (policy == return_value_policy::reference_internal) {
|
||||
keep_alive_impl(inst, parent);
|
||||
}
|
||||
|
||||
return inst.release();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
handle smart_holder_from_shared_ptr(const std::shared_ptr<T const> &src,
|
||||
return_value_policy policy,
|
||||
handle parent,
|
||||
const std::pair<const void *, const type_info *> &st) {
|
||||
return smart_holder_from_shared_ptr(std::const_pointer_cast<T>(src), // Const2Mutbl
|
||||
policy,
|
||||
parent,
|
||||
st);
|
||||
}
|
||||
|
||||
struct shared_ptr_parent_life_support {
|
||||
PyObject *parent;
|
||||
explicit shared_ptr_parent_life_support(PyObject *parent) : parent{parent} {
|
||||
Py_INCREF(parent);
|
||||
}
|
||||
// NOLINTNEXTLINE(readability-make-member-function-const)
|
||||
void operator()(void *) {
|
||||
gil_scoped_acquire gil;
|
||||
Py_DECREF(parent);
|
||||
}
|
||||
};
|
||||
|
||||
struct shared_ptr_trampoline_self_life_support {
|
||||
PyObject *self;
|
||||
explicit shared_ptr_trampoline_self_life_support(instance *inst)
|
||||
: self{reinterpret_cast<PyObject *>(inst)} {
|
||||
gil_scoped_acquire gil;
|
||||
Py_INCREF(self);
|
||||
}
|
||||
// NOLINTNEXTLINE(readability-make-member-function-const)
|
||||
void operator()(void *) {
|
||||
gil_scoped_acquire gil;
|
||||
Py_DECREF(self);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T,
|
||||
typename D,
|
||||
typename std::enable_if<std::is_default_constructible<D>::value, int>::type = 0>
|
||||
inline std::unique_ptr<T, D> unique_with_deleter(T *raw_ptr, std::unique_ptr<D> &&deleter) {
|
||||
if (deleter == nullptr) {
|
||||
return std::unique_ptr<T, D>(raw_ptr);
|
||||
}
|
||||
return std::unique_ptr<T, D>(raw_ptr, std::move(*deleter));
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename D,
|
||||
typename std::enable_if<!std::is_default_constructible<D>::value, int>::type = 0>
|
||||
inline std::unique_ptr<T, D> unique_with_deleter(T *raw_ptr, std::unique_ptr<D> &&deleter) {
|
||||
if (deleter == nullptr) {
|
||||
pybind11_fail("smart_holder_type_casters: deleter is not default constructible and no"
|
||||
" instance available to return.");
|
||||
}
|
||||
return std::unique_ptr<T, D>(raw_ptr, std::move(*deleter));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct load_helper : value_and_holder_helper {
|
||||
static std::shared_ptr<T> make_shared_ptr_with_responsible_parent(T *raw_ptr, handle parent) {
|
||||
return std::shared_ptr<T>(raw_ptr, shared_ptr_parent_life_support(parent.ptr()));
|
||||
}
|
||||
|
||||
std::shared_ptr<T> load_as_shared_ptr(void *void_raw_ptr,
|
||||
handle responsible_parent = nullptr) const {
|
||||
if (!have_holder()) {
|
||||
return nullptr;
|
||||
}
|
||||
throw_if_uninitialized_or_disowned_holder(typeid(T));
|
||||
smart_holder &hld = holder();
|
||||
hld.ensure_is_not_disowned("load_as_shared_ptr");
|
||||
if (hld.vptr_is_using_noop_deleter) {
|
||||
if (responsible_parent) {
|
||||
return make_shared_ptr_with_responsible_parent(static_cast<T *>(void_raw_ptr),
|
||||
responsible_parent);
|
||||
}
|
||||
throw std::runtime_error("Non-owning holder (load_as_shared_ptr).");
|
||||
}
|
||||
auto *type_raw_ptr = static_cast<T *>(void_raw_ptr);
|
||||
if (hld.pointee_depends_on_holder_owner) {
|
||||
auto *vptr_gd_ptr = std::get_deleter<pybindit::memory::guarded_delete>(hld.vptr);
|
||||
if (vptr_gd_ptr != nullptr) {
|
||||
std::shared_ptr<void> released_ptr = vptr_gd_ptr->released_ptr.lock();
|
||||
if (released_ptr) {
|
||||
return std::shared_ptr<T>(released_ptr, type_raw_ptr);
|
||||
}
|
||||
std::shared_ptr<T> to_be_released(
|
||||
type_raw_ptr, shared_ptr_trampoline_self_life_support(loaded_v_h.inst));
|
||||
vptr_gd_ptr->released_ptr = to_be_released;
|
||||
return to_be_released;
|
||||
}
|
||||
auto *sptsls_ptr = std::get_deleter<shared_ptr_trampoline_self_life_support>(hld.vptr);
|
||||
if (sptsls_ptr != nullptr) {
|
||||
// This code is reachable only if there are multiple registered_instances for the
|
||||
// same pointee.
|
||||
if (reinterpret_cast<PyObject *>(loaded_v_h.inst) == sptsls_ptr->self) {
|
||||
pybind11_fail("smart_holder_type_caster_support load_as_shared_ptr failure: "
|
||||
"loaded_v_h.inst == sptsls_ptr->self");
|
||||
}
|
||||
}
|
||||
if (sptsls_ptr != nullptr
|
||||
|| !pybindit::memory::type_has_shared_from_this(type_raw_ptr)) {
|
||||
return std::shared_ptr<T>(
|
||||
type_raw_ptr, shared_ptr_trampoline_self_life_support(loaded_v_h.inst));
|
||||
}
|
||||
if (hld.vptr_is_external_shared_ptr) {
|
||||
pybind11_fail("smart_holder_type_casters load_as_shared_ptr failure: not "
|
||||
"implemented: trampoline-self-life-support for external shared_ptr "
|
||||
"to type inheriting from std::enable_shared_from_this.");
|
||||
}
|
||||
pybind11_fail(
|
||||
"smart_holder_type_casters: load_as_shared_ptr failure: internal inconsistency.");
|
||||
}
|
||||
std::shared_ptr<void> void_shd_ptr = hld.template as_shared_ptr<void>();
|
||||
return std::shared_ptr<T>(void_shd_ptr, type_raw_ptr);
|
||||
}
|
||||
|
||||
template <typename D>
|
||||
std::unique_ptr<T, D> load_as_unique_ptr(void *raw_void_ptr,
|
||||
const char *context = "load_as_unique_ptr") {
|
||||
if (!have_holder()) {
|
||||
return unique_with_deleter<T, D>(nullptr, std::unique_ptr<D>());
|
||||
}
|
||||
throw_if_uninitialized_or_disowned_holder(typeid(T));
|
||||
throw_if_instance_is_currently_owned_by_shared_ptr();
|
||||
holder().ensure_is_not_disowned(context);
|
||||
holder().template ensure_compatible_rtti_uqp_del<T, D>(context);
|
||||
holder().ensure_use_count_1(context);
|
||||
|
||||
T *raw_type_ptr = static_cast<T *>(raw_void_ptr);
|
||||
|
||||
auto *self_life_support
|
||||
= dynamic_raw_ptr_cast_if_possible<trampoline_self_life_support>(raw_type_ptr);
|
||||
if (self_life_support == nullptr && holder().pointee_depends_on_holder_owner) {
|
||||
throw value_error("Alias class (also known as trampoline) does not inherit from "
|
||||
"py::trampoline_self_life_support, therefore the ownership of this "
|
||||
"instance cannot safely be transferred to C++.");
|
||||
}
|
||||
|
||||
// Temporary variable to store the extracted deleter in.
|
||||
std::unique_ptr<D> extracted_deleter;
|
||||
|
||||
auto *gd = std::get_deleter<pybindit::memory::guarded_delete>(holder().vptr);
|
||||
if (gd && gd->use_del_fun) { // Note the ensure_compatible_rtti_uqp_del<T, D>() call above.
|
||||
// In smart_holder_poc, a custom deleter is always stored in a guarded delete.
|
||||
// The guarded delete's std::function<void(void*)> actually points at the
|
||||
// custom_deleter type, so we can verify it is of the custom deleter type and
|
||||
// finally extract its deleter.
|
||||
using custom_deleter_D = pybindit::memory::custom_deleter<T, D>;
|
||||
const auto &custom_deleter_ptr = gd->del_fun.template target<custom_deleter_D>();
|
||||
assert(custom_deleter_ptr != nullptr);
|
||||
// Now that we have confirmed the type of the deleter matches the desired return
|
||||
// value we can extract the function.
|
||||
extracted_deleter = std::unique_ptr<D>(new D(std::move(custom_deleter_ptr->deleter)));
|
||||
}
|
||||
|
||||
// Critical transfer-of-ownership section. This must stay together.
|
||||
if (self_life_support != nullptr) {
|
||||
holder().disown();
|
||||
} else {
|
||||
holder().release_ownership();
|
||||
}
|
||||
auto result = unique_with_deleter<T, D>(raw_type_ptr, std::move(extracted_deleter));
|
||||
if (self_life_support != nullptr) {
|
||||
self_life_support->activate_life_support(loaded_v_h);
|
||||
} else {
|
||||
void *value_void_ptr = loaded_v_h.value_ptr();
|
||||
loaded_v_h.value_ptr() = nullptr;
|
||||
deregister_instance(loaded_v_h.inst, value_void_ptr, loaded_v_h.type);
|
||||
}
|
||||
// Critical section end.
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(smart_holder_type_caster_support)
|
||||
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
|
||||
class type_caster_generic {
|
||||
public:
|
||||
PYBIND11_NOINLINE explicit type_caster_generic(const std::type_info &type_info)
|
||||
@ -572,6 +929,17 @@ public:
|
||||
|
||||
// Base methods for generic caster; there are overridden in copyable_holder_caster
|
||||
void load_value(value_and_holder &&v_h) {
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
if (typeinfo->holder_enum_v == detail::holder_enum_t::smart_holder) {
|
||||
smart_holder_type_caster_support::value_and_holder_helper v_h_helper;
|
||||
v_h_helper.loaded_v_h = v_h;
|
||||
if (v_h_helper.have_holder()) {
|
||||
v_h_helper.throw_if_uninitialized_or_disowned_holder(cpptype->name());
|
||||
value = v_h_helper.holder().template as_raw_ptr_unowned<void>();
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
auto *&vptr = v_h.value_ptr();
|
||||
// Lazy allocation for unallocated values:
|
||||
if (vptr == nullptr) {
|
||||
|
33
include/pybind11/detail/using_smart_holder.h
Normal file
33
include/pybind11/detail/using_smart_holder.h
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2024 The Pybind Development Team.
|
||||
// All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "internals.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
# include "smart_holder_poc.h"
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
using pybindit::memory::smart_holder;
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
template <typename H>
|
||||
using is_smart_holder = std::is_same<H, smart_holder>;
|
||||
#else
|
||||
template <typename>
|
||||
struct is_smart_holder : std::false_type {};
|
||||
#endif
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
@ -11,8 +11,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "detail/class.h"
|
||||
#include "detail/dynamic_raw_ptr_cast_if_possible.h"
|
||||
#include "detail/init.h"
|
||||
#include "detail/smart_holder_sfinae_hooks_only.h"
|
||||
#include "detail/using_smart_holder.h"
|
||||
#include "attr.h"
|
||||
#include "gil.h"
|
||||
#include "gil_safe_call_once.h"
|
||||
@ -1394,8 +1395,7 @@ class generic_type : public object {
|
||||
public:
|
||||
PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check)
|
||||
protected:
|
||||
void initialize(const type_record &rec,
|
||||
void *(*type_caster_module_local_load)(PyObject *, const type_info *) ) {
|
||||
void initialize(const type_record &rec) {
|
||||
if (rec.scope && hasattr(rec.scope, "__dict__")
|
||||
&& rec.scope.attr("__dict__").contains(rec.name)) {
|
||||
pybind11_fail("generic_type: cannot initialize type \"" + std::string(rec.name)
|
||||
@ -1424,6 +1424,9 @@ protected:
|
||||
tinfo->simple_ancestors = true;
|
||||
tinfo->default_holder = rec.default_holder;
|
||||
tinfo->module_local = rec.module_local;
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
tinfo->holder_enum_v = rec.holder_enum_v;
|
||||
#endif
|
||||
|
||||
with_internals([&](internals &internals) {
|
||||
auto tindex = std::type_index(*rec.type);
|
||||
@ -1450,7 +1453,7 @@ protected:
|
||||
|
||||
if (rec.module_local) {
|
||||
// Stash the local typeinfo and loader so that external modules can access it.
|
||||
tinfo->module_local_load = type_caster_module_local_load;
|
||||
tinfo->module_local_load = &type_caster_generic::local_load;
|
||||
setattr(m_ptr, PYBIND11_MODULE_LOCAL_ID, capsule(tinfo));
|
||||
}
|
||||
}
|
||||
@ -1586,49 +1589,7 @@ auto method_adaptor(Return (Class::*pmf)(Args...) const) -> Return (Derived::*)(
|
||||
return pmf;
|
||||
}
|
||||
|
||||
#ifndef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
|
||||
|
||||
template <typename T>
|
||||
using default_holder_type = std::unique_ptr<T>;
|
||||
|
||||
# ifndef PYBIND11_SH_AVL
|
||||
# define PYBIND11_SH_AVL(...) std::shared_ptr<__VA_ARGS__> // "Smart_Holder if AVaiLable"
|
||||
// -------- std::shared_ptr(...) -- same length by design, to not disturb the indentation
|
||||
// of existing code.
|
||||
# endif
|
||||
|
||||
# define PYBIND11_SH_DEF(...) std::shared_ptr<__VA_ARGS__> // "Smart_Holder if DEFault"
|
||||
// -------- std::shared_ptr(...) -- same length by design, to not disturb the indentation
|
||||
// of existing code.
|
||||
|
||||
# define PYBIND11_TYPE_CASTER_BASE_HOLDER(T, ...)
|
||||
|
||||
#else
|
||||
|
||||
template <typename>
|
||||
using default_holder_type = smart_holder;
|
||||
|
||||
# ifndef PYBIND11_SH_AVL
|
||||
# define PYBIND11_SH_AVL(...) ::pybind11::smart_holder // "Smart_Holder if AVaiLable"
|
||||
// -------- std::shared_ptr(...) -- same length by design, to not disturb the indentation
|
||||
// of existing code.
|
||||
# endif
|
||||
|
||||
# define PYBIND11_SH_DEF(...) ::pybind11::smart_holder // "Smart_Holder if DEFault"
|
||||
|
||||
// This define could be hidden away inside detail/smart_holder_type_casters.h, but is kept here
|
||||
// for clarity.
|
||||
# define PYBIND11_TYPE_CASTER_BASE_HOLDER(T, ...) \
|
||||
namespace pybind11 { \
|
||||
namespace detail { \
|
||||
template <> \
|
||||
class type_caster<T> : public type_caster_base<T> {}; \
|
||||
template <> \
|
||||
class type_caster<__VA_ARGS__> : public type_caster_holder<T, __VA_ARGS__> {}; \
|
||||
} \
|
||||
}
|
||||
|
||||
#endif
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
// Helper for the property_cpp_function static member functions below.
|
||||
// The only purpose of these functions is to support .def_readonly & .def_readwrite.
|
||||
@ -1637,8 +1598,7 @@ using default_holder_type = smart_holder;
|
||||
// against accidents. As a side-effect, it also explains why the syntactical overhead for
|
||||
// perfect forwarding is not needed.
|
||||
template <typename PM>
|
||||
using must_be_member_function_pointer
|
||||
= detail::enable_if_t<std::is_member_pointer<PM>::value, int>;
|
||||
using must_be_member_function_pointer = enable_if_t<std::is_member_pointer<PM>::value, int>;
|
||||
|
||||
// Note that property_cpp_function is intentionally in the main pybind11 namespace,
|
||||
// because user-defined specializations could be useful.
|
||||
@ -1647,9 +1607,9 @@ using must_be_member_function_pointer
|
||||
// getter and setter functions.
|
||||
// WARNING: This classic implementation can lead to dangling pointers for raw pointer members.
|
||||
// See test_ptr() in tests/test_class_sh_property.py
|
||||
// This implementation works as-is (and safely) for smart_holder std::shared_ptr members.
|
||||
template <typename T, typename D, typename SFINAE = void>
|
||||
struct property_cpp_function {
|
||||
// However, this implementation works as-is (and safely) for smart_holder std::shared_ptr members.
|
||||
template <typename T, typename D>
|
||||
struct property_cpp_function_classic {
|
||||
template <typename PM, must_be_member_function_pointer<PM> = 0>
|
||||
static cpp_function readonly(PM pm, const handle &hdl) {
|
||||
return cpp_function([pm](const T &c) -> const D & { return c.*pm; }, is_method(hdl));
|
||||
@ -1666,32 +1626,55 @@ struct property_cpp_function {
|
||||
}
|
||||
};
|
||||
|
||||
// smart_holder specializations for raw pointer members.
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
template <typename T, typename D, typename SFINAE = void>
|
||||
struct property_cpp_function : detail::property_cpp_function_classic<T, D> {};
|
||||
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <typename T, typename D, typename SFINAE = void>
|
||||
struct both_t_and_d_use_type_caster_base : std::false_type {};
|
||||
|
||||
// `T` is assumed to be equivalent to `intrinsic_t<T>`.
|
||||
// `D` is may or may not be equivalent to `intrinsic_t<D>`.
|
||||
template <typename T, typename D>
|
||||
struct both_t_and_d_use_type_caster_base<
|
||||
T,
|
||||
D,
|
||||
enable_if_t<all_of<std::is_base_of<type_caster_base<T>, type_caster<T>>,
|
||||
std::is_base_of<type_caster_base<intrinsic_t<D>>, make_caster<D>>>::value>>
|
||||
: std::true_type {};
|
||||
|
||||
// Specialization for raw pointer members, using smart_holder if that is the class_ holder,
|
||||
// or falling back to the classic implementation if not.
|
||||
// WARNING: Like the classic implementation, this implementation can lead to dangling pointers.
|
||||
// See test_ptr() in tests/test_class_sh_property.py
|
||||
// However, the read functions return a shared_ptr to the member, emulating the PyCLIF approach:
|
||||
// https://github.com/google/clif/blob/c371a6d4b28d25d53a16e6d2a6d97305fb1be25a/clif/python/instance.h#L233
|
||||
// This prevents disowning of the Python object owning the raw pointer member.
|
||||
template <typename T, typename D>
|
||||
struct property_cpp_function<
|
||||
T,
|
||||
D,
|
||||
detail::enable_if_t<detail::all_of<detail::type_uses_smart_holder_type_caster<T>,
|
||||
detail::type_uses_smart_holder_type_caster<D>,
|
||||
std::is_pointer<D>>::value>> {
|
||||
|
||||
struct property_cpp_function_sh_raw_ptr_member {
|
||||
using drp = typename std::remove_pointer<D>::type;
|
||||
|
||||
template <typename PM, must_be_member_function_pointer<PM> = 0>
|
||||
static cpp_function readonly(PM pm, const handle &hdl) {
|
||||
type_info *tinfo = get_type_info(typeid(T), /*throw_if_missing=*/true);
|
||||
if (tinfo->holder_enum_v == holder_enum_t::smart_holder) {
|
||||
return cpp_function(
|
||||
[pm](handle c_hdl) -> std::shared_ptr<drp> {
|
||||
std::shared_ptr<T> c_sp = detail::type_caster<T>::shared_ptr_from_python(c_hdl);
|
||||
std::shared_ptr<T> c_sp
|
||||
= type_caster<std::shared_ptr<T>>::shared_ptr_with_responsible_parent(
|
||||
c_hdl);
|
||||
D ptr = (*c_sp).*pm;
|
||||
return std::shared_ptr<drp>(c_sp, ptr);
|
||||
},
|
||||
is_method(hdl));
|
||||
}
|
||||
return property_cpp_function_classic<T, D>::readonly(pm, hdl);
|
||||
}
|
||||
|
||||
template <typename PM, must_be_member_function_pointer<PM> = 0>
|
||||
static cpp_function read(PM pm, const handle &hdl) {
|
||||
@ -1700,82 +1683,96 @@ struct property_cpp_function<
|
||||
|
||||
template <typename PM, must_be_member_function_pointer<PM> = 0>
|
||||
static cpp_function write(PM pm, const handle &hdl) {
|
||||
return cpp_function([pm](T &c, D value) { c.*pm = std::forward<D>(value); },
|
||||
type_info *tinfo = get_type_info(typeid(T), /*throw_if_missing=*/true);
|
||||
if (tinfo->holder_enum_v == holder_enum_t::smart_holder) {
|
||||
return cpp_function([pm](T &c, D value) { c.*pm = std::forward<D>(std::move(value)); },
|
||||
is_method(hdl));
|
||||
}
|
||||
return property_cpp_function_classic<T, D>::write(pm, hdl);
|
||||
}
|
||||
};
|
||||
|
||||
// smart_holder specializations for members held by-value.
|
||||
// Specialization for members held by-value, using smart_holder if that is the class_ holder,
|
||||
// or falling back to the classic implementation if not.
|
||||
// The read functions return a shared_ptr to the member, emulating the PyCLIF approach:
|
||||
// https://github.com/google/clif/blob/c371a6d4b28d25d53a16e6d2a6d97305fb1be25a/clif/python/instance.h#L233
|
||||
// This prevents disowning of the Python object owning the member.
|
||||
template <typename T, typename D>
|
||||
struct property_cpp_function<
|
||||
T,
|
||||
D,
|
||||
detail::enable_if_t<detail::all_of<detail::type_uses_smart_holder_type_caster<T>,
|
||||
detail::type_uses_smart_holder_type_caster<D>,
|
||||
detail::none_of<std::is_pointer<D>,
|
||||
detail::is_std_unique_ptr<D>,
|
||||
detail::is_std_shared_ptr<D>>>::value>> {
|
||||
|
||||
struct property_cpp_function_sh_member_held_by_value {
|
||||
template <typename PM, must_be_member_function_pointer<PM> = 0>
|
||||
static cpp_function readonly(PM pm, const handle &hdl) {
|
||||
type_info *tinfo = get_type_info(typeid(T), /*throw_if_missing=*/true);
|
||||
if (tinfo->holder_enum_v == holder_enum_t::smart_holder) {
|
||||
return cpp_function(
|
||||
[pm](handle c_hdl) -> std::shared_ptr<typename std::add_const<D>::type> {
|
||||
std::shared_ptr<T> c_sp = detail::type_caster<T>::shared_ptr_from_python(c_hdl);
|
||||
return std::shared_ptr<typename std::add_const<D>::type>(c_sp, &(c_sp.get()->*pm));
|
||||
std::shared_ptr<T> c_sp
|
||||
= type_caster<std::shared_ptr<T>>::shared_ptr_with_responsible_parent(
|
||||
c_hdl);
|
||||
return std::shared_ptr<typename std::add_const<D>::type>(c_sp,
|
||||
&(c_sp.get()->*pm));
|
||||
},
|
||||
is_method(hdl));
|
||||
}
|
||||
return property_cpp_function_classic<T, D>::readonly(pm, hdl);
|
||||
}
|
||||
|
||||
template <typename PM, must_be_member_function_pointer<PM> = 0>
|
||||
static cpp_function read(PM pm, const handle &hdl) {
|
||||
type_info *tinfo = get_type_info(typeid(T), /*throw_if_missing=*/true);
|
||||
if (tinfo->holder_enum_v == holder_enum_t::smart_holder) {
|
||||
return cpp_function(
|
||||
[pm](handle c_hdl) -> std::shared_ptr<D> {
|
||||
std::shared_ptr<T> c_sp = detail::type_caster<T>::shared_ptr_from_python(c_hdl);
|
||||
std::shared_ptr<T> c_sp
|
||||
= type_caster<std::shared_ptr<T>>::shared_ptr_with_responsible_parent(
|
||||
c_hdl);
|
||||
return std::shared_ptr<D>(c_sp, &(c_sp.get()->*pm));
|
||||
},
|
||||
is_method(hdl));
|
||||
}
|
||||
return property_cpp_function_classic<T, D>::read(pm, hdl);
|
||||
}
|
||||
|
||||
template <typename PM, must_be_member_function_pointer<PM> = 0>
|
||||
static cpp_function write(PM pm, const handle &hdl) {
|
||||
type_info *tinfo = get_type_info(typeid(T), /*throw_if_missing=*/true);
|
||||
if (tinfo->holder_enum_v == holder_enum_t::smart_holder) {
|
||||
return cpp_function([pm](T &c, const D &value) { c.*pm = value; }, is_method(hdl));
|
||||
}
|
||||
return property_cpp_function_classic<T, D>::write(pm, hdl);
|
||||
}
|
||||
};
|
||||
|
||||
// smart_holder specializations for std::unique_ptr members.
|
||||
// Specialization for std::unique_ptr members, using smart_holder if that is the class_ holder,
|
||||
// or falling back to the classic implementation if not.
|
||||
// read disowns the member unique_ptr.
|
||||
// write disowns the passed Python object.
|
||||
// readonly is disabled (static_assert) because there is no safe & intuitive way to make the member
|
||||
// accessible as a Python object without disowning the member unique_ptr. A .def_readonly disowning
|
||||
// the unique_ptr member is deemed highly prone to misunderstandings.
|
||||
template <typename T, typename D>
|
||||
struct property_cpp_function<
|
||||
T,
|
||||
D,
|
||||
detail::enable_if_t<detail::all_of<
|
||||
detail::type_uses_smart_holder_type_caster<T>,
|
||||
detail::is_std_unique_ptr<D>,
|
||||
detail::type_uses_smart_holder_type_caster<typename D::element_type>>::value>> {
|
||||
|
||||
struct property_cpp_function_sh_unique_ptr_member {
|
||||
template <typename PM, must_be_member_function_pointer<PM> = 0>
|
||||
static cpp_function readonly(PM, const handle &) {
|
||||
static_assert(!detail::is_std_unique_ptr<D>::value,
|
||||
static_assert(!is_instantiation<std::unique_ptr, D>::value,
|
||||
"def_readonly cannot be used for std::unique_ptr members.");
|
||||
return cpp_function{}; // Unreachable.
|
||||
}
|
||||
|
||||
template <typename PM, must_be_member_function_pointer<PM> = 0>
|
||||
static cpp_function read(PM pm, const handle &hdl) {
|
||||
type_info *tinfo = get_type_info(typeid(T), /*throw_if_missing=*/true);
|
||||
if (tinfo->holder_enum_v == holder_enum_t::smart_holder) {
|
||||
return cpp_function(
|
||||
[pm](handle c_hdl) -> D {
|
||||
std::shared_ptr<T> c_sp = detail::type_caster<T>::shared_ptr_from_python(c_hdl);
|
||||
std::shared_ptr<T> c_sp
|
||||
= type_caster<std::shared_ptr<T>>::shared_ptr_with_responsible_parent(
|
||||
c_hdl);
|
||||
return D{std::move(c_sp.get()->*pm)};
|
||||
},
|
||||
is_method(hdl));
|
||||
}
|
||||
return property_cpp_function_classic<T, D>::read(pm, hdl);
|
||||
}
|
||||
|
||||
template <typename PM, must_be_member_function_pointer<PM> = 0>
|
||||
static cpp_function write(PM pm, const handle &hdl) {
|
||||
@ -1783,18 +1780,63 @@ struct property_cpp_function<
|
||||
}
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(detail)
|
||||
|
||||
template <typename T, typename D>
|
||||
struct property_cpp_function<
|
||||
T,
|
||||
D,
|
||||
detail::enable_if_t<detail::all_of<std::is_pointer<D>,
|
||||
detail::both_t_and_d_use_type_caster_base<T, D>>::value>>
|
||||
: detail::property_cpp_function_sh_raw_ptr_member<T, D> {};
|
||||
|
||||
template <typename T, typename D>
|
||||
struct property_cpp_function<T,
|
||||
D,
|
||||
detail::enable_if_t<detail::all_of<
|
||||
detail::none_of<std::is_pointer<D>,
|
||||
std::is_array<D>,
|
||||
detail::is_instantiation<std::unique_ptr, D>,
|
||||
detail::is_instantiation<std::shared_ptr, D>>,
|
||||
detail::both_t_and_d_use_type_caster_base<T, D>>::value>>
|
||||
: detail::property_cpp_function_sh_member_held_by_value<T, D> {};
|
||||
|
||||
template <typename T, typename D>
|
||||
struct property_cpp_function<
|
||||
T,
|
||||
D,
|
||||
detail::enable_if_t<detail::all_of<
|
||||
detail::is_instantiation<std::unique_ptr, D>,
|
||||
detail::both_t_and_d_use_type_caster_base<T, typename D::element_type>>::value>>
|
||||
: detail::property_cpp_function_sh_unique_ptr_member<T, D> {};
|
||||
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
|
||||
#if defined(PYBIND11_USE_SMART_HOLDER_AS_DEFAULT) \
|
||||
&& defined(PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT)
|
||||
// NOTE: THIS IS MEANT FOR STRESS-TESTING ONLY!
|
||||
// As of PR #5257, for production use, there is no longer a strong reason to make
|
||||
// smart_holder the default holder:
|
||||
// Simply use `py::classh` (see below) instead of `py::class_` as needed.
|
||||
// Running the pybind11 unit tests with smart_holder as the default holder is to ensure
|
||||
// that `py::smart_holder` / `py::classh` is backward-compatible with all pre-existing
|
||||
// functionality.
|
||||
# define PYBIND11_ACTUALLY_USING_SMART_HOLDER_AS_DEFAULT
|
||||
template <typename>
|
||||
using default_holder_type = smart_holder;
|
||||
#else
|
||||
template <typename T>
|
||||
using default_holder_type = std::unique_ptr<T>;
|
||||
#endif
|
||||
|
||||
template <typename type_, typename... options>
|
||||
class class_ : public detail::generic_type {
|
||||
template <typename T>
|
||||
using is_holder = detail::is_holder_type<type_, T>;
|
||||
template <typename T>
|
||||
using is_subtype = detail::is_strict_base_of<type_, T>;
|
||||
template <typename T>
|
||||
using is_base = detail::is_strict_base_of<T, type_>;
|
||||
template <typename T>
|
||||
using is_holder
|
||||
= detail::any_of<detail::is_holder_type<type_, T>,
|
||||
detail::all_of<detail::negation<is_base<T>>,
|
||||
detail::negation<is_subtype<T>>,
|
||||
detail::type_uses_smart_holder_type_caster<type_>>>;
|
||||
// struct instead of using here to help MSVC:
|
||||
template <typename T>
|
||||
struct is_valid_class_option : detail::any_of<is_holder<T>, is_subtype<T>, is_base<T>> {};
|
||||
@ -1826,36 +1868,6 @@ public:
|
||||
none_of<std::is_same<multiple_inheritance, Extra>...>::value),
|
||||
"Error: multiple inheritance bases must be specified via class_ template options");
|
||||
|
||||
static constexpr bool holder_is_smart_holder
|
||||
= detail::is_smart_holder_type<holder_type>::value;
|
||||
static constexpr bool wrapped_type_uses_smart_holder_type_caster
|
||||
= detail::type_uses_smart_holder_type_caster<type>::value;
|
||||
static constexpr bool type_caster_type_is_type_caster_base_subtype
|
||||
= std::is_base_of<detail::type_caster_base<type>, detail::type_caster<type>>::value;
|
||||
// Necessary conditions, but not strict.
|
||||
static_assert(!(detail::is_instantiation<std::unique_ptr, holder_type>::value
|
||||
&& wrapped_type_uses_smart_holder_type_caster),
|
||||
"py::class_ holder vs type_caster mismatch:"
|
||||
" missing PYBIND11_TYPE_CASTER_BASE_HOLDER(T, std::unique_ptr<T>)?");
|
||||
static_assert(!(detail::is_instantiation<std::shared_ptr, holder_type>::value
|
||||
&& wrapped_type_uses_smart_holder_type_caster),
|
||||
"py::class_ holder vs type_caster mismatch:"
|
||||
" missing PYBIND11_TYPE_CASTER_BASE_HOLDER(T, std::shared_ptr<T>)?");
|
||||
static_assert(!(holder_is_smart_holder && type_caster_type_is_type_caster_base_subtype),
|
||||
"py::class_ holder vs type_caster mismatch:"
|
||||
" missing PYBIND11_SMART_HOLDER_TYPE_CASTERS(T)?");
|
||||
#ifdef PYBIND11_STRICT_ASSERTS_CLASS_HOLDER_VS_TYPE_CASTER_MIX
|
||||
// Strict conditions cannot be enforced universally at the moment (PR #2836).
|
||||
static_assert(holder_is_smart_holder == wrapped_type_uses_smart_holder_type_caster,
|
||||
"py::class_ holder vs type_caster mismatch:"
|
||||
" missing PYBIND11_SMART_HOLDER_TYPE_CASTERS(T)"
|
||||
" or collision with custom py::detail::type_caster<T>?");
|
||||
static_assert(!holder_is_smart_holder == type_caster_type_is_type_caster_base_subtype,
|
||||
"py::class_ holder vs type_caster mismatch:"
|
||||
" missing PYBIND11_TYPE_CASTER_BASE_HOLDER(T, ...)"
|
||||
" or collision with custom py::detail::type_caster<T>?");
|
||||
#endif
|
||||
|
||||
type_record record;
|
||||
record.scope = scope;
|
||||
record.name = name;
|
||||
@ -1869,6 +1881,18 @@ public:
|
||||
// A more fitting name would be uses_unique_ptr_holder.
|
||||
record.default_holder = detail::is_instantiation<std::unique_ptr, holder_type>::value;
|
||||
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
if (detail::is_instantiation<std::unique_ptr, holder_type>::value) {
|
||||
record.holder_enum_v = detail::holder_enum_t::std_unique_ptr;
|
||||
} else if (detail::is_instantiation<std::shared_ptr, holder_type>::value) {
|
||||
record.holder_enum_v = detail::holder_enum_t::std_shared_ptr;
|
||||
} else if (std::is_same<holder_type, smart_holder>::value) {
|
||||
record.holder_enum_v = detail::holder_enum_t::smart_holder;
|
||||
} else {
|
||||
record.holder_enum_v = detail::holder_enum_t::custom_holder;
|
||||
}
|
||||
#endif
|
||||
|
||||
set_operator_new<type>(&record);
|
||||
|
||||
/* Register base classes specified via template arguments to class_, if any */
|
||||
@ -1877,7 +1901,7 @@ public:
|
||||
/* Process optional arguments, if any */
|
||||
process_attributes<Extra...>::init(extra..., &record);
|
||||
|
||||
generic_type_initialize(record);
|
||||
generic_type::initialize(record);
|
||||
|
||||
if (has_alias) {
|
||||
with_internals([&](internals &internals) {
|
||||
@ -2138,18 +2162,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T = type,
|
||||
detail::enable_if_t<!detail::type_uses_smart_holder_type_caster<T>::value, int> = 0>
|
||||
void generic_type_initialize(const detail::type_record &record) {
|
||||
generic_type::initialize(record, &detail::type_caster_generic::local_load);
|
||||
}
|
||||
|
||||
template <typename T = type,
|
||||
detail::enable_if_t<detail::type_uses_smart_holder_type_caster<T>::value, int> = 0>
|
||||
void generic_type_initialize(const detail::type_record &record) {
|
||||
generic_type::initialize(record, detail::type_caster<T>::get_local_load_function_ptr());
|
||||
}
|
||||
|
||||
/// Initialize holder object, variant 1: object derives from enable_shared_from_this
|
||||
template <typename T>
|
||||
static void init_holder(detail::instance *inst,
|
||||
@ -2203,8 +2215,8 @@ private:
|
||||
/// instance. Should be called as soon as the `type` value_ptr is set for an instance. Takes
|
||||
/// an optional pointer to an existing holder to use; if not specified and the instance is
|
||||
/// `.owned`, a new holder will be constructed to manage the value pointer.
|
||||
template <typename T = type,
|
||||
detail::enable_if_t<!detail::type_uses_smart_holder_type_caster<T>::value, int> = 0>
|
||||
template <typename H = holder_type,
|
||||
detail::enable_if_t<!detail::is_smart_holder<H>::value, int> = 0>
|
||||
static void init_instance(detail::instance *inst, const void *holder_ptr) {
|
||||
auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(type)));
|
||||
if (!v_h.instance_registered()) {
|
||||
@ -2214,13 +2226,70 @@ private:
|
||||
init_holder(inst, v_h, (const holder_type *) holder_ptr, v_h.value_ptr<type>());
|
||||
}
|
||||
|
||||
template <typename T = type,
|
||||
typename A = type_alias,
|
||||
detail::enable_if_t<detail::type_uses_smart_holder_type_caster<T>::value, int> = 0>
|
||||
static void init_instance(detail::instance *inst, const void *holder_ptr) {
|
||||
detail::type_caster<T>::template init_instance_for_type<T, A>(inst, holder_ptr);
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
|
||||
template <typename WrappedType>
|
||||
static bool try_initialization_using_shared_from_this(holder_type *, WrappedType *, ...) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Adopting existing approach used by type_caster_base, although it leads to somewhat fuzzy
|
||||
// ownership semantics: if we detected via shared_from_this that a shared_ptr exists already,
|
||||
// it is reused, irrespective of the return_value_policy in effect.
|
||||
// "SomeBaseOfWrappedType" is needed because std::enable_shared_from_this is not necessarily a
|
||||
// direct base of WrappedType.
|
||||
template <typename WrappedType, typename SomeBaseOfWrappedType>
|
||||
static bool try_initialization_using_shared_from_this(
|
||||
holder_type *uninitialized_location,
|
||||
WrappedType *value_ptr_w_t,
|
||||
const std::enable_shared_from_this<SomeBaseOfWrappedType> *) {
|
||||
auto shd_ptr = std::dynamic_pointer_cast<WrappedType>(
|
||||
detail::try_get_shared_from_this(value_ptr_w_t));
|
||||
if (!shd_ptr) {
|
||||
return false;
|
||||
}
|
||||
// Note: inst->owned ignored.
|
||||
new (uninitialized_location) holder_type(holder_type::from_shared_ptr(shd_ptr));
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename H = holder_type,
|
||||
detail::enable_if_t<detail::is_smart_holder<H>::value, int> = 0>
|
||||
static void init_instance(detail::instance *inst, const void *holder_const_void_ptr) {
|
||||
// Need for const_cast is a consequence of the type_info::init_instance type:
|
||||
// void (*init_instance)(instance *, const void *);
|
||||
auto *holder_void_ptr = const_cast<void *>(holder_const_void_ptr);
|
||||
|
||||
auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(type)));
|
||||
if (!v_h.instance_registered()) {
|
||||
register_instance(inst, v_h.value_ptr(), v_h.type);
|
||||
v_h.set_instance_registered();
|
||||
}
|
||||
auto *uninitialized_location = std::addressof(v_h.holder<holder_type>());
|
||||
auto *value_ptr_w_t = v_h.value_ptr<type>();
|
||||
bool pointee_depends_on_holder_owner
|
||||
= detail::dynamic_raw_ptr_cast_if_possible<type_alias>(value_ptr_w_t) != nullptr;
|
||||
if (holder_void_ptr) {
|
||||
// Note: inst->owned ignored.
|
||||
auto *holder_ptr = static_cast<holder_type *>(holder_void_ptr);
|
||||
new (uninitialized_location) holder_type(std::move(*holder_ptr));
|
||||
} else if (!try_initialization_using_shared_from_this(
|
||||
uninitialized_location, value_ptr_w_t, value_ptr_w_t)) {
|
||||
if (inst->owned) {
|
||||
new (uninitialized_location) holder_type(holder_type::from_raw_ptr_take_ownership(
|
||||
value_ptr_w_t, /*void_cast_raw_ptr*/ pointee_depends_on_holder_owner));
|
||||
} else {
|
||||
new (uninitialized_location)
|
||||
holder_type(holder_type::from_raw_ptr_unowned(value_ptr_w_t));
|
||||
}
|
||||
}
|
||||
v_h.holder<holder_type>().pointee_depends_on_holder_owner
|
||||
= pointee_depends_on_holder_owner;
|
||||
v_h.set_holder_constructed();
|
||||
}
|
||||
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
|
||||
/// Deallocates an instance; via holder, if constructed; otherwise via operator delete.
|
||||
static void dealloc(detail::value_and_holder &v_h) {
|
||||
// We could be deallocating because we are cleaning up after a Python exception.
|
||||
@ -2261,6 +2330,18 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
|
||||
// Supports easier switching between py::class_<T> and py::class_<T, py::smart_holder>:
|
||||
// users can simply replace the `_` in `class_` with `h` or vice versa.
|
||||
template <typename type_, typename... options>
|
||||
class classh : public class_<type_, smart_holder, options...> {
|
||||
public:
|
||||
using class_<type_, smart_holder, options...>::class_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/// Binds an existing constructor taking arguments Args...
|
||||
template <typename... Args>
|
||||
detail::initimpl::constructor<Args...> init() {
|
||||
|
@ -1,29 +1,14 @@
|
||||
// Copyright (c) 2021 The Pybind Development Team.
|
||||
// Copyright (c) 2021-2024 The Pybind Development Team.
|
||||
// All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pybind11.h"
|
||||
#include "detail/common.h"
|
||||
#include "detail/smart_holder_type_casters.h"
|
||||
|
||||
#undef PYBIND11_SH_AVL // Undoing #define in pybind11.h
|
||||
|
||||
#define PYBIND11_SH_AVL(...) ::pybind11::smart_holder // "Smart_Holder if AVaiLable"
|
||||
// ---- std::shared_ptr(...) -- same length by design, to not disturb the indentation
|
||||
// of existing code.
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
// Supports easier switching between py::class_<T> and py::class_<T, py::smart_holder>:
|
||||
// users can simply replace the `_` in `class_` with `h` or vice versa.
|
||||
// Note though that the PYBIND11_SMART_HOLDER_TYPE_CASTERS(T) macro also needs to be
|
||||
// added (for `classh`) or commented out (when falling back to `class_`).
|
||||
template <typename type_, typename... options>
|
||||
class classh : public class_<type_, smart_holder, options...> {
|
||||
public:
|
||||
using class_<type_, smart_holder, options...>::class_;
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
// Legacy macros introduced with smart_holder_type_casters implementation in 2021.
|
||||
// Deprecated.
|
||||
#define PYBIND11_TYPE_CASTER_BASE_HOLDER(...)
|
||||
#define PYBIND11_SMART_HOLDER_TYPE_CASTERS(...)
|
||||
#define PYBIND11_SH_AVL(...) // "Smart_Holder if AVaiLable"
|
||||
#define PYBIND11_SH_DEF(...) // "Smart_Holder if DEFault"
|
||||
|
@ -392,15 +392,12 @@ struct variant_caster<V<Ts...>> {
|
||||
|
||||
template <typename U, typename... Us>
|
||||
bool load_alternative(handle src, bool convert, type_list<U, Us...>) {
|
||||
PYBIND11_WARNING_PUSH
|
||||
PYBIND11_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
|
||||
auto caster = make_caster<U>();
|
||||
if (caster.load(src, convert)) {
|
||||
value = cast_op<U>(std::move(caster));
|
||||
return true;
|
||||
}
|
||||
return load_alternative(src, convert, type_list<Us...>{});
|
||||
PYBIND11_WARNING_POP
|
||||
}
|
||||
|
||||
bool load_alternative(handle, bool, type_list<>) { return false; }
|
||||
|
@ -4,9 +4,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "detail/common.h"
|
||||
#include "detail/smart_holder_poc.h"
|
||||
#include "detail/value_and_holder.h"
|
||||
#include "detail/internals.h"
|
||||
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
|
||||
# include "detail/common.h"
|
||||
# include "detail/using_smart_holder.h"
|
||||
# include "detail/value_and_holder.h"
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
|
||||
@ -40,7 +44,7 @@ struct trampoline_self_life_support {
|
||||
if (value_void_ptr != nullptr) {
|
||||
PyGILState_STATE threadstate = PyGILState_Ensure();
|
||||
v_h.value_ptr() = nullptr;
|
||||
v_h.holder<pybindit::memory::smart_holder>().release_disowned();
|
||||
v_h.holder<smart_holder>().release_disowned();
|
||||
detail::deregister_instance(v_h.inst, value_void_ptr, v_h.type);
|
||||
Py_DECREF((PyObject *) v_h.inst); // Must be after deregister.
|
||||
PyGILState_Release(threadstate);
|
||||
@ -59,3 +63,5 @@ struct trampoline_self_life_support {
|
||||
};
|
||||
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
|
@ -8,5 +8,5 @@ def _to_int(s: str) -> int | str:
|
||||
return s
|
||||
|
||||
|
||||
__version__ = "2.14.0.dev1"
|
||||
__version__ = "3.0.0.dev1"
|
||||
version_info = tuple(_to_int(s) for s in __version__.split("."))
|
||||
|
@ -19,9 +19,16 @@ atyp rtrn_valu_atyp() { return atyp(); }
|
||||
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_module_local::atyp)
|
||||
|
||||
PYBIND11_MODULE(class_sh_module_local_0, m) {
|
||||
m.attr("defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
|
||||
#ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
|
||||
using namespace pybind11_tests::class_sh_module_local;
|
||||
|
||||
m.def("get_mtxt", get_mtxt);
|
||||
|
||||
m.def("rtrn_valu_atyp", rtrn_valu_atyp);
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
}
|
||||
|
@ -18,6 +18,12 @@ std::string get_mtxt(const atyp &obj) { return obj.mtxt; }
|
||||
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_module_local::atyp)
|
||||
|
||||
PYBIND11_MODULE(class_sh_module_local_1, m) {
|
||||
m.attr("defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
|
||||
#ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
|
||||
namespace py = pybind11;
|
||||
using namespace pybind11_tests::class_sh_module_local;
|
||||
|
||||
@ -30,4 +36,5 @@ PYBIND11_MODULE(class_sh_module_local_1, m) {
|
||||
.def("tag", [](const atyp &) { return 1; });
|
||||
|
||||
m.def("get_mtxt", get_mtxt);
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
}
|
||||
|
@ -18,6 +18,12 @@ std::string get_mtxt(const atyp &obj) { return obj.mtxt; }
|
||||
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_module_local::atyp)
|
||||
|
||||
PYBIND11_MODULE(class_sh_module_local_2, m) {
|
||||
m.attr("defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
|
||||
#ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
|
||||
namespace py = pybind11;
|
||||
using namespace pybind11_tests::class_sh_module_local;
|
||||
|
||||
@ -30,4 +36,5 @@ PYBIND11_MODULE(class_sh_module_local_2, m) {
|
||||
.def("tag", [](const atyp &) { return 2; });
|
||||
|
||||
m.def("get_mtxt", get_mtxt);
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
}
|
||||
|
@ -60,10 +60,9 @@ detail_headers = {
|
||||
"include/pybind11/detail/init.h",
|
||||
"include/pybind11/detail/internals.h",
|
||||
"include/pybind11/detail/smart_holder_poc.h",
|
||||
"include/pybind11/detail/smart_holder_sfinae_hooks_only.h",
|
||||
"include/pybind11/detail/smart_holder_type_casters.h",
|
||||
"include/pybind11/detail/type_caster_base.h",
|
||||
"include/pybind11/detail/typeid.h",
|
||||
"include/pybind11/detail/using_smart_holder.h",
|
||||
"include/pybind11/detail/value_and_holder.h",
|
||||
}
|
||||
|
||||
|
@ -25,8 +25,6 @@
|
||||
PYBIND11_WARNING_DISABLE_MSVC(4324)
|
||||
// warning C4324: structure was padded due to alignment specifier
|
||||
|
||||
namespace {
|
||||
|
||||
// test_brace_initialization
|
||||
struct NoBraceInitialization {
|
||||
explicit NoBraceInitialization(std::vector<int> v) : vec{std::move(v)} {}
|
||||
@ -36,17 +34,6 @@ struct NoBraceInitialization {
|
||||
std::vector<int> vec;
|
||||
};
|
||||
|
||||
// test_mismatched_holder
|
||||
struct MismatchBase1 {};
|
||||
struct MismatchDerived1 : MismatchBase1 {};
|
||||
struct MismatchBase2 {};
|
||||
struct MismatchDerived2 : MismatchBase2 {};
|
||||
|
||||
// test_multiple_instances_with_same_pointer
|
||||
struct SamePointer {};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace test_class {
|
||||
namespace pr4220_tripped_over_this { // PR #4227
|
||||
|
||||
@ -67,12 +54,6 @@ void bind_empty0(py::module_ &m) {
|
||||
} // namespace pr4220_tripped_over_this
|
||||
} // namespace test_class
|
||||
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(MismatchBase1, std::shared_ptr<MismatchBase1>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(MismatchDerived1, std::unique_ptr<MismatchDerived1>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(MismatchBase2, std::unique_ptr<MismatchBase2>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(MismatchDerived2, std::shared_ptr<MismatchDerived2>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(SamePointer, std::unique_ptr<SamePointer>)
|
||||
|
||||
TEST_SUBMODULE(class_, m) {
|
||||
m.def("obj_class_name", [](py::handle obj) { return py::detail::obj_class_name(obj.ptr()); });
|
||||
|
||||
@ -108,6 +89,16 @@ TEST_SUBMODULE(class_, m) {
|
||||
.def_static("__new__",
|
||||
[](const py::object &) { return NoConstructorNew::new_instance(); });
|
||||
|
||||
// test_pass_unique_ptr
|
||||
struct ToBeHeldByUniquePtr {};
|
||||
py::class_<ToBeHeldByUniquePtr, std::unique_ptr<ToBeHeldByUniquePtr>>(m, "ToBeHeldByUniquePtr")
|
||||
.def(py::init<>());
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
m.def("pass_unique_ptr", [](std::unique_ptr<ToBeHeldByUniquePtr> &&) {});
|
||||
#else
|
||||
m.attr("pass_unique_ptr") = py::none();
|
||||
#endif
|
||||
|
||||
// test_inheritance
|
||||
class Pet {
|
||||
public:
|
||||
@ -221,6 +212,12 @@ TEST_SUBMODULE(class_, m) {
|
||||
m.def("as_type", [](const py::object &ob) { return py::type(ob); });
|
||||
|
||||
// test_mismatched_holder
|
||||
struct MismatchBase1 {};
|
||||
struct MismatchDerived1 : MismatchBase1 {};
|
||||
|
||||
struct MismatchBase2 {};
|
||||
struct MismatchDerived2 : MismatchBase2 {};
|
||||
|
||||
m.def("mismatched_holder_1", []() {
|
||||
auto mod = py::module_::import("__main__");
|
||||
py::class_<MismatchBase1, std::shared_ptr<MismatchBase1>>(mod, "MismatchBase1");
|
||||
@ -520,6 +517,7 @@ TEST_SUBMODULE(class_, m) {
|
||||
.def("throw_something", &PyPrintDestructor::throw_something);
|
||||
|
||||
// test_multiple_instances_with_same_pointer
|
||||
struct SamePointer {};
|
||||
static SamePointer samePointer;
|
||||
py::class_<SamePointer, std::unique_ptr<SamePointer, py::nodelete>>(m, "SamePointer")
|
||||
.def(py::init([]() { return &samePointer; }));
|
||||
@ -619,18 +617,12 @@ CHECK_NOALIAS(8);
|
||||
static_assert(std::is_same<typename DoesntBreak##N::holder_type, \
|
||||
std::TYPE##_ptr<BreaksBase<(N)>>>::value, \
|
||||
"DoesntBreak" #N " has wrong holder_type!")
|
||||
#define CHECK_SMART_HOLDER(N) \
|
||||
static_assert(std::is_same<typename DoesntBreak##N::holder_type, py::smart_holder>::value, \
|
||||
"DoesntBreak" #N " has wrong holder_type!")
|
||||
CHECK_HOLDER(1, unique);
|
||||
CHECK_HOLDER(2, unique);
|
||||
CHECK_HOLDER(3, unique);
|
||||
#ifndef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
|
||||
#ifndef PYBIND11_ACTUALLY_USING_SMART_HOLDER_AS_DEFAULT
|
||||
CHECK_HOLDER(4, unique);
|
||||
CHECK_HOLDER(5, unique);
|
||||
#else
|
||||
CHECK_SMART_HOLDER(4);
|
||||
CHECK_SMART_HOLDER(5);
|
||||
#endif
|
||||
CHECK_HOLDER(6, shared);
|
||||
CHECK_HOLDER(7, shared);
|
||||
|
@ -41,6 +41,18 @@ def test_instance_new():
|
||||
assert cstats.alive() == 0
|
||||
|
||||
|
||||
def test_pass_unique_ptr():
|
||||
obj = m.ToBeHeldByUniquePtr()
|
||||
if m.pass_unique_ptr is None:
|
||||
pytest.skip("smart_holder not available.")
|
||||
with pytest.raises(RuntimeError) as execinfo:
|
||||
m.pass_unique_ptr(obj)
|
||||
assert str(execinfo.value).startswith(
|
||||
"Passing `std::unique_ptr<T>` from Python to C++ requires `py::classh` (with T = "
|
||||
)
|
||||
assert "ToBeHeldByUniquePtr" in str(execinfo.value)
|
||||
|
||||
|
||||
def test_type():
|
||||
assert m.check_type(1) == m.DerivedClass1
|
||||
with pytest.raises(RuntimeError) as execinfo:
|
||||
|
@ -145,6 +145,12 @@ namespace pybind11_tests {
|
||||
namespace class_sh_basic {
|
||||
|
||||
TEST_SUBMODULE(class_sh_basic, m) {
|
||||
m.attr("defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
|
||||
#ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
py::classh<atyp>(m, "atyp").def(py::init<>()).def(py::init([](const std::string &mtxt) {
|
||||
@ -233,11 +239,14 @@ TEST_SUBMODULE(class_sh_basic, m) {
|
||||
[]() { return std::unique_ptr<atyp>(new atyp("rtrn_uq_automatic_reference")); },
|
||||
pybind11::return_value_policy::automatic_reference);
|
||||
|
||||
m.def("pass_shared_ptr_ptr", [](std::shared_ptr<atyp> *) {});
|
||||
|
||||
py::classh<LocalUnusualOpRef>(m, "LocalUnusualOpRef");
|
||||
m.def("CallCastUnusualOpRefConstRef",
|
||||
[]() { return CastUnusualOpRefConstRef(LocalUnusualOpRef()); });
|
||||
m.def("CallCastUnusualOpRefMovable",
|
||||
[]() { return CastUnusualOpRefMovable(LocalUnusualOpRef()); });
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
}
|
||||
|
||||
} // namespace class_sh_basic
|
||||
|
@ -7,6 +7,9 @@ import pytest
|
||||
|
||||
from pybind11_tests import class_sh_basic as m
|
||||
|
||||
if not m.defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT:
|
||||
pytest.skip("smart_holder not available.", allow_module_level=True)
|
||||
|
||||
|
||||
def test_atyp_constructors():
|
||||
obj = m.atyp()
|
||||
@ -132,9 +135,7 @@ def test_cannot_disown_use_count_ne_1(pass_f, rtrn_f):
|
||||
stash.Add(obj)
|
||||
with pytest.raises(ValueError) as exc_info:
|
||||
pass_f(obj)
|
||||
assert str(exc_info.value) == (
|
||||
"Cannot disown use_count != 1 (loaded_as_unique_ptr)."
|
||||
)
|
||||
assert str(exc_info.value) == ("Cannot disown use_count != 1 (load_as_unique_ptr).")
|
||||
|
||||
|
||||
def test_unique_ptr_roundtrip(num_round_trips=1000):
|
||||
@ -220,6 +221,16 @@ def test_unique_ptr_return_value_policy_automatic_reference():
|
||||
assert m.get_mtxt(m.rtrn_uq_automatic_reference()) == "rtrn_uq_automatic_reference"
|
||||
|
||||
|
||||
def test_pass_shared_ptr_ptr():
|
||||
obj = m.atyp()
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.pass_shared_ptr_ptr(obj)
|
||||
assert str(excinfo.value) == (
|
||||
"Passing `std::shared_ptr<T> *` from Python to C++ is not supported"
|
||||
" (inherently unsafe)."
|
||||
)
|
||||
|
||||
|
||||
def test_unusual_op_ref():
|
||||
# Merely to test that this still exists and built successfully.
|
||||
assert m.CallCastUnusualOpRefConstRef().__class__.__name__ == "LocalUnusualOpRef"
|
||||
|
@ -32,6 +32,12 @@ PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_disowning::Atype<1>)
|
||||
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_disowning::Atype<2>)
|
||||
|
||||
TEST_SUBMODULE(class_sh_disowning, m) {
|
||||
m.attr("defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
|
||||
#ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
|
||||
using namespace pybind11_tests::class_sh_disowning;
|
||||
|
||||
py::classh<Atype<1>>(m, "Atype1").def(py::init<int>()).def("get", &Atype<1>::get);
|
||||
@ -43,4 +49,5 @@ TEST_SUBMODULE(class_sh_disowning, m) {
|
||||
|
||||
m.def("overloaded", (int (*)(std::unique_ptr<Atype<1>>, int)) & overloaded);
|
||||
m.def("overloaded", (int (*)(std::unique_ptr<Atype<2>>, int)) & overloaded);
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
}
|
||||
|
@ -4,6 +4,9 @@ import pytest
|
||||
|
||||
from pybind11_tests import class_sh_disowning as m
|
||||
|
||||
if not m.defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT:
|
||||
pytest.skip("smart_holder not available.", allow_module_level=True)
|
||||
|
||||
|
||||
def is_disowned(obj):
|
||||
try:
|
||||
|
@ -57,6 +57,12 @@ PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_disowning_mi::Base1)
|
||||
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_disowning_mi::Base2)
|
||||
|
||||
TEST_SUBMODULE(class_sh_disowning_mi, m) {
|
||||
m.attr("defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
|
||||
#ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
|
||||
using namespace pybind11_tests::class_sh_disowning_mi;
|
||||
|
||||
py::classh<B>(m, "B")
|
||||
@ -92,4 +98,5 @@ TEST_SUBMODULE(class_sh_disowning_mi, m) {
|
||||
py::classh<Base2>(m, "Base2").def(py::init<int>()).def("bar", &Base2::bar);
|
||||
m.def("disown_base1", disown_base1);
|
||||
m.def("disown_base2", disown_base2);
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ import pytest
|
||||
import env # noqa: F401
|
||||
from pybind11_tests import class_sh_disowning_mi as m
|
||||
|
||||
if not m.defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT:
|
||||
pytest.skip("smart_holder not available.", allow_module_level=True)
|
||||
|
||||
|
||||
def test_diamond_inheritance():
|
||||
# Very similar to test_multiple_inheritance.py:test_diamond_inheritance.
|
||||
|
@ -87,6 +87,12 @@ PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_factory_constructors
|
||||
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_factory_constructors::with_alias)
|
||||
|
||||
TEST_SUBMODULE(class_sh_factory_constructors, m) {
|
||||
m.attr("defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
|
||||
#ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
|
||||
using namespace pybind11_tests::class_sh_factory_constructors;
|
||||
|
||||
py::classh<atyp_valu>(m, "atyp_valu")
|
||||
@ -177,4 +183,5 @@ TEST_SUBMODULE(class_sh_factory_constructors, m) {
|
||||
[](int, int, int, int, int) {
|
||||
return std::make_shared<with_alias>(); // Invalid alias factory.
|
||||
}));
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
}
|
||||
|
@ -4,6 +4,9 @@ import pytest
|
||||
|
||||
from pybind11_tests import class_sh_factory_constructors as m
|
||||
|
||||
if not m.defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT:
|
||||
pytest.skip("smart_holder not available.", allow_module_level=True)
|
||||
|
||||
|
||||
def test_atyp_factories():
|
||||
assert m.atyp_valu().get_mtxt() == "Valu"
|
||||
|
@ -73,6 +73,12 @@ namespace pybind11_tests {
|
||||
namespace class_sh_inheritance {
|
||||
|
||||
TEST_SUBMODULE(class_sh_inheritance, m) {
|
||||
m.attr("defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
|
||||
#ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
|
||||
py::classh<base>(m, "base");
|
||||
py::classh<drvd, base>(m, "drvd");
|
||||
|
||||
@ -99,6 +105,7 @@ TEST_SUBMODULE(class_sh_inheritance, m) {
|
||||
m.def("pass_cptr_base1", pass_cptr_base1);
|
||||
m.def("pass_cptr_base2", pass_cptr_base2);
|
||||
m.def("pass_cptr_drvd2", pass_cptr_drvd2);
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
}
|
||||
|
||||
} // namespace class_sh_inheritance
|
||||
|
@ -1,7 +1,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import class_sh_inheritance as m
|
||||
|
||||
if not m.defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT:
|
||||
pytest.skip("smart_holder not available.", allow_module_level=True)
|
||||
|
||||
|
||||
def test_rtrn_mptr_drvd_pass_cptr_base():
|
||||
d = m.rtrn_mptr_drvd()
|
||||
|
@ -40,6 +40,12 @@ PYBIND11_SMART_HOLDER_TYPE_CASTERS(test_class_sh_mi_thunks::Base1)
|
||||
PYBIND11_SMART_HOLDER_TYPE_CASTERS(test_class_sh_mi_thunks::Derived)
|
||||
|
||||
TEST_SUBMODULE(class_sh_mi_thunks, m) {
|
||||
m.attr("defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
|
||||
#ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
|
||||
using namespace test_class_sh_mi_thunks;
|
||||
|
||||
m.def("ptrdiff_drvd_base0", []() {
|
||||
@ -97,4 +103,5 @@ TEST_SUBMODULE(class_sh_mi_thunks, m) {
|
||||
}
|
||||
return obj_der->vec.size();
|
||||
});
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
}
|
||||
|
@ -4,6 +4,9 @@ import pytest
|
||||
|
||||
from pybind11_tests import class_sh_mi_thunks as m
|
||||
|
||||
if not m.defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT:
|
||||
pytest.skip("smart_holder not available.", allow_module_level=True)
|
||||
|
||||
|
||||
def test_ptrdiff_drvd_base0():
|
||||
ptrdiff = m.ptrdiff_drvd_base0()
|
||||
@ -49,6 +52,5 @@ def test_get_shared_vec_size_unique():
|
||||
with pytest.raises(ValueError) as exc_info:
|
||||
m.vec_size_base0_unique_ptr(obj)
|
||||
assert (
|
||||
str(exc_info.value)
|
||||
== "Cannot disown external shared_ptr (loaded_as_unique_ptr)."
|
||||
str(exc_info.value) == "Cannot disown external shared_ptr (load_as_unique_ptr)."
|
||||
)
|
||||
|
@ -5,6 +5,9 @@ import class_sh_module_local_1 as m1
|
||||
import class_sh_module_local_2 as m2
|
||||
import pytest
|
||||
|
||||
if not m0.defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT:
|
||||
pytest.skip("smart_holder not available.", allow_module_level=True)
|
||||
|
||||
|
||||
def test_cross_module_get_mtxt():
|
||||
obj1 = m1.atyp("A")
|
||||
|
@ -58,6 +58,12 @@ PYBIND11_SMART_HOLDER_TYPE_CASTERS(test_class_sh_property::WithCharArrayMember)
|
||||
PYBIND11_SMART_HOLDER_TYPE_CASTERS(test_class_sh_property::WithConstCharPtrMember)
|
||||
|
||||
TEST_SUBMODULE(class_sh_property, m) {
|
||||
m.attr("defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
|
||||
#ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
|
||||
using namespace test_class_sh_property;
|
||||
|
||||
py::class_<ClassicField, std::unique_ptr<ClassicField>>(m, "ClassicField")
|
||||
@ -103,4 +109,5 @@ TEST_SUBMODULE(class_sh_property, m) {
|
||||
py::classh<WithConstCharPtrMember>(m, "WithConstCharPtrMember")
|
||||
.def(py::init<>())
|
||||
.def_readonly("const_char_ptr_member", &WithConstCharPtrMember::const_char_ptr_member);
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
}
|
||||
|
@ -7,6 +7,9 @@ import pytest
|
||||
import env # noqa: F401
|
||||
from pybind11_tests import class_sh_property as m
|
||||
|
||||
if not m.defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT:
|
||||
pytest.skip("smart_holder not available.", allow_module_level=True)
|
||||
|
||||
|
||||
@pytest.mark.xfail("env.PYPY", reason="gc after `del field` is apparently deferred")
|
||||
@pytest.mark.parametrize("m_attr", ["m_valu_readonly", "m_valu_readwrite"])
|
||||
@ -18,7 +21,7 @@ def test_valu_getter(m_attr):
|
||||
assert field.num == -99
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
m.DisownOuter(outer)
|
||||
assert str(excinfo.value) == "Cannot disown use_count != 1 (loaded_as_unique_ptr)."
|
||||
assert str(excinfo.value) == "Cannot disown use_count != 1 (load_as_unique_ptr)."
|
||||
del field
|
||||
m.DisownOuter(outer)
|
||||
with pytest.raises(ValueError, match="Python instance was disowned") as excinfo:
|
||||
|
@ -51,6 +51,12 @@ PYBIND11_SMART_HOLDER_TYPE_CASTERS(DataField)
|
||||
PYBIND11_SMART_HOLDER_TYPE_CASTERS(DataFieldsHolder)
|
||||
|
||||
TEST_SUBMODULE(class_sh_property_non_owning, m) {
|
||||
m.attr("defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
|
||||
#ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
|
||||
py::classh<CoreField>(m, "CoreField").def_readwrite("int_value", &CoreField::int_value);
|
||||
|
||||
py::classh<DataField>(m, "DataField")
|
||||
@ -65,4 +71,5 @@ TEST_SUBMODULE(class_sh_property_non_owning, m) {
|
||||
py::classh<DataFieldsHolder>(m, "DataFieldsHolder")
|
||||
.def(py::init<std::size_t>())
|
||||
.def("vec_at", &DataFieldsHolder::vec_at, py::return_value_policy::reference_internal);
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
}
|
||||
|
@ -4,6 +4,9 @@ import pytest
|
||||
|
||||
from pybind11_tests import class_sh_property_non_owning as m
|
||||
|
||||
if not m.defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT:
|
||||
pytest.skip("smart_holder not available.", allow_module_level=True)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("persistent_holder", [True, False])
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -50,7 +50,12 @@ PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::FooSmHld)
|
||||
namespace pybind11_tests {
|
||||
|
||||
TEST_SUBMODULE(class_sh_shared_ptr_copy_move, m) {
|
||||
#if true // Trick to avoid clang-format changes, in support of PR #5213.
|
||||
m.attr("defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
|
||||
#ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
py::class_<FooShPtr, std::shared_ptr<FooShPtr>>(m, "FooShPtr")
|
||||
@ -108,7 +113,7 @@ TEST_SUBMODULE(class_sh_shared_ptr_copy_move, m) {
|
||||
l.append(std::move(o));
|
||||
return l;
|
||||
});
|
||||
#endif
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
}
|
||||
|
||||
} // namespace pybind11_tests
|
||||
|
@ -1,7 +1,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import class_sh_shared_ptr_copy_move as m
|
||||
|
||||
if not m.defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT:
|
||||
pytest.skip("smart_holder not available.", allow_module_level=True)
|
||||
|
||||
|
||||
def test_shptr_copy():
|
||||
txt = m.test_ShPtr_copy()[0].get_history()
|
||||
|
@ -34,6 +34,7 @@ struct AbaseAlias : Abase<SerNo> {
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
template <>
|
||||
struct AbaseAlias<1> : Abase<1>, py::trampoline_self_life_support {
|
||||
using Abase<1>::Abase;
|
||||
@ -45,6 +46,7 @@ struct AbaseAlias<1> : Abase<1>, py::trampoline_self_life_support {
|
||||
other_val);
|
||||
}
|
||||
};
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
|
||||
template <int SerNo>
|
||||
int AddInCppRawPtr(const Abase<SerNo> *obj, int other_val) {
|
||||
@ -63,6 +65,7 @@ int AddInCppUniquePtr(std::unique_ptr<Abase<SerNo>> obj, int other_val) {
|
||||
|
||||
template <int SerNo>
|
||||
void wrap(py::module_ m, const char *py_class_name) {
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
py::classh<Abase<SerNo>, AbaseAlias<SerNo>>(m, py_class_name)
|
||||
.def(py::init<int>(), py::arg("val"))
|
||||
.def("Get", &Abase<SerNo>::Get)
|
||||
@ -71,6 +74,7 @@ void wrap(py::module_ m, const char *py_class_name) {
|
||||
m.def("AddInCppRawPtr", AddInCppRawPtr<SerNo>, py::arg("obj"), py::arg("other_val"));
|
||||
m.def("AddInCppSharedPtr", AddInCppSharedPtr<SerNo>, py::arg("obj"), py::arg("other_val"));
|
||||
m.def("AddInCppUniquePtr", AddInCppUniquePtr<SerNo>, py::arg("obj"), py::arg("other_val"));
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace class_sh_trampoline_basic
|
||||
@ -82,6 +86,13 @@ PYBIND11_SMART_HOLDER_TYPE_CASTERS(Abase<0>)
|
||||
PYBIND11_SMART_HOLDER_TYPE_CASTERS(Abase<1>)
|
||||
|
||||
TEST_SUBMODULE(class_sh_trampoline_basic, m) {
|
||||
m.attr("defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
|
||||
#ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
|
||||
wrap<0>(m, "Abase0");
|
||||
wrap<1>(m, "Abase1");
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
}
|
||||
|
@ -4,6 +4,9 @@ import pytest
|
||||
|
||||
from pybind11_tests import class_sh_trampoline_basic as m
|
||||
|
||||
if not m.defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT:
|
||||
pytest.skip("smart_holder not available.", allow_module_level=True)
|
||||
|
||||
|
||||
class PyDrvd0(m.Abase0):
|
||||
def __init__(self, val):
|
||||
|
@ -38,9 +38,11 @@ protected:
|
||||
Big5() : history{"DefaultConstructor"} {}
|
||||
};
|
||||
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
struct Big5Trampoline : Big5, py::trampoline_self_life_support {
|
||||
using Big5::Big5;
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace class_sh_trampoline_self_life_support
|
||||
} // namespace pybind11_tests
|
||||
@ -50,6 +52,12 @@ using namespace pybind11_tests::class_sh_trampoline_self_life_support;
|
||||
PYBIND11_SMART_HOLDER_TYPE_CASTERS(Big5)
|
||||
|
||||
TEST_SUBMODULE(class_sh_trampoline_self_life_support, m) {
|
||||
m.attr("defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
|
||||
#ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
|
||||
py::classh<Big5, Big5Trampoline>(m, "Big5")
|
||||
.def(py::init<std::string>())
|
||||
.def_readonly("history", &Big5::history);
|
||||
@ -86,4 +94,5 @@ TEST_SUBMODULE(class_sh_trampoline_self_life_support, m) {
|
||||
py::object o1 = py::cast(std::move(obj));
|
||||
return py::make_tuple(o1, o2);
|
||||
});
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
}
|
||||
|
@ -4,6 +4,9 @@ import pytest
|
||||
|
||||
import pybind11_tests.class_sh_trampoline_self_life_support as m
|
||||
|
||||
if not m.defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT:
|
||||
pytest.skip("smart_holder not available.", allow_module_level=True)
|
||||
|
||||
|
||||
class PyBig5(m.Big5):
|
||||
pass
|
||||
|
@ -71,9 +71,11 @@ struct SftSharedPtrStash {
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
struct SftTrampoline : Sft, py::trampoline_self_life_support {
|
||||
using Sft::Sft;
|
||||
};
|
||||
#endif
|
||||
|
||||
long use_count(const std::shared_ptr<Sft> &obj) { return obj.use_count(); }
|
||||
|
||||
@ -108,6 +110,12 @@ PYBIND11_SMART_HOLDER_TYPE_CASTERS(Sft)
|
||||
PYBIND11_SMART_HOLDER_TYPE_CASTERS(SftSharedPtrStash)
|
||||
|
||||
TEST_SUBMODULE(class_sh_trampoline_shared_from_this, m) {
|
||||
m.attr("defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
|
||||
#ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
|
||||
py::classh<Sft, SftTrampoline>(m, "Sft")
|
||||
.def(py::init<const std::string &>())
|
||||
.def(py::init([](const std::string &history, int) {
|
||||
@ -132,4 +140,5 @@ TEST_SUBMODULE(class_sh_trampoline_shared_from_this, m) {
|
||||
m.def("make_pure_cpp_sft_unq_ptr", make_pure_cpp_sft_unq_ptr);
|
||||
m.def("make_pure_cpp_sft_shd_ptr", make_pure_cpp_sft_shd_ptr);
|
||||
m.def("pass_through_shd_ptr", pass_through_shd_ptr);
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
}
|
||||
|
@ -8,6 +8,9 @@ import pytest
|
||||
import env
|
||||
import pybind11_tests.class_sh_trampoline_shared_from_this as m
|
||||
|
||||
if not m.defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT:
|
||||
pytest.skip("smart_holder not available.", allow_module_level=True)
|
||||
|
||||
|
||||
class PySft(m.Sft):
|
||||
pass
|
||||
@ -238,7 +241,7 @@ def test_std_make_shared_factory():
|
||||
str_exc_info_value = str(exc_info.value)
|
||||
assert (
|
||||
str_exc_info_value
|
||||
== "smart_holder_type_casters loaded_as_shared_ptr failure: not implemented:"
|
||||
== "smart_holder_type_casters load_as_shared_ptr failure: not implemented:"
|
||||
" trampoline-self-life-support for external shared_ptr to type inheriting"
|
||||
" from std::enable_shared_from_this."
|
||||
)
|
||||
|
@ -63,6 +63,12 @@ PYBIND11_SMART_HOLDER_TYPE_CASTERS(SpGoAway)
|
||||
PYBIND11_SMART_HOLDER_TYPE_CASTERS(SpGoAwayTester)
|
||||
|
||||
TEST_SUBMODULE(class_sh_trampoline_shared_ptr_cpp_arg, m) {
|
||||
m.attr("defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
|
||||
#ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
|
||||
// For testing whether a python subclass of a C++ object dies when the
|
||||
// last python reference is lost
|
||||
|
||||
@ -95,4 +101,5 @@ TEST_SUBMODULE(class_sh_trampoline_shared_ptr_cpp_arg, m) {
|
||||
py::classh<SpGoAwayTester>(m, "SpGoAwayTester")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("obj", &SpGoAwayTester::m_obj);
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
}
|
||||
|
@ -4,6 +4,9 @@ import pytest
|
||||
|
||||
import pybind11_tests.class_sh_trampoline_shared_ptr_cpp_arg as m
|
||||
|
||||
if not m.defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT:
|
||||
pytest.skip("smart_holder not available.", allow_module_level=True)
|
||||
|
||||
|
||||
def test_shared_ptr_cpp_arg():
|
||||
import weakref
|
||||
|
@ -39,6 +39,7 @@ PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_trampoline_unique_pt
|
||||
namespace pybind11_tests {
|
||||
namespace class_sh_trampoline_unique_ptr {
|
||||
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
class PyClass : public Class, public py::trampoline_self_life_support {
|
||||
public:
|
||||
std::unique_ptr<Class> clone() const override {
|
||||
@ -47,11 +48,18 @@ public:
|
||||
|
||||
int foo() const override { PYBIND11_OVERRIDE_PURE(int, Class, foo); }
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace class_sh_trampoline_unique_ptr
|
||||
} // namespace pybind11_tests
|
||||
|
||||
TEST_SUBMODULE(class_sh_trampoline_unique_ptr, m) {
|
||||
m.attr("defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
|
||||
#ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
|
||||
using namespace pybind11_tests::class_sh_trampoline_unique_ptr;
|
||||
|
||||
py::classh<Class, PyClass>(m, "Class")
|
||||
@ -63,4 +71,5 @@ TEST_SUBMODULE(class_sh_trampoline_unique_ptr, m) {
|
||||
|
||||
m.def("clone", [](const Class &obj) { return obj.clone(); });
|
||||
m.def("clone_and_foo", [](const Class &obj) { return obj.clone()->foo(); });
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
}
|
||||
|
@ -1,7 +1,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
import pybind11_tests.class_sh_trampoline_unique_ptr as m
|
||||
|
||||
if not m.defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT:
|
||||
pytest.skip("smart_holder not available.", allow_module_level=True)
|
||||
|
||||
|
||||
class MyClass(m.Class):
|
||||
def foo(self):
|
||||
|
@ -31,9 +31,16 @@ namespace pybind11_tests {
|
||||
namespace class_sh_unique_ptr_custom_deleter {
|
||||
|
||||
TEST_SUBMODULE(class_sh_unique_ptr_custom_deleter, m) {
|
||||
m.attr("defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
|
||||
#ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
|
||||
py::classh<Pet>(m, "Pet").def_readwrite("name", &Pet::name);
|
||||
|
||||
m.def("create", &Pet::New);
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
}
|
||||
|
||||
} // namespace class_sh_unique_ptr_custom_deleter
|
||||
|
@ -1,7 +1,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from pybind11_tests import class_sh_unique_ptr_custom_deleter as m
|
||||
|
||||
if not m.defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT:
|
||||
pytest.skip("smart_holder not available.", allow_module_level=True)
|
||||
|
||||
|
||||
def test_create():
|
||||
pet = m.create("abc")
|
||||
|
@ -45,6 +45,12 @@ namespace pybind11_tests {
|
||||
namespace class_sh_unique_ptr_member {
|
||||
|
||||
TEST_SUBMODULE(class_sh_unique_ptr_member, m) {
|
||||
m.attr("defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
|
||||
#ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
|
||||
py::classh<pointee>(m, "pointee").def(py::init<>()).def("get_int", &pointee::get_int);
|
||||
|
||||
m.def("make_unique_pointee", make_unique_pointee);
|
||||
@ -54,6 +60,7 @@ TEST_SUBMODULE(class_sh_unique_ptr_member, m) {
|
||||
.def("is_owner", &ptr_owner::is_owner)
|
||||
.def("give_up_ownership_via_unique_ptr", &ptr_owner::give_up_ownership_via_unique_ptr)
|
||||
.def("give_up_ownership_via_shared_ptr", &ptr_owner::give_up_ownership_via_shared_ptr);
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
}
|
||||
|
||||
} // namespace class_sh_unique_ptr_member
|
||||
|
@ -4,6 +4,9 @@ import pytest
|
||||
|
||||
from pybind11_tests import class_sh_unique_ptr_member as m
|
||||
|
||||
if not m.defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT:
|
||||
pytest.skip("smart_holder not available.", allow_module_level=True)
|
||||
|
||||
|
||||
def test_make_unique_pointee():
|
||||
obj = m.make_unique_pointee()
|
||||
|
@ -31,6 +31,8 @@ int get_from_cpp_plainc_ptr(const Base *b) { return b->get() + 4000; }
|
||||
|
||||
int get_from_cpp_unique_ptr(std::unique_ptr<Base> b) { return b->get() + 5000; }
|
||||
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
|
||||
struct BaseVirtualOverrider : Base, py::trampoline_self_life_support {
|
||||
using Base::Base;
|
||||
|
||||
@ -43,6 +45,8 @@ struct CppDerivedVirtualOverrider : CppDerived, py::trampoline_self_life_support
|
||||
int get() const override { PYBIND11_OVERRIDE(int, CppDerived, get); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace class_sh_virtual_py_cpp_mix
|
||||
} // namespace pybind11_tests
|
||||
|
||||
@ -53,6 +57,12 @@ PYBIND11_SMART_HOLDER_TYPE_CASTERS(CppDerivedPlain)
|
||||
PYBIND11_SMART_HOLDER_TYPE_CASTERS(CppDerived)
|
||||
|
||||
TEST_SUBMODULE(class_sh_virtual_py_cpp_mix, m) {
|
||||
m.attr("defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT") =
|
||||
#ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
false;
|
||||
#else
|
||||
true;
|
||||
|
||||
py::classh<Base, BaseVirtualOverrider>(m, "Base").def(py::init<>()).def("get", &Base::get);
|
||||
|
||||
py::classh<CppDerivedPlain, Base>(m, "CppDerivedPlain").def(py::init<>());
|
||||
@ -61,4 +71,5 @@ TEST_SUBMODULE(class_sh_virtual_py_cpp_mix, m) {
|
||||
|
||||
m.def("get_from_cpp_plainc_ptr", get_from_cpp_plainc_ptr, py::arg("b"));
|
||||
m.def("get_from_cpp_unique_ptr", get_from_cpp_unique_ptr, py::arg("b"));
|
||||
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
}
|
||||
|
@ -4,6 +4,9 @@ import pytest
|
||||
|
||||
from pybind11_tests import class_sh_virtual_py_cpp_mix as m
|
||||
|
||||
if not m.defined_PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT:
|
||||
pytest.skip("smart_holder not available.", allow_module_level=True)
|
||||
|
||||
|
||||
class PyBase(m.Base): # Avoiding name PyDerived, for more systematic naming.
|
||||
def __init__(self):
|
||||
|
@ -1,18 +1,20 @@
|
||||
#include "pybind11_tests.h"
|
||||
|
||||
// The main purpose of this test is to ensure the suggested BOILERPLATE code block below is
|
||||
// correct.
|
||||
// The main purpose of this test was to ensure that the suggested
|
||||
// BOILERPLATE code block (NOW DEPRECATED!) block below is correct.
|
||||
|
||||
// Copy this block of code into your project.
|
||||
// Replace FOOEXT with the name of your project.
|
||||
// BOILERPLATE BEGIN
|
||||
// BOILERPLATE BEGIN DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED
|
||||
#ifdef FOOEXT_USING_PYBIND11_SMART_HOLDER
|
||||
# include <pybind11/smart_holder.h>
|
||||
#else
|
||||
# include <pybind11/pybind11.h>
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
# ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
template <typename type_, typename... options>
|
||||
using classh = class_<type_, options...>;
|
||||
# endif
|
||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
# ifndef PYBIND11_SH_AVL
|
||||
# define PYBIND11_SH_AVL(...) std::shared_ptr<__VA_ARGS__> // "Smart_Holder if AVaiLable"
|
||||
@ -27,7 +29,7 @@ PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
||||
# define PYBIND11_TYPE_CASTER_BASE_HOLDER(...)
|
||||
# endif
|
||||
#endif
|
||||
// BOILERPLATE END
|
||||
// BOILERPLATE END DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED
|
||||
|
||||
namespace {
|
||||
struct FooUc {};
|
||||
@ -37,8 +39,8 @@ struct FooSc {};
|
||||
struct FooSp {};
|
||||
} // namespace
|
||||
|
||||
PYBIND11_SMART_HOLDER_TYPE_CASTERS(FooUp)
|
||||
PYBIND11_SMART_HOLDER_TYPE_CASTERS(FooSp)
|
||||
PYBIND11_SMART_HOLDER_TYPE_CASTERS(FooUp) // DEPRECATED
|
||||
PYBIND11_SMART_HOLDER_TYPE_CASTERS(FooSp) // DEPRECATED
|
||||
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(FooSa, std::shared_ptr<FooSa>)
|
||||
|
||||
|
@ -66,7 +66,7 @@ PYBIND11_EMBEDDED_MODULE(widget_module, m) {
|
||||
PYBIND11_EMBEDDED_MODULE(trampoline_module, m) {
|
||||
py::class_<test_override_cache_helper,
|
||||
test_override_cache_helper_trampoline,
|
||||
PYBIND11_SH_DEF(test_override_cache_helper)>(m, "test_override_cache_helper")
|
||||
std::shared_ptr<test_override_cache_helper>>(m, "test_override_cache_helper")
|
||||
.def(py::init_alias<>())
|
||||
.def("func", &test_override_cache_helper::func);
|
||||
}
|
||||
|
@ -250,7 +250,7 @@ TEST_SUBMODULE(factory_constructors, m) {
|
||||
};
|
||||
|
||||
// test_init_factory_basic, test_init_factory_casting
|
||||
py::class_<TestFactory3, PYBIND11_SH_DEF(TestFactory3)> pyTestFactory3(m, "TestFactory3");
|
||||
py::class_<TestFactory3, std::shared_ptr<TestFactory3>> pyTestFactory3(m, "TestFactory3");
|
||||
pyTestFactory3
|
||||
.def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct3(v); }))
|
||||
.def(py::init([](shared_ptr_tag) { return TestFactoryHelper::construct3(); }));
|
||||
@ -277,12 +277,12 @@ TEST_SUBMODULE(factory_constructors, m) {
|
||||
.def_readwrite("value", &TestFactory3::value);
|
||||
|
||||
// test_init_factory_casting
|
||||
py::class_<TestFactory4, TestFactory3, PYBIND11_SH_DEF(TestFactory4)>(m, "TestFactory4")
|
||||
py::class_<TestFactory4, TestFactory3, std::shared_ptr<TestFactory4>>(m, "TestFactory4")
|
||||
.def(py::init(c4a)) // pointer
|
||||
;
|
||||
|
||||
// Doesn't need to be registered, but registering makes getting ConstructorStats easier:
|
||||
py::class_<TestFactory5, TestFactory3, PYBIND11_SH_DEF(TestFactory5)>(m, "TestFactory5");
|
||||
py::class_<TestFactory5, TestFactory3, std::shared_ptr<TestFactory5>>(m, "TestFactory5");
|
||||
|
||||
// test_init_factory_alias
|
||||
// Alias testing
|
||||
@ -305,7 +305,7 @@ TEST_SUBMODULE(factory_constructors, m) {
|
||||
|
||||
// test_init_factory_dual
|
||||
// Separate alias constructor testing
|
||||
py::class_<TestFactory7, PyTF7, PYBIND11_SH_DEF(TestFactory7)>(m, "TestFactory7")
|
||||
py::class_<TestFactory7, PyTF7, std::shared_ptr<TestFactory7>>(m, "TestFactory7")
|
||||
.def(py::init([](int i) { return TestFactory7(i); }, [](int i) { return PyTF7(i); }))
|
||||
.def(py::init([](pointer_tag, int i) { return new TestFactory7(i); },
|
||||
[](pointer_tag, int i) { return new PyTF7(i); }))
|
||||
|
@ -280,11 +280,10 @@ def test_init_factory_dual():
|
||||
assert not g1.has_alias()
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
PythFactory7(tag.shared_ptr, tag.invalid_base, 14)
|
||||
assert str(excinfo.value) in (
|
||||
"pybind11::init(): construction failed: returned holder-wrapped instance is not an "
|
||||
"alias instance",
|
||||
"pybind11::init(): construction failed: returned std::shared_ptr pointee is not an "
|
||||
"alias instance",
|
||||
assert (
|
||||
str(excinfo.value)
|
||||
== "pybind11::init(): construction failed: returned holder-wrapped instance is not an "
|
||||
"alias instance"
|
||||
)
|
||||
|
||||
assert [i.alive() for i in cstats] == [13, 7]
|
||||
|
@ -121,7 +121,7 @@ public:
|
||||
};
|
||||
int none1(const NoneTester &obj) { return obj.answer; }
|
||||
int none2(NoneTester *obj) { return obj ? obj->answer : -1; }
|
||||
int none3(const std::shared_ptr<NoneTester> &obj) { return obj ? obj->answer : -1; }
|
||||
int none3(std::shared_ptr<NoneTester> &obj) { return obj ? obj->answer : -1; }
|
||||
int none4(std::shared_ptr<NoneTester> *obj) { return obj && *obj ? (*obj)->answer : -1; }
|
||||
int none5(const std::shared_ptr<NoneTester> &obj) { return obj ? obj->answer : -1; }
|
||||
|
||||
@ -417,21 +417,17 @@ TEST_SUBMODULE(methods_and_attributes, m) {
|
||||
// [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works.
|
||||
|
||||
// test_accepts_none
|
||||
py::class_<NoneTester, PYBIND11_SH_DEF(NoneTester)>(m, "NoneTester").def(py::init<>());
|
||||
py::class_<NoneTester, std::shared_ptr<NoneTester>>(m, "NoneTester").def(py::init<>());
|
||||
m.def("no_none1", &none1, py::arg{}.none(false));
|
||||
m.def("no_none2", &none2, py::arg{}.none(false));
|
||||
m.def("no_none3", &none3, py::arg{}.none(false));
|
||||
m.def("no_none4", &none4, py::arg{}.none(false));
|
||||
m.def("no_none5", &none5, py::arg{}.none(false));
|
||||
m.def("ok_none1", &none1);
|
||||
m.def("ok_none2", &none2, py::arg{}.none(true));
|
||||
m.def("ok_none3", &none3);
|
||||
m.def("ok_none5", &none5);
|
||||
|
||||
#ifndef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
|
||||
// smart_holder_type_caster does not support conversion to `const shared_ptr<T> *`.
|
||||
m.def("no_none4", &none4, py::arg{}.none(false));
|
||||
m.def("ok_none4", &none4, py::arg{}.none(true));
|
||||
#endif
|
||||
m.def("ok_none5", &none5);
|
||||
|
||||
m.def("no_none_kwarg", &none2, "a"_a.none(false));
|
||||
m.def("no_none_kwarg_kw_only", &none2, py::kw_only(), "a"_a.none(false));
|
||||
|
@ -387,10 +387,12 @@ def test_accepts_none(msg):
|
||||
assert m.no_none1(a) == 42
|
||||
assert m.no_none2(a) == 42
|
||||
assert m.no_none3(a) == 42
|
||||
assert m.no_none4(a) == 42
|
||||
assert m.no_none5(a) == 42
|
||||
assert m.ok_none1(a) == 42
|
||||
assert m.ok_none2(a) == 42
|
||||
assert m.ok_none3(a) == 42
|
||||
assert m.ok_none4(a) == 42
|
||||
assert m.ok_none5(a) == 42
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
@ -402,6 +404,9 @@ def test_accepts_none(msg):
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.no_none3(None)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.no_none4(None)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.no_none5(None)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
@ -422,6 +427,7 @@ def test_accepts_none(msg):
|
||||
# The rest take the argument as pointer or holder, and accept None:
|
||||
assert m.ok_none2(None) == -1
|
||||
assert m.ok_none3(None) == -1
|
||||
assert m.ok_none4(None) == -1
|
||||
assert m.ok_none5(None) == -1
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
@ -437,14 +443,6 @@ def test_accepts_none(msg):
|
||||
m.no_none_kwarg_kw_only(a=None)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
|
||||
if hasattr(m, "no_none4"):
|
||||
assert m.no_none4(a) == 42
|
||||
assert m.ok_none4(a) == 42
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.no_none4(None)
|
||||
assert "incompatible function arguments" in str(excinfo.value)
|
||||
assert m.ok_none4(None) == -1
|
||||
|
||||
|
||||
def test_casts_none():
|
||||
"""#2778: implicit casting from None to object (not pointer)"""
|
||||
|
@ -148,15 +148,15 @@ TEST_SUBMODULE(multiple_inheritance, m) {
|
||||
// test_multiple_inheritance_virtbase
|
||||
// Test the case where not all base classes are specified, and where pybind11 requires the
|
||||
// py::multiple_inheritance flag to perform proper casting between types.
|
||||
py::class_<Base1a, PYBIND11_SH_DEF(Base1a)>(m, "Base1a")
|
||||
py::class_<Base1a, std::shared_ptr<Base1a>>(m, "Base1a")
|
||||
.def(py::init<int>())
|
||||
.def("foo", &Base1a::foo);
|
||||
|
||||
py::class_<Base2a, PYBIND11_SH_DEF(Base2a)>(m, "Base2a")
|
||||
py::class_<Base2a, std::shared_ptr<Base2a>>(m, "Base2a")
|
||||
.def(py::init<int>())
|
||||
.def("bar", &Base2a::bar);
|
||||
|
||||
py::class_<Base12a, /* Base1 missing */ Base2a, PYBIND11_SH_DEF(Base12a)>(
|
||||
py::class_<Base12a, /* Base1 missing */ Base2a, std::shared_ptr<Base12a>>(
|
||||
m, "Base12a", py::multiple_inheritance())
|
||||
.def(py::init<int, int>());
|
||||
|
||||
@ -173,14 +173,14 @@ TEST_SUBMODULE(multiple_inheritance, m) {
|
||||
};
|
||||
struct I801E : I801B3, I801D {};
|
||||
|
||||
py::class_<I801B1, PYBIND11_SH_DEF(I801B1)>(m, "I801B1")
|
||||
py::class_<I801B1, std::shared_ptr<I801B1>>(m, "I801B1")
|
||||
.def(py::init<>())
|
||||
.def_readonly("a", &I801B1::a);
|
||||
py::class_<I801B2, PYBIND11_SH_DEF(I801B2)>(m, "I801B2")
|
||||
py::class_<I801B2, std::shared_ptr<I801B2>>(m, "I801B2")
|
||||
.def(py::init<>())
|
||||
.def_readonly("b", &I801B2::b);
|
||||
py::class_<I801C, I801B1, I801B2, PYBIND11_SH_DEF(I801C)>(m, "I801C").def(py::init<>());
|
||||
py::class_<I801D, I801C, PYBIND11_SH_DEF(I801D)>(m, "I801D").def(py::init<>());
|
||||
py::class_<I801C, I801B1, I801B2, std::shared_ptr<I801C>>(m, "I801C").def(py::init<>());
|
||||
py::class_<I801D, I801C, std::shared_ptr<I801D>>(m, "I801D").def(py::init<>());
|
||||
|
||||
// Two separate issues here: first, we want to recognize a pointer to a base type as being a
|
||||
// known instance even when the pointer value is unequal (i.e. due to a non-first
|
||||
|
@ -287,42 +287,6 @@ PYBIND11_DECLARE_HOLDER_TYPE(T, custom_unique_ptr<T>);
|
||||
PYBIND11_DECLARE_HOLDER_TYPE(T, shared_ptr_with_addressof_operator<T>);
|
||||
PYBIND11_DECLARE_HOLDER_TYPE(T, unique_ptr_with_addressof_operator<T>);
|
||||
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(Object, ref<Object>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(MyObject1, ref<MyObject1>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(MyObject2, std::shared_ptr<MyObject2>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(MyObject3, std::shared_ptr<MyObject3>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(MyObject4, std::unique_ptr<MyObject4, py::nodelete>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(MyObject4a, std::unique_ptr<MyObject4a, py::nodelete>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(MyObject4b, std::unique_ptr<MyObject4b>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(MyObject5, huge_unique_ptr<MyObject5>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(SharedPtrRef::A, std::shared_ptr<SharedPtrRef::A>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(SharedPtrRef, std::unique_ptr<SharedPtrRef>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(SharedFromThisRef::B, std::shared_ptr<SharedFromThisRef::B>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(SharedFromThisRef, std::unique_ptr<SharedFromThisRef>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(SharedFromThisVirt, std::shared_ptr<SharedFromThisVirt>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(C, custom_unique_ptr<C>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(TypeForHolderWithAddressOf,
|
||||
shared_ptr_with_addressof_operator<TypeForHolderWithAddressOf>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(
|
||||
TypeForMoveOnlyHolderWithAddressOf,
|
||||
unique_ptr_with_addressof_operator<TypeForMoveOnlyHolderWithAddressOf>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(HeldByDefaultHolder, std::unique_ptr<HeldByDefaultHolder>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(ElementBase, std::shared_ptr<ElementBase>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(ElementA, std::shared_ptr<ElementA>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(ElementList, std::shared_ptr<ElementList>)
|
||||
|
||||
#ifdef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
|
||||
// To prevent triggering a static_assert in the smart_holder code.
|
||||
// This is a very special case, because the associated test exercises a holder mismatch.
|
||||
namespace pybind11 {
|
||||
namespace detail {
|
||||
template <>
|
||||
class type_caster<std::shared_ptr<HeldByDefaultHolder>>
|
||||
: public copyable_holder_caster<HeldByDefaultHolder, std::shared_ptr<HeldByDefaultHolder>> {};
|
||||
} // namespace detail
|
||||
} // namespace pybind11
|
||||
#endif
|
||||
|
||||
TEST_SUBMODULE(smart_ptr, m) {
|
||||
// Please do not interleave `struct` and `class` definitions with bindings code,
|
||||
// but implement `struct`s and `class`es in the anonymous namespace above.
|
||||
|
@ -301,9 +301,9 @@ def test_smart_ptr_from_default():
|
||||
instance = m.HeldByDefaultHolder()
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
m.HeldByDefaultHolder.load_shared_ptr(instance)
|
||||
assert str(excinfo.value) in (
|
||||
"Unable to load a smart-pointer type from a non-smart_holder instance.",
|
||||
"Unable to load a custom holder type from a default-holder instance",
|
||||
assert (
|
||||
"Unable to load a custom holder type from a "
|
||||
"default-holder instance" in str(excinfo.value)
|
||||
)
|
||||
|
||||
|
||||
|
@ -406,7 +406,7 @@ TEST_SUBMODULE(virtual_functions, m) {
|
||||
|
||||
py::class_<test_override_cache_helper,
|
||||
test_override_cache_helper_trampoline,
|
||||
PYBIND11_SH_DEF(test_override_cache_helper)>(m, "test_override_cache_helper")
|
||||
std::shared_ptr<test_override_cache_helper>>(m, "test_override_cache_helper")
|
||||
.def(py::init_alias<>())
|
||||
.def("func", &test_override_cache_helper::func);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include <pybind11/smart_holder.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
#include "number_bucket.h"
|
||||
|
||||
@ -22,6 +22,8 @@ void wrap_number_bucket(py::module m, const char *class_name) {
|
||||
.def("add", &WrappedType::add, py::arg("other"));
|
||||
}
|
||||
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
|
||||
template <typename T>
|
||||
class padded_unique_ptr {
|
||||
std::unique_ptr<T> ptr;
|
||||
@ -35,20 +37,21 @@ public:
|
||||
static_assert(sizeof(padded_unique_ptr<nb_pu>) == sizeof(py::smart_holder),
|
||||
"Unexpected sizeof mismatch.");
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace hc
|
||||
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
PYBIND11_DECLARE_HOLDER_TYPE(T, hc::padded_unique_ptr<T>);
|
||||
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(hc::nb_up, std::unique_ptr<hc::nb_up>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(hc::nb_sp, std::shared_ptr<hc::nb_sp>)
|
||||
PYBIND11_TYPE_CASTER_BASE_HOLDER(hc::nb_pu, hc::padded_unique_ptr<hc::nb_pu>)
|
||||
PYBIND11_SMART_HOLDER_TYPE_CASTERS(hc::nb_sh)
|
||||
#endif
|
||||
|
||||
PYBIND11_MODULE(pybind11_ubench_holder_comparison, m) {
|
||||
using namespace hc;
|
||||
m.def("sizeof_smart_holder", []() { return sizeof(py::smart_holder); });
|
||||
wrap_number_bucket<nb_up, std::unique_ptr<nb_up>>(m, "number_bucket_up");
|
||||
wrap_number_bucket<nb_sp, std::shared_ptr<nb_sp>>(m, "number_bucket_sp");
|
||||
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
|
||||
m.def("sizeof_smart_holder", []() { return sizeof(py::smart_holder); });
|
||||
wrap_number_bucket<nb_pu, padded_unique_ptr<nb_pu>>(m, "number_bucket_pu");
|
||||
wrap_number_bucket<nb_sh, py::smart_holder>(m, "number_bucket_sh");
|
||||
#endif
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user