[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:
Ralf W. Grosse-Kunstleve 2024-07-31 20:17:31 +07:00 committed by GitHub
parent 6765d4e814
commit 48f25275c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
68 changed files with 1331 additions and 1765 deletions

View File

@ -154,10 +154,9 @@ set(PYBIND11_HEADERS
include/pybind11/detail/init.h include/pybind11/detail/init.h
include/pybind11/detail/internals.h include/pybind11/detail/internals.h
include/pybind11/detail/smart_holder_poc.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/type_caster_base.h
include/pybind11/detail/typeid.h include/pybind11/detail/typeid.h
include/pybind11/detail/using_smart_holder.h
include/pybind11/detail/value_and_holder.h include/pybind11/detail/value_and_holder.h
include/pybind11/attr.h include/pybind11/attr.h
include/pybind11/buffer_info.h include/pybind11/buffer_info.h

View File

@ -6,58 +6,71 @@ pybind11 — smart_holder branch
Overview Overview
======== ========
- The smart_holder git branch is a strict superset of the master - The smart_holder branch is a strict superset of the pybind11 master branch.
branch. Everything that works on master is expected to work exactly the same Everything that works with the master branch is expected to work exactly the
with the smart_holder branch. same with the smart_holder branch.
- **Smart-pointer interoperability** (``std::unique_ptr``, ``std::shared_ptr``) - Activating the smart_holder functionality for a given C++ type ``T`` is as
is implemented as an **add-on**. easy as changing ``py::class_<T>`` to ``py::classh<T>`` in client code.
- The add-on also supports - The ``py::classh<T>`` functionality includes
* passing a Python object back to C++ via ``std::unique_ptr``, safely
* 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. **disowning** the Python object.
* safely passing `"trampoline" * safely passing `"trampoline"
<https://pybind11.readthedocs.io/en/stable/advanced/classes.html#overriding-virtual-functions-in-python>`_ <https://pybind11.readthedocs.io/en/stable/advanced/classes.html#overriding-virtual-functions-in-python>`_
objects (objects with C++ virtual function overrides implemented in 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 associated Python objects are automatically kept alive for the lifetime
of the smart-pointer. of the smart-pointer.
- The smart_holder branch can be used in two modes: Note: As of `PR #5257 <https://github.com/pybind/pybind11/pull/5257>`_
* **Conservative mode**: ``py::class_`` works exactly as on master. the smart_holder functionality is fully baked into pybind11.
``py::classh`` uses ``py::smart_holder``. Prior to PR #5257 the smart_holder implementation was an "add-on", which made
* **Progressive mode**: ``py::class_`` uses ``py::smart_holder`` it necessary to use a ``PYBIND11_SMART_HOLDER_TYPE_CASTERS`` macro. This macro
(i.e. ``py::smart_holder`` is the default holder). 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? What is fundamentally different?
-------------------------------- --------------------------------
- Classic pybind11 has the concept of "smart-pointer is holder". - Classic pybind11 has the concept of "smart-pointer is holder".
Interoperability between smart-pointers is completely missing. For Interoperability between smart-pointers is completely missing. For example,
example, when using ``std::shared_ptr`` as holder, ``return``-ing with ``py::class_<T, std::shared_ptr<T>>``, ``return``-ing a
a ``std::unique_ptr`` leads to undefined runtime behavior ``std::unique_ptr<T>`` leads to undefined runtime behavior
(`#1138 <https://github.com/pybind/pybind11/issues/1138>`_). A (`#1138 <https://github.com/pybind/pybind11/issues/1138>`_).
`systematic analysis is here <https://github.com/pybind/pybind11/pull/2672#issuecomment-748392993>`_. 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 - ``py::smart_holder`` has a richer concept in comparison, with well-defined
runtime behavior. The holder "knows" about both ``std::unique_ptr`` and runtime behavior in all situations. ``py::smart_holder`` "knows" about both
``std::shared_ptr`` and how they interoperate. ``std::unique_ptr<T>`` and ``std::shared_ptr<T>``, 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).
What motivated the development of the smart_holder code? What motivated the development of the smart_holder code?
-------------------------------------------------------- --------------------------------------------------------
- Necessity is the mother. The bigger context is the ongoing retooling of - The original context was retooling of `PyCLIF
`PyCLIF <https://github.com/google/clif/>`_, to use pybind11 underneath <https://github.com/google/clif/>`_, to use pybind11 underneath,
instead of directly targeting the Python C API. Essentially, the smart_holder instead of directly targeting the Python C API. Essentially the smart_holder
branch is porting established PyCLIF functionality into pybind11. branch is porting established PyCLIF functionality into pybind11. (However,
this work also led to bug fixes in PyCLIF.)
Installation 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. 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 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. 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 Related links
============= =============
@ -353,4 +120,4 @@ Related links
* Small `slide deck * Small `slide deck
<https://docs.google.com/presentation/d/1r7auDN0x-b6uf-XCvUnZz6z09raasRcCHBMVDh7PsnQ/>`_ <https://docs.google.com/presentation/d/1r7auDN0x-b6uf-XCvUnZz6z09raasRcCHBMVDh7PsnQ/>`_
presented in meeting with pybind11 maintainers on Feb 22, 2021. Slides 5 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.)

View File

@ -331,6 +331,10 @@ struct type_record {
/// Is the class inheritable from python classes? /// Is the class inheritable from python classes?
bool is_final : 1; 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 *) ) { PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *) ) {
auto *base_info = detail::get_type_info(base, false); auto *base_info = detail::get_type_info(base, false);
if (!base_info) { if (!base_info) {

View File

@ -12,7 +12,6 @@
#include "detail/common.h" #include "detail/common.h"
#include "detail/descr.h" #include "detail/descr.h"
#include "detail/smart_holder_sfinae_hooks_only.h"
#include "detail/type_caster_base.h" #include "detail/type_caster_base.h"
#include "detail/typeid.h" #include "detail/typeid.h"
#include "pytypes.h" #include "pytypes.h"
@ -29,33 +28,17 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#ifdef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
# include "detail/smart_holder_type_casters.h"
#endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_WARNING_DISABLE_MSVC(4127) PYBIND11_WARNING_DISABLE_MSVC(4127)
PYBIND11_NAMESPACE_BEGIN(detail) 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> 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> template <typename type>
using make_caster = type_caster<intrinsic_t<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 // Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T
template <typename T> template <typename T>
typename make_caster<T>::template cast_op_type<T> cast_op(make_caster<T> &caster) { 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(); } 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. /// Type caster for holder types like std::shared_ptr, etc.
/// The SFINAE hook is provided to help work around the current lack of support /// The SFINAE hook is provided to help work around the current lack of support
/// for smart-pointer interoperability. Please consider it an implementation /// for smart-pointer interoperability. Please consider it an implementation
@ -852,12 +836,140 @@ protected:
holder_type holder; 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 /// Specialize for the common std::shared_ptr, so users don't need to
template <typename T> template <typename T>
class type_caster<std::shared_ptr<T>> : public copyable_holder_caster<T, std::shared_ptr<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. /// Type caster for holder types like std::unique_ptr.
/// Please consider the SFINAE hook an implementation detail, as explained /// Please consider the SFINAE hook an implementation detail, as explained
/// in the comment for the copyable_holder_caster. /// 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; 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> template <typename type, typename deleter>
class type_caster<std::unique_ptr<type, deleter>> class type_caster<std::unique_ptr<type, deleter>>
: public move_only_holder_caster<type, std::unique_ptr<type, deleter>> {}; : public move_only_holder_caster<type, std::unique_ptr<type, deleter>> {};
#endif
template <typename type, typename holder_type> template <typename type, typename holder_type>
using type_caster_holder = conditional_t<is_copy_constructible<holder_type>::value, 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> template <typename base, typename holder>
struct is_holder_type struct is_holder_type
: std::is_base_of<detail::type_caster_holder<base, holder>, detail::type_caster<holder>> {}; : 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> template <typename base, typename deleter>
struct is_holder_type<base, std::unique_ptr<base, deleter>> : std::true_type {}; 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 #ifdef PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION // See PR #4888
// This leads to compilation errors if a specialization is missing. // This leads to compilation errors if a specialization is missing.
@ -1141,7 +1363,6 @@ template <typename type>
using cast_is_temporary_value_reference using cast_is_temporary_value_reference
= bool_constant<(std::is_reference<type>::value || std::is_pointer<type>::value) = bool_constant<(std::is_reference<type>::value || std::is_pointer<type>::value)
&& !std::is_base_of<type_caster_generic, make_caster<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>; && !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 // 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> template <typename Return>
struct return_value_policy_override< struct return_value_policy_override<
Return, Return,
detail::enable_if_t<std::is_base_of<type_caster_generic, make_caster<Return>>::value detail::enable_if_t<std::is_base_of<type_caster_generic, make_caster<Return>>::value, void>> {
|| type_uses_smart_holder_type_caster<intrinsic_t<Return>>::value,
void>> {
static return_value_policy policy(return_value_policy p) { static return_value_policy policy(return_value_policy p) {
return !std::is_lvalue_reference<Return>::value && !std::is_pointer<Return>::value return !std::is_lvalue_reference<Return>::value && !std::is_pointer<Return>::value
? return_value_policy::move ? return_value_policy::move
@ -1857,9 +2076,7 @@ PYBIND11_NAMESPACE_END(detail)
template <typename T> template <typename T>
handle type::handle_of() { handle type::handle_of() {
static_assert( static_assert(std::is_base_of<detail::type_caster_generic, detail::make_caster<T>>::value,
detail::any_of<std::is_base_of<detail::type_caster_generic, detail::make_caster<T>>,
detail::type_uses_smart_holder_type_caster<T>>::value,
"py::type::of<T> only supports the case where T is a registered C++ types."); "py::type::of<T> only supports the case where T is a registered C++ types.");
return detail::get_type_handle(typeid(T), true); return detail::get_type_handle(typeid(T), true);
@ -1869,7 +2086,7 @@ handle type::handle_of() {
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) \ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) \
namespace detail { \ namespace detail { \
template <> \ 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) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

View File

@ -9,13 +9,13 @@
#pragma once #pragma once
#define PYBIND11_VERSION_MAJOR 2 #define PYBIND11_VERSION_MAJOR 3
#define PYBIND11_VERSION_MINOR 14 #define PYBIND11_VERSION_MINOR 0
#define PYBIND11_VERSION_PATCH 0.dev1 #define PYBIND11_VERSION_PATCH 0.dev1
// Similar to Python's convention: https://docs.python.org/3/c-api/apiabiversion.html // Similar to Python's convention: https://docs.python.org/3/c-api/apiabiversion.html
// Additional convention: 0xD = dev // Additional convention: 0xD = dev
#define PYBIND11_VERSION_HEX 0x020E00D1 #define PYBIND11_VERSION_HEX 0x030000D1
// Define some generic pybind11 helper macros for warning management. // Define some generic pybind11 helper macros for warning management.
// //

View File

@ -10,7 +10,7 @@
#pragma once #pragma once
#include "class.h" #include "class.h"
#include "smart_holder_sfinae_hooks_only.h" #include "using_smart_holder.h"
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) 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 // 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 // derived type (through those holder's implicit conversion from derived class holder
// constructors). // constructors).
template <typename Class, template <typename Class, detail::enable_if_t<!is_smart_holder<Holder<Class>>::value, int> = 0>
detail::enable_if_t<!detail::type_uses_smart_holder_type_caster<Cpp<Class>>::value, int>
= 0>
void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) { void construct(value_and_holder &v_h, Holder<Class> holder, bool need_alias) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias); PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
auto *ptr = holder_helper<Holder<Class>>::get(holder); 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)); 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, template <typename Class,
typename D = std::default_delete<Cpp<Class>>, typename D = std::default_delete<Cpp<Class>>,
detail::enable_if_t<detail::type_uses_smart_holder_type_caster<Cpp<Class>>::value, int> detail::enable_if_t<is_smart_holder<Holder<Class>>::value, int> = 0>
= 0>
void construct(value_and_holder &v_h, std::unique_ptr<Cpp<Class>, D> &&unq_ptr, bool need_alias) { 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); PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
auto *ptr = unq_ptr.get(); 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 // 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 // it does not matter if void_cast_raw_ptr is true or false, therefore it's not necessary
// to also inspect the type. // 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)); std::move(unq_ptr), /*void_cast_raw_ptr*/ Class::has_alias && is_alias<Class>(ptr));
v_h.value_ptr() = ptr; v_h.value_ptr() = ptr;
v_h.type->init_instance(v_h.inst, &smhldr); 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, template <typename Class,
typename D = std::default_delete<Alias<Class>>, typename D = std::default_delete<Alias<Class>>,
detail::enable_if_t<detail::type_uses_smart_holder_type_caster<Alias<Class>>::value, int> detail::enable_if_t<is_smart_holder<Holder<Class>>::value, int> = 0>
= 0>
void construct(value_and_holder &v_h, void construct(value_and_holder &v_h,
std::unique_ptr<Alias<Class>, D> &&unq_ptr, std::unique_ptr<Alias<Class>, D> &&unq_ptr,
bool /*need_alias*/) { bool /*need_alias*/) {
auto *ptr = unq_ptr.get(); auto *ptr = unq_ptr.get();
no_nullptr(ptr); no_nullptr(ptr);
auto smhldr = type_caster<Alias<Class>>::smart_holder_from_unique_ptr( auto smhldr
std::move(unq_ptr), /*void_cast_raw_ptr*/ true); = init_smart_holder_from_unique_ptr(std::move(unq_ptr), /*void_cast_raw_ptr*/ true);
v_h.value_ptr() = ptr; v_h.value_ptr() = ptr;
v_h.type->init_instance(v_h.inst, &smhldr); v_h.type->init_instance(v_h.inst, &smhldr);
} }
template <typename Class, template <typename Class, detail::enable_if_t<is_smart_holder<Holder<Class>>::value, int> = 0>
detail::enable_if_t<detail::type_uses_smart_holder_type_caster<Cpp<Class>>::value, int>
= 0>
void construct(value_and_holder &v_h, std::shared_ptr<Cpp<Class>> &&shd_ptr, bool need_alias) { void construct(value_and_holder &v_h, std::shared_ptr<Cpp<Class>> &&shd_ptr, bool need_alias) {
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias); PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(need_alias);
auto *ptr = shd_ptr.get(); 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 " throw type_error("pybind11::init(): construction failed: returned std::shared_ptr pointee "
"is not an alias instance"); "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.value_ptr() = ptr;
v_h.type->init_instance(v_h.inst, &smhldr); v_h.type->init_instance(v_h.inst, &smhldr);
} }
template <typename Class, template <typename Class, detail::enable_if_t<is_smart_holder<Holder<Class>>::value, int> = 0>
detail::enable_if_t<detail::type_uses_smart_holder_type_caster<Alias<Class>>::value, int>
= 0>
void construct(value_and_holder &v_h, void construct(value_and_holder &v_h,
std::shared_ptr<Alias<Class>> &&shd_ptr, std::shared_ptr<Alias<Class>> &&shd_ptr,
bool /*need_alias*/) { bool /*need_alias*/) {
auto *ptr = shd_ptr.get(); auto *ptr = shd_ptr.get();
no_nullptr(ptr); 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.value_ptr() = ptr;
v_h.type->init_instance(v_h.inst, &smhldr); v_h.type->init_instance(v_h.inst, &smhldr);
} }
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
// Implementing class for py::init<...>() // Implementing class for py::init<...>()
template <typename... Args> template <typename... Args>
struct constructor { struct constructor {

View File

@ -16,7 +16,6 @@
#endif #endif
#include "../pytypes.h" #include "../pytypes.h"
#include "smart_holder_sfinae_hooks_only.h"
#include <exception> #include <exception>
#include <mutex> #include <mutex>
@ -37,7 +36,9 @@
/// further ABI-incompatible changes may be made before the ABI is officially /// further ABI-incompatible changes may be made before the ABI is officially
/// changed to the new version. /// changed to the new version.
#ifndef PYBIND11_INTERNALS_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 Python 3.12+, before first 3.12 beta release.
// Version bump for MSVC piggy-backed on PR #4779. See comments there. // Version bump for MSVC piggy-backed on PR #4779. See comments there.
# define PYBIND11_INTERNALS_VERSION 5 # 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. /// Additional type information which does not fit into the PyTypeObject.
/// Changes to this struct also require bumping `PYBIND11_INTERNALS_VERSION`. /// Changes to this struct also require bumping `PYBIND11_INTERNALS_VERSION`.
struct type_info { struct type_info {
@ -260,9 +275,14 @@ struct type_info {
/* True if there is no multiple inheritance in this type's inheritance tree */ /* True if there is no multiple inheritance in this type's inheritance tree */
bool simple_ancestors : 1; bool simple_ancestors : 1;
/* for base vs derived holder_type checks */ /* 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; bool default_holder : 1;
/* true if this is a type registered with py::module_local */ /* true if this is a type registered with py::module_local */
bool module_local : 1; 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! /// On MSVC, debug and release builds are not ABI-compatible!
@ -322,25 +342,15 @@ struct type_info {
# define PYBIND11_INTERNALS_KIND "" # define PYBIND11_INTERNALS_KIND ""
#endif #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 \ #define PYBIND11_INTERNALS_ID \
"__pybind11_internals_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \ "__pybind11_internals_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \
PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \ PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB \
PYBIND11_BUILD_TYPE PYBIND11_INTERNALS_SH_DEF "__" PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__"
#define PYBIND11_MODULE_LOCAL_ID \ #define PYBIND11_MODULE_LOCAL_ID \
"__pybind11_module_local_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \ "__pybind11_module_local_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \
PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI \ PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB \
PYBIND11_BUILD_TYPE PYBIND11_INTERNALS_SH_DEF "__" PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__"
/// Each module locally stores a pointer to the `internals` data. The data /// Each module locally stores a pointer to the `internals` data. The data
/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`. /// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`.

View File

@ -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

View File

@ -9,11 +9,15 @@
#pragma once #pragma once
#include "../gil.h"
#include "../pytypes.h" #include "../pytypes.h"
#include "../trampoline_self_life_support.h"
#include "common.h" #include "common.h"
#include "descr.h" #include "descr.h"
#include "dynamic_raw_ptr_cast_if_possible.h"
#include "internals.h" #include "internals.h"
#include "typeid.h" #include "typeid.h"
#include "using_smart_holder.h"
#include "value_and_holder.h" #include "value_and_holder.h"
#include <cstdint> #include <cstdint>
@ -468,6 +472,359 @@ inline PyThreadState *get_thread_state_unchecked() {
void keep_alive_impl(handle nurse, handle patient); void keep_alive_impl(handle nurse, handle patient);
inline PyObject *make_new_instance(PyTypeObject *type); 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 { class type_caster_generic {
public: public:
PYBIND11_NOINLINE explicit type_caster_generic(const std::type_info &type_info) 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 // Base methods for generic caster; there are overridden in copyable_holder_caster
void load_value(value_and_holder &&v_h) { 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(); auto *&vptr = v_h.value_ptr();
// Lazy allocation for unallocated values: // Lazy allocation for unallocated values:
if (vptr == nullptr) { if (vptr == nullptr) {

View 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)

View File

@ -11,8 +11,9 @@
#pragma once #pragma once
#include "detail/class.h" #include "detail/class.h"
#include "detail/dynamic_raw_ptr_cast_if_possible.h"
#include "detail/init.h" #include "detail/init.h"
#include "detail/smart_holder_sfinae_hooks_only.h" #include "detail/using_smart_holder.h"
#include "attr.h" #include "attr.h"
#include "gil.h" #include "gil.h"
#include "gil_safe_call_once.h" #include "gil_safe_call_once.h"
@ -1394,8 +1395,7 @@ class generic_type : public object {
public: public:
PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check) PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check)
protected: protected:
void initialize(const type_record &rec, void initialize(const type_record &rec) {
void *(*type_caster_module_local_load)(PyObject *, const type_info *) ) {
if (rec.scope && hasattr(rec.scope, "__dict__") if (rec.scope && hasattr(rec.scope, "__dict__")
&& rec.scope.attr("__dict__").contains(rec.name)) { && rec.scope.attr("__dict__").contains(rec.name)) {
pybind11_fail("generic_type: cannot initialize type \"" + std::string(rec.name) pybind11_fail("generic_type: cannot initialize type \"" + std::string(rec.name)
@ -1424,6 +1424,9 @@ protected:
tinfo->simple_ancestors = true; tinfo->simple_ancestors = true;
tinfo->default_holder = rec.default_holder; tinfo->default_holder = rec.default_holder;
tinfo->module_local = rec.module_local; 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) { with_internals([&](internals &internals) {
auto tindex = std::type_index(*rec.type); auto tindex = std::type_index(*rec.type);
@ -1450,7 +1453,7 @@ protected:
if (rec.module_local) { if (rec.module_local) {
// Stash the local typeinfo and loader so that external modules can access it. // 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)); 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; return pmf;
} }
#ifndef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT PYBIND11_NAMESPACE_BEGIN(detail)
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
// Helper for the property_cpp_function static member functions below. // Helper for the property_cpp_function static member functions below.
// The only purpose of these functions is to support .def_readonly & .def_readwrite. // 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 // against accidents. As a side-effect, it also explains why the syntactical overhead for
// perfect forwarding is not needed. // perfect forwarding is not needed.
template <typename PM> template <typename PM>
using must_be_member_function_pointer using must_be_member_function_pointer = enable_if_t<std::is_member_pointer<PM>::value, int>;
= detail::enable_if_t<std::is_member_pointer<PM>::value, int>;
// Note that property_cpp_function is intentionally in the main pybind11 namespace, // Note that property_cpp_function is intentionally in the main pybind11 namespace,
// because user-defined specializations could be useful. // because user-defined specializations could be useful.
@ -1647,9 +1607,9 @@ using must_be_member_function_pointer
// getter and setter functions. // getter and setter functions.
// WARNING: This classic implementation can lead to dangling pointers for raw pointer members. // WARNING: This classic implementation can lead to dangling pointers for raw pointer members.
// See test_ptr() in tests/test_class_sh_property.py // See test_ptr() in tests/test_class_sh_property.py
// This implementation works as-is (and safely) for smart_holder std::shared_ptr members. // However, this implementation works as-is (and safely) for smart_holder std::shared_ptr members.
template <typename T, typename D, typename SFINAE = void> template <typename T, typename D>
struct property_cpp_function { struct property_cpp_function_classic {
template <typename PM, must_be_member_function_pointer<PM> = 0> template <typename PM, must_be_member_function_pointer<PM> = 0>
static cpp_function readonly(PM pm, const handle &hdl) { static cpp_function readonly(PM pm, const handle &hdl) {
return cpp_function([pm](const T &c) -> const D & { return c.*pm; }, is_method(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. // WARNING: Like the classic implementation, this implementation can lead to dangling pointers.
// See test_ptr() in tests/test_class_sh_property.py // 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: // 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 // https://github.com/google/clif/blob/c371a6d4b28d25d53a16e6d2a6d97305fb1be25a/clif/python/instance.h#L233
// This prevents disowning of the Python object owning the raw pointer member. // This prevents disowning of the Python object owning the raw pointer member.
template <typename T, typename D> template <typename T, typename D>
struct property_cpp_function< struct property_cpp_function_sh_raw_ptr_member {
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>> {
using drp = typename std::remove_pointer<D>::type; using drp = typename std::remove_pointer<D>::type;
template <typename PM, must_be_member_function_pointer<PM> = 0> template <typename PM, must_be_member_function_pointer<PM> = 0>
static cpp_function readonly(PM pm, const handle &hdl) { 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( return cpp_function(
[pm](handle c_hdl) -> std::shared_ptr<drp> { [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; D ptr = (*c_sp).*pm;
return std::shared_ptr<drp>(c_sp, ptr); return std::shared_ptr<drp>(c_sp, ptr);
}, },
is_method(hdl)); is_method(hdl));
} }
return property_cpp_function_classic<T, D>::readonly(pm, hdl);
}
template <typename PM, must_be_member_function_pointer<PM> = 0> template <typename PM, must_be_member_function_pointer<PM> = 0>
static cpp_function read(PM pm, const handle &hdl) { 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> template <typename PM, must_be_member_function_pointer<PM> = 0>
static cpp_function write(PM pm, const handle &hdl) { 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)); 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: // 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 // https://github.com/google/clif/blob/c371a6d4b28d25d53a16e6d2a6d97305fb1be25a/clif/python/instance.h#L233
// This prevents disowning of the Python object owning the member. // This prevents disowning of the Python object owning the member.
template <typename T, typename D> template <typename T, typename D>
struct property_cpp_function< struct property_cpp_function_sh_member_held_by_value {
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>> {
template <typename PM, must_be_member_function_pointer<PM> = 0> template <typename PM, must_be_member_function_pointer<PM> = 0>
static cpp_function readonly(PM pm, const handle &hdl) { 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( return cpp_function(
[pm](handle c_hdl) -> std::shared_ptr<typename std::add_const<D>::type> { [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); std::shared_ptr<T> c_sp
return std::shared_ptr<typename std::add_const<D>::type>(c_sp, &(c_sp.get()->*pm)); = 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)); is_method(hdl));
} }
return property_cpp_function_classic<T, D>::readonly(pm, hdl);
}
template <typename PM, must_be_member_function_pointer<PM> = 0> template <typename PM, must_be_member_function_pointer<PM> = 0>
static cpp_function read(PM pm, const handle &hdl) { 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( return cpp_function(
[pm](handle c_hdl) -> std::shared_ptr<D> { [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)); return std::shared_ptr<D>(c_sp, &(c_sp.get()->*pm));
}, },
is_method(hdl)); is_method(hdl));
} }
return property_cpp_function_classic<T, D>::read(pm, hdl);
}
template <typename PM, must_be_member_function_pointer<PM> = 0> template <typename PM, must_be_member_function_pointer<PM> = 0>
static cpp_function write(PM pm, const handle &hdl) { 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 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. // read disowns the member unique_ptr.
// write disowns the passed Python object. // write disowns the passed Python object.
// readonly is disabled (static_assert) because there is no safe & intuitive way to make the member // 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 // 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. // the unique_ptr member is deemed highly prone to misunderstandings.
template <typename T, typename D> template <typename T, typename D>
struct property_cpp_function< struct property_cpp_function_sh_unique_ptr_member {
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>> {
template <typename PM, must_be_member_function_pointer<PM> = 0> template <typename PM, must_be_member_function_pointer<PM> = 0>
static cpp_function readonly(PM, const handle &) { 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."); "def_readonly cannot be used for std::unique_ptr members.");
return cpp_function{}; // Unreachable. return cpp_function{}; // Unreachable.
} }
template <typename PM, must_be_member_function_pointer<PM> = 0> template <typename PM, must_be_member_function_pointer<PM> = 0>
static cpp_function read(PM pm, const handle &hdl) { 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( return cpp_function(
[pm](handle c_hdl) -> D { [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)}; return D{std::move(c_sp.get()->*pm)};
}, },
is_method(hdl)); is_method(hdl));
} }
return property_cpp_function_classic<T, D>::read(pm, hdl);
}
template <typename PM, must_be_member_function_pointer<PM> = 0> template <typename PM, must_be_member_function_pointer<PM> = 0>
static cpp_function write(PM pm, const handle &hdl) { 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> template <typename type_, typename... options>
class class_ : public detail::generic_type { class class_ : public detail::generic_type {
template <typename T>
using is_holder = detail::is_holder_type<type_, T>;
template <typename T> template <typename T>
using is_subtype = detail::is_strict_base_of<type_, T>; using is_subtype = detail::is_strict_base_of<type_, T>;
template <typename T> template <typename T>
using is_base = detail::is_strict_base_of<T, type_>; 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: // struct instead of using here to help MSVC:
template <typename T> template <typename T>
struct is_valid_class_option : detail::any_of<is_holder<T>, is_subtype<T>, is_base<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), none_of<std::is_same<multiple_inheritance, Extra>...>::value),
"Error: multiple inheritance bases must be specified via class_ template options"); "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; type_record record;
record.scope = scope; record.scope = scope;
record.name = name; record.name = name;
@ -1869,6 +1881,18 @@ public:
// A more fitting name would be uses_unique_ptr_holder. // A more fitting name would be uses_unique_ptr_holder.
record.default_holder = detail::is_instantiation<std::unique_ptr, holder_type>::value; 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); set_operator_new<type>(&record);
/* Register base classes specified via template arguments to class_, if any */ /* Register base classes specified via template arguments to class_, if any */
@ -1877,7 +1901,7 @@ public:
/* Process optional arguments, if any */ /* Process optional arguments, if any */
process_attributes<Extra...>::init(extra..., &record); process_attributes<Extra...>::init(extra..., &record);
generic_type_initialize(record); generic_type::initialize(record);
if (has_alias) { if (has_alias) {
with_internals([&](internals &internals) { with_internals([&](internals &internals) {
@ -2138,18 +2162,6 @@ public:
} }
private: 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 /// Initialize holder object, variant 1: object derives from enable_shared_from_this
template <typename T> template <typename T>
static void init_holder(detail::instance *inst, 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 /// 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 /// 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. /// `.owned`, a new holder will be constructed to manage the value pointer.
template <typename T = type, template <typename H = holder_type,
detail::enable_if_t<!detail::type_uses_smart_holder_type_caster<T>::value, int> = 0> detail::enable_if_t<!detail::is_smart_holder<H>::value, int> = 0>
static void init_instance(detail::instance *inst, const void *holder_ptr) { 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))); auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(type)));
if (!v_h.instance_registered()) { 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>()); init_holder(inst, v_h, (const holder_type *) holder_ptr, v_h.value_ptr<type>());
} }
template <typename T = type, #ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
typename A = type_alias,
detail::enable_if_t<detail::type_uses_smart_holder_type_caster<T>::value, int> = 0> template <typename WrappedType>
static void init_instance(detail::instance *inst, const void *holder_ptr) { static bool try_initialization_using_shared_from_this(holder_type *, WrappedType *, ...) {
detail::type_caster<T>::template init_instance_for_type<T, A>(inst, holder_ptr); 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. /// Deallocates an instance; via holder, if constructed; otherwise via operator delete.
static void dealloc(detail::value_and_holder &v_h) { static void dealloc(detail::value_and_holder &v_h) {
// We could be deallocating because we are cleaning up after a Python exception. // 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... /// Binds an existing constructor taking arguments Args...
template <typename... Args> template <typename... Args>
detail::initimpl::constructor<Args...> init() { detail::initimpl::constructor<Args...> init() {

View File

@ -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 // All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file. // BSD-style license that can be found in the LICENSE file.
#pragma once #pragma once
#include "pybind11.h" #include "pybind11.h"
#include "detail/common.h"
#include "detail/smart_holder_type_casters.h"
#undef PYBIND11_SH_AVL // Undoing #define in pybind11.h // Legacy macros introduced with smart_holder_type_casters implementation in 2021.
// Deprecated.
#define PYBIND11_SH_AVL(...) ::pybind11::smart_holder // "Smart_Holder if AVaiLable" #define PYBIND11_TYPE_CASTER_BASE_HOLDER(...)
// ---- std::shared_ptr(...) -- same length by design, to not disturb the indentation #define PYBIND11_SMART_HOLDER_TYPE_CASTERS(...)
// of existing code. #define PYBIND11_SH_AVL(...) // "Smart_Holder if AVaiLable"
#define PYBIND11_SH_DEF(...) // "Smart_Holder if DEFault"
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)

View File

@ -392,15 +392,12 @@ struct variant_caster<V<Ts...>> {
template <typename U, typename... Us> template <typename U, typename... Us>
bool load_alternative(handle src, bool convert, type_list<U, 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>(); auto caster = make_caster<U>();
if (caster.load(src, convert)) { if (caster.load(src, convert)) {
value = cast_op<U>(std::move(caster)); value = cast_op<U>(std::move(caster));
return true; return true;
} }
return load_alternative(src, convert, type_list<Us...>{}); return load_alternative(src, convert, type_list<Us...>{});
PYBIND11_WARNING_POP
} }
bool load_alternative(handle, bool, type_list<>) { return false; } bool load_alternative(handle, bool, type_list<>) { return false; }

View File

@ -4,8 +4,12 @@
#pragma once #pragma once
#include "detail/internals.h"
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
# include "detail/common.h" # include "detail/common.h"
#include "detail/smart_holder_poc.h" # include "detail/using_smart_holder.h"
# include "detail/value_and_holder.h" # include "detail/value_and_holder.h"
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
@ -40,7 +44,7 @@ struct trampoline_self_life_support {
if (value_void_ptr != nullptr) { if (value_void_ptr != nullptr) {
PyGILState_STATE threadstate = PyGILState_Ensure(); PyGILState_STATE threadstate = PyGILState_Ensure();
v_h.value_ptr() = nullptr; 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); detail::deregister_instance(v_h.inst, value_void_ptr, v_h.type);
Py_DECREF((PyObject *) v_h.inst); // Must be after deregister. Py_DECREF((PyObject *) v_h.inst); // Must be after deregister.
PyGILState_Release(threadstate); PyGILState_Release(threadstate);
@ -59,3 +63,5 @@ struct trampoline_self_life_support {
}; };
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT

View File

@ -8,5 +8,5 @@ def _to_int(s: str) -> int | str:
return s return s
__version__ = "2.14.0.dev1" __version__ = "3.0.0.dev1"
version_info = tuple(_to_int(s) for s in __version__.split(".")) version_info = tuple(_to_int(s) for s in __version__.split("."))

View File

@ -19,9 +19,16 @@ atyp rtrn_valu_atyp() { return atyp(); }
PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_module_local::atyp) PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_module_local::atyp)
PYBIND11_MODULE(class_sh_module_local_0, m) { 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; using namespace pybind11_tests::class_sh_module_local;
m.def("get_mtxt", get_mtxt); m.def("get_mtxt", get_mtxt);
m.def("rtrn_valu_atyp", rtrn_valu_atyp); m.def("rtrn_valu_atyp", rtrn_valu_atyp);
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
} }

View File

@ -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_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_module_local::atyp)
PYBIND11_MODULE(class_sh_module_local_1, m) { 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; namespace py = pybind11;
using namespace pybind11_tests::class_sh_module_local; 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; }); .def("tag", [](const atyp &) { return 1; });
m.def("get_mtxt", get_mtxt); m.def("get_mtxt", get_mtxt);
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
} }

View File

@ -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_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_module_local::atyp)
PYBIND11_MODULE(class_sh_module_local_2, m) { 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; namespace py = pybind11;
using namespace pybind11_tests::class_sh_module_local; 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; }); .def("tag", [](const atyp &) { return 2; });
m.def("get_mtxt", get_mtxt); m.def("get_mtxt", get_mtxt);
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
} }

View File

@ -60,10 +60,9 @@ detail_headers = {
"include/pybind11/detail/init.h", "include/pybind11/detail/init.h",
"include/pybind11/detail/internals.h", "include/pybind11/detail/internals.h",
"include/pybind11/detail/smart_holder_poc.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/type_caster_base.h",
"include/pybind11/detail/typeid.h", "include/pybind11/detail/typeid.h",
"include/pybind11/detail/using_smart_holder.h",
"include/pybind11/detail/value_and_holder.h", "include/pybind11/detail/value_and_holder.h",
} }

View File

@ -25,8 +25,6 @@
PYBIND11_WARNING_DISABLE_MSVC(4324) PYBIND11_WARNING_DISABLE_MSVC(4324)
// warning C4324: structure was padded due to alignment specifier // warning C4324: structure was padded due to alignment specifier
namespace {
// test_brace_initialization // test_brace_initialization
struct NoBraceInitialization { struct NoBraceInitialization {
explicit NoBraceInitialization(std::vector<int> v) : vec{std::move(v)} {} explicit NoBraceInitialization(std::vector<int> v) : vec{std::move(v)} {}
@ -36,17 +34,6 @@ struct NoBraceInitialization {
std::vector<int> vec; 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 test_class {
namespace pr4220_tripped_over_this { // PR #4227 namespace pr4220_tripped_over_this { // PR #4227
@ -67,12 +54,6 @@ void bind_empty0(py::module_ &m) {
} // namespace pr4220_tripped_over_this } // namespace pr4220_tripped_over_this
} // namespace test_class } // 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) { TEST_SUBMODULE(class_, m) {
m.def("obj_class_name", [](py::handle obj) { return py::detail::obj_class_name(obj.ptr()); }); 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__", .def_static("__new__",
[](const py::object &) { return NoConstructorNew::new_instance(); }); [](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 // test_inheritance
class Pet { class Pet {
public: public:
@ -221,6 +212,12 @@ TEST_SUBMODULE(class_, m) {
m.def("as_type", [](const py::object &ob) { return py::type(ob); }); m.def("as_type", [](const py::object &ob) { return py::type(ob); });
// test_mismatched_holder // test_mismatched_holder
struct MismatchBase1 {};
struct MismatchDerived1 : MismatchBase1 {};
struct MismatchBase2 {};
struct MismatchDerived2 : MismatchBase2 {};
m.def("mismatched_holder_1", []() { m.def("mismatched_holder_1", []() {
auto mod = py::module_::import("__main__"); auto mod = py::module_::import("__main__");
py::class_<MismatchBase1, std::shared_ptr<MismatchBase1>>(mod, "MismatchBase1"); py::class_<MismatchBase1, std::shared_ptr<MismatchBase1>>(mod, "MismatchBase1");
@ -520,6 +517,7 @@ TEST_SUBMODULE(class_, m) {
.def("throw_something", &PyPrintDestructor::throw_something); .def("throw_something", &PyPrintDestructor::throw_something);
// test_multiple_instances_with_same_pointer // test_multiple_instances_with_same_pointer
struct SamePointer {};
static SamePointer samePointer; static SamePointer samePointer;
py::class_<SamePointer, std::unique_ptr<SamePointer, py::nodelete>>(m, "SamePointer") py::class_<SamePointer, std::unique_ptr<SamePointer, py::nodelete>>(m, "SamePointer")
.def(py::init([]() { return &samePointer; })); .def(py::init([]() { return &samePointer; }));
@ -619,18 +617,12 @@ CHECK_NOALIAS(8);
static_assert(std::is_same<typename DoesntBreak##N::holder_type, \ static_assert(std::is_same<typename DoesntBreak##N::holder_type, \
std::TYPE##_ptr<BreaksBase<(N)>>>::value, \ std::TYPE##_ptr<BreaksBase<(N)>>>::value, \
"DoesntBreak" #N " has wrong holder_type!") "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(1, unique);
CHECK_HOLDER(2, unique); CHECK_HOLDER(2, unique);
CHECK_HOLDER(3, 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(4, unique);
CHECK_HOLDER(5, unique); CHECK_HOLDER(5, unique);
#else
CHECK_SMART_HOLDER(4);
CHECK_SMART_HOLDER(5);
#endif #endif
CHECK_HOLDER(6, shared); CHECK_HOLDER(6, shared);
CHECK_HOLDER(7, shared); CHECK_HOLDER(7, shared);

View File

@ -41,6 +41,18 @@ def test_instance_new():
assert cstats.alive() == 0 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(): def test_type():
assert m.check_type(1) == m.DerivedClass1 assert m.check_type(1) == m.DerivedClass1
with pytest.raises(RuntimeError) as execinfo: with pytest.raises(RuntimeError) as execinfo:

View File

@ -145,6 +145,12 @@ namespace pybind11_tests {
namespace class_sh_basic { namespace class_sh_basic {
TEST_SUBMODULE(class_sh_basic, m) { 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; namespace py = pybind11;
py::classh<atyp>(m, "atyp").def(py::init<>()).def(py::init([](const std::string &mtxt) { 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")); }, []() { return std::unique_ptr<atyp>(new atyp("rtrn_uq_automatic_reference")); },
pybind11::return_value_policy::automatic_reference); pybind11::return_value_policy::automatic_reference);
m.def("pass_shared_ptr_ptr", [](std::shared_ptr<atyp> *) {});
py::classh<LocalUnusualOpRef>(m, "LocalUnusualOpRef"); py::classh<LocalUnusualOpRef>(m, "LocalUnusualOpRef");
m.def("CallCastUnusualOpRefConstRef", m.def("CallCastUnusualOpRefConstRef",
[]() { return CastUnusualOpRefConstRef(LocalUnusualOpRef()); }); []() { return CastUnusualOpRefConstRef(LocalUnusualOpRef()); });
m.def("CallCastUnusualOpRefMovable", m.def("CallCastUnusualOpRefMovable",
[]() { return CastUnusualOpRefMovable(LocalUnusualOpRef()); }); []() { return CastUnusualOpRefMovable(LocalUnusualOpRef()); });
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
} }
} // namespace class_sh_basic } // namespace class_sh_basic

View File

@ -7,6 +7,9 @@ import pytest
from pybind11_tests import class_sh_basic as m 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(): def test_atyp_constructors():
obj = m.atyp() obj = m.atyp()
@ -132,9 +135,7 @@ def test_cannot_disown_use_count_ne_1(pass_f, rtrn_f):
stash.Add(obj) stash.Add(obj)
with pytest.raises(ValueError) as exc_info: with pytest.raises(ValueError) as exc_info:
pass_f(obj) pass_f(obj)
assert str(exc_info.value) == ( assert str(exc_info.value) == ("Cannot disown use_count != 1 (load_as_unique_ptr).")
"Cannot disown use_count != 1 (loaded_as_unique_ptr)."
)
def test_unique_ptr_roundtrip(num_round_trips=1000): 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" 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(): def test_unusual_op_ref():
# Merely to test that this still exists and built successfully. # Merely to test that this still exists and built successfully.
assert m.CallCastUnusualOpRefConstRef().__class__.__name__ == "LocalUnusualOpRef" assert m.CallCastUnusualOpRefConstRef().__class__.__name__ == "LocalUnusualOpRef"

View File

@ -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>) PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_disowning::Atype<2>)
TEST_SUBMODULE(class_sh_disowning, m) { 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; using namespace pybind11_tests::class_sh_disowning;
py::classh<Atype<1>>(m, "Atype1").def(py::init<int>()).def("get", &Atype<1>::get); 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<1>>, int)) & overloaded);
m.def("overloaded", (int (*)(std::unique_ptr<Atype<2>>, int)) & overloaded); m.def("overloaded", (int (*)(std::unique_ptr<Atype<2>>, int)) & overloaded);
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
} }

View File

@ -4,6 +4,9 @@ import pytest
from pybind11_tests import class_sh_disowning as m 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): def is_disowned(obj):
try: try:

View File

@ -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) PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_disowning_mi::Base2)
TEST_SUBMODULE(class_sh_disowning_mi, m) { 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; using namespace pybind11_tests::class_sh_disowning_mi;
py::classh<B>(m, "B") 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); py::classh<Base2>(m, "Base2").def(py::init<int>()).def("bar", &Base2::bar);
m.def("disown_base1", disown_base1); m.def("disown_base1", disown_base1);
m.def("disown_base2", disown_base2); m.def("disown_base2", disown_base2);
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
} }

View File

@ -5,6 +5,9 @@ import pytest
import env # noqa: F401 import env # noqa: F401
from pybind11_tests import class_sh_disowning_mi as m 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(): def test_diamond_inheritance():
# Very similar to test_multiple_inheritance.py:test_diamond_inheritance. # Very similar to test_multiple_inheritance.py:test_diamond_inheritance.

View File

@ -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) PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_factory_constructors::with_alias)
TEST_SUBMODULE(class_sh_factory_constructors, m) { 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; using namespace pybind11_tests::class_sh_factory_constructors;
py::classh<atyp_valu>(m, "atyp_valu") py::classh<atyp_valu>(m, "atyp_valu")
@ -177,4 +183,5 @@ TEST_SUBMODULE(class_sh_factory_constructors, m) {
[](int, int, int, int, int) { [](int, int, int, int, int) {
return std::make_shared<with_alias>(); // Invalid alias factory. return std::make_shared<with_alias>(); // Invalid alias factory.
})); }));
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
} }

View File

@ -4,6 +4,9 @@ import pytest
from pybind11_tests import class_sh_factory_constructors as m 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(): def test_atyp_factories():
assert m.atyp_valu().get_mtxt() == "Valu" assert m.atyp_valu().get_mtxt() == "Valu"

View File

@ -73,6 +73,12 @@ namespace pybind11_tests {
namespace class_sh_inheritance { namespace class_sh_inheritance {
TEST_SUBMODULE(class_sh_inheritance, m) { 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<base>(m, "base");
py::classh<drvd, base>(m, "drvd"); 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_base1", pass_cptr_base1);
m.def("pass_cptr_base2", pass_cptr_base2); m.def("pass_cptr_base2", pass_cptr_base2);
m.def("pass_cptr_drvd2", pass_cptr_drvd2); m.def("pass_cptr_drvd2", pass_cptr_drvd2);
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
} }
} // namespace class_sh_inheritance } // namespace class_sh_inheritance

View File

@ -1,7 +1,12 @@
from __future__ import annotations from __future__ import annotations
import pytest
from pybind11_tests import class_sh_inheritance as m 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(): def test_rtrn_mptr_drvd_pass_cptr_base():
d = m.rtrn_mptr_drvd() d = m.rtrn_mptr_drvd()

View File

@ -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) PYBIND11_SMART_HOLDER_TYPE_CASTERS(test_class_sh_mi_thunks::Derived)
TEST_SUBMODULE(class_sh_mi_thunks, m) { 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; using namespace test_class_sh_mi_thunks;
m.def("ptrdiff_drvd_base0", []() { m.def("ptrdiff_drvd_base0", []() {
@ -97,4 +103,5 @@ TEST_SUBMODULE(class_sh_mi_thunks, m) {
} }
return obj_der->vec.size(); return obj_der->vec.size();
}); });
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
} }

View File

@ -4,6 +4,9 @@ import pytest
from pybind11_tests import class_sh_mi_thunks as m 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(): def test_ptrdiff_drvd_base0():
ptrdiff = m.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: with pytest.raises(ValueError) as exc_info:
m.vec_size_base0_unique_ptr(obj) m.vec_size_base0_unique_ptr(obj)
assert ( assert (
str(exc_info.value) str(exc_info.value) == "Cannot disown external shared_ptr (load_as_unique_ptr)."
== "Cannot disown external shared_ptr (loaded_as_unique_ptr)."
) )

View File

@ -5,6 +5,9 @@ import class_sh_module_local_1 as m1
import class_sh_module_local_2 as m2 import class_sh_module_local_2 as m2
import pytest 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(): def test_cross_module_get_mtxt():
obj1 = m1.atyp("A") obj1 = m1.atyp("A")

View File

@ -58,6 +58,12 @@ PYBIND11_SMART_HOLDER_TYPE_CASTERS(test_class_sh_property::WithCharArrayMember)
PYBIND11_SMART_HOLDER_TYPE_CASTERS(test_class_sh_property::WithConstCharPtrMember) PYBIND11_SMART_HOLDER_TYPE_CASTERS(test_class_sh_property::WithConstCharPtrMember)
TEST_SUBMODULE(class_sh_property, m) { 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; using namespace test_class_sh_property;
py::class_<ClassicField, std::unique_ptr<ClassicField>>(m, "ClassicField") py::class_<ClassicField, std::unique_ptr<ClassicField>>(m, "ClassicField")
@ -103,4 +109,5 @@ TEST_SUBMODULE(class_sh_property, m) {
py::classh<WithConstCharPtrMember>(m, "WithConstCharPtrMember") py::classh<WithConstCharPtrMember>(m, "WithConstCharPtrMember")
.def(py::init<>()) .def(py::init<>())
.def_readonly("const_char_ptr_member", &WithConstCharPtrMember::const_char_ptr_member); .def_readonly("const_char_ptr_member", &WithConstCharPtrMember::const_char_ptr_member);
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
} }

View File

@ -7,6 +7,9 @@ import pytest
import env # noqa: F401 import env # noqa: F401
from pybind11_tests import class_sh_property as m 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.xfail("env.PYPY", reason="gc after `del field` is apparently deferred")
@pytest.mark.parametrize("m_attr", ["m_valu_readonly", "m_valu_readwrite"]) @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 assert field.num == -99
with pytest.raises(ValueError) as excinfo: with pytest.raises(ValueError) as excinfo:
m.DisownOuter(outer) 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 del field
m.DisownOuter(outer) m.DisownOuter(outer)
with pytest.raises(ValueError, match="Python instance was disowned") as excinfo: with pytest.raises(ValueError, match="Python instance was disowned") as excinfo:

View File

@ -51,6 +51,12 @@ PYBIND11_SMART_HOLDER_TYPE_CASTERS(DataField)
PYBIND11_SMART_HOLDER_TYPE_CASTERS(DataFieldsHolder) PYBIND11_SMART_HOLDER_TYPE_CASTERS(DataFieldsHolder)
TEST_SUBMODULE(class_sh_property_non_owning, m) { 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<CoreField>(m, "CoreField").def_readwrite("int_value", &CoreField::int_value);
py::classh<DataField>(m, "DataField") py::classh<DataField>(m, "DataField")
@ -65,4 +71,5 @@ TEST_SUBMODULE(class_sh_property_non_owning, m) {
py::classh<DataFieldsHolder>(m, "DataFieldsHolder") py::classh<DataFieldsHolder>(m, "DataFieldsHolder")
.def(py::init<std::size_t>()) .def(py::init<std::size_t>())
.def("vec_at", &DataFieldsHolder::vec_at, py::return_value_policy::reference_internal); .def("vec_at", &DataFieldsHolder::vec_at, py::return_value_policy::reference_internal);
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
} }

View File

@ -4,6 +4,9 @@ import pytest
from pybind11_tests import class_sh_property_non_owning as m 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("persistent_holder", [True, False])
@pytest.mark.parametrize( @pytest.mark.parametrize(

View File

@ -50,7 +50,12 @@ PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::FooSmHld)
namespace pybind11_tests { namespace pybind11_tests {
TEST_SUBMODULE(class_sh_shared_ptr_copy_move, m) { 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; namespace py = pybind11;
py::class_<FooShPtr, std::shared_ptr<FooShPtr>>(m, "FooShPtr") 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)); l.append(std::move(o));
return l; return l;
}); });
#endif #endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
} }
} // namespace pybind11_tests } // namespace pybind11_tests

View File

@ -1,7 +1,12 @@
from __future__ import annotations from __future__ import annotations
import pytest
from pybind11_tests import class_sh_shared_ptr_copy_move as m 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(): def test_shptr_copy():
txt = m.test_ShPtr_copy()[0].get_history() txt = m.test_ShPtr_copy()[0].get_history()

View File

@ -34,6 +34,7 @@ struct AbaseAlias : Abase<SerNo> {
} }
}; };
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
template <> template <>
struct AbaseAlias<1> : Abase<1>, py::trampoline_self_life_support { struct AbaseAlias<1> : Abase<1>, py::trampoline_self_life_support {
using Abase<1>::Abase; using Abase<1>::Abase;
@ -45,6 +46,7 @@ struct AbaseAlias<1> : Abase<1>, py::trampoline_self_life_support {
other_val); other_val);
} }
}; };
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
template <int SerNo> template <int SerNo>
int AddInCppRawPtr(const Abase<SerNo> *obj, int other_val) { 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> template <int SerNo>
void wrap(py::module_ m, const char *py_class_name) { 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) py::classh<Abase<SerNo>, AbaseAlias<SerNo>>(m, py_class_name)
.def(py::init<int>(), py::arg("val")) .def(py::init<int>(), py::arg("val"))
.def("Get", &Abase<SerNo>::Get) .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("AddInCppRawPtr", AddInCppRawPtr<SerNo>, py::arg("obj"), py::arg("other_val"));
m.def("AddInCppSharedPtr", AddInCppSharedPtr<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")); m.def("AddInCppUniquePtr", AddInCppUniquePtr<SerNo>, py::arg("obj"), py::arg("other_val"));
#endif
} }
} // namespace class_sh_trampoline_basic } // namespace class_sh_trampoline_basic
@ -82,6 +86,13 @@ PYBIND11_SMART_HOLDER_TYPE_CASTERS(Abase<0>)
PYBIND11_SMART_HOLDER_TYPE_CASTERS(Abase<1>) PYBIND11_SMART_HOLDER_TYPE_CASTERS(Abase<1>)
TEST_SUBMODULE(class_sh_trampoline_basic, m) { 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<0>(m, "Abase0");
wrap<1>(m, "Abase1"); wrap<1>(m, "Abase1");
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
} }

View File

@ -4,6 +4,9 @@ import pytest
from pybind11_tests import class_sh_trampoline_basic as m 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): class PyDrvd0(m.Abase0):
def __init__(self, val): def __init__(self, val):

View File

@ -38,9 +38,11 @@ protected:
Big5() : history{"DefaultConstructor"} {} Big5() : history{"DefaultConstructor"} {}
}; };
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
struct Big5Trampoline : Big5, py::trampoline_self_life_support { struct Big5Trampoline : Big5, py::trampoline_self_life_support {
using Big5::Big5; using Big5::Big5;
}; };
#endif
} // namespace class_sh_trampoline_self_life_support } // namespace class_sh_trampoline_self_life_support
} // namespace pybind11_tests } // namespace pybind11_tests
@ -50,6 +52,12 @@ using namespace pybind11_tests::class_sh_trampoline_self_life_support;
PYBIND11_SMART_HOLDER_TYPE_CASTERS(Big5) PYBIND11_SMART_HOLDER_TYPE_CASTERS(Big5)
TEST_SUBMODULE(class_sh_trampoline_self_life_support, m) { 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") py::classh<Big5, Big5Trampoline>(m, "Big5")
.def(py::init<std::string>()) .def(py::init<std::string>())
.def_readonly("history", &Big5::history); .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)); py::object o1 = py::cast(std::move(obj));
return py::make_tuple(o1, o2); return py::make_tuple(o1, o2);
}); });
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
} }

View File

@ -4,6 +4,9 @@ import pytest
import pybind11_tests.class_sh_trampoline_self_life_support as m 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): class PyBig5(m.Big5):
pass pass

View File

@ -71,9 +71,11 @@ struct SftSharedPtrStash {
} }
}; };
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
struct SftTrampoline : Sft, py::trampoline_self_life_support { struct SftTrampoline : Sft, py::trampoline_self_life_support {
using Sft::Sft; using Sft::Sft;
}; };
#endif
long use_count(const std::shared_ptr<Sft> &obj) { return obj.use_count(); } 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) PYBIND11_SMART_HOLDER_TYPE_CASTERS(SftSharedPtrStash)
TEST_SUBMODULE(class_sh_trampoline_shared_from_this, m) { 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") py::classh<Sft, SftTrampoline>(m, "Sft")
.def(py::init<const std::string &>()) .def(py::init<const std::string &>())
.def(py::init([](const std::string &history, int) { .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_unq_ptr", make_pure_cpp_sft_unq_ptr);
m.def("make_pure_cpp_sft_shd_ptr", make_pure_cpp_sft_shd_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); m.def("pass_through_shd_ptr", pass_through_shd_ptr);
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
} }

View File

@ -8,6 +8,9 @@ import pytest
import env import env
import pybind11_tests.class_sh_trampoline_shared_from_this as m 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): class PySft(m.Sft):
pass pass
@ -238,7 +241,7 @@ def test_std_make_shared_factory():
str_exc_info_value = str(exc_info.value) str_exc_info_value = str(exc_info.value)
assert ( assert (
str_exc_info_value 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" " trampoline-self-life-support for external shared_ptr to type inheriting"
" from std::enable_shared_from_this." " from std::enable_shared_from_this."
) )

View File

@ -63,6 +63,12 @@ PYBIND11_SMART_HOLDER_TYPE_CASTERS(SpGoAway)
PYBIND11_SMART_HOLDER_TYPE_CASTERS(SpGoAwayTester) PYBIND11_SMART_HOLDER_TYPE_CASTERS(SpGoAwayTester)
TEST_SUBMODULE(class_sh_trampoline_shared_ptr_cpp_arg, m) { 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 // For testing whether a python subclass of a C++ object dies when the
// last python reference is lost // last python reference is lost
@ -95,4 +101,5 @@ TEST_SUBMODULE(class_sh_trampoline_shared_ptr_cpp_arg, m) {
py::classh<SpGoAwayTester>(m, "SpGoAwayTester") py::classh<SpGoAwayTester>(m, "SpGoAwayTester")
.def(py::init<>()) .def(py::init<>())
.def_readwrite("obj", &SpGoAwayTester::m_obj); .def_readwrite("obj", &SpGoAwayTester::m_obj);
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
} }

View File

@ -4,6 +4,9 @@ import pytest
import pybind11_tests.class_sh_trampoline_shared_ptr_cpp_arg as m 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(): def test_shared_ptr_cpp_arg():
import weakref import weakref

View File

@ -39,6 +39,7 @@ PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_trampoline_unique_pt
namespace pybind11_tests { namespace pybind11_tests {
namespace class_sh_trampoline_unique_ptr { namespace class_sh_trampoline_unique_ptr {
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
class PyClass : public Class, public py::trampoline_self_life_support { class PyClass : public Class, public py::trampoline_self_life_support {
public: public:
std::unique_ptr<Class> clone() const override { std::unique_ptr<Class> clone() const override {
@ -47,11 +48,18 @@ public:
int foo() const override { PYBIND11_OVERRIDE_PURE(int, Class, foo); } int foo() const override { PYBIND11_OVERRIDE_PURE(int, Class, foo); }
}; };
#endif
} // namespace class_sh_trampoline_unique_ptr } // namespace class_sh_trampoline_unique_ptr
} // namespace pybind11_tests } // namespace pybind11_tests
TEST_SUBMODULE(class_sh_trampoline_unique_ptr, m) { 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; using namespace pybind11_tests::class_sh_trampoline_unique_ptr;
py::classh<Class, PyClass>(m, "Class") 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", [](const Class &obj) { return obj.clone(); });
m.def("clone_and_foo", [](const Class &obj) { return obj.clone()->foo(); }); m.def("clone_and_foo", [](const Class &obj) { return obj.clone()->foo(); });
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
} }

View File

@ -1,7 +1,12 @@
from __future__ import annotations from __future__ import annotations
import pytest
import pybind11_tests.class_sh_trampoline_unique_ptr as m 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): class MyClass(m.Class):
def foo(self): def foo(self):

View File

@ -31,9 +31,16 @@ namespace pybind11_tests {
namespace class_sh_unique_ptr_custom_deleter { namespace class_sh_unique_ptr_custom_deleter {
TEST_SUBMODULE(class_sh_unique_ptr_custom_deleter, m) { 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); py::classh<Pet>(m, "Pet").def_readwrite("name", &Pet::name);
m.def("create", &Pet::New); m.def("create", &Pet::New);
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
} }
} // namespace class_sh_unique_ptr_custom_deleter } // namespace class_sh_unique_ptr_custom_deleter

View File

@ -1,7 +1,12 @@
from __future__ import annotations from __future__ import annotations
import pytest
from pybind11_tests import class_sh_unique_ptr_custom_deleter as m 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(): def test_create():
pet = m.create("abc") pet = m.create("abc")

View File

@ -45,6 +45,12 @@ namespace pybind11_tests {
namespace class_sh_unique_ptr_member { namespace class_sh_unique_ptr_member {
TEST_SUBMODULE(class_sh_unique_ptr_member, m) { 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); py::classh<pointee>(m, "pointee").def(py::init<>()).def("get_int", &pointee::get_int);
m.def("make_unique_pointee", make_unique_pointee); 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("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_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); .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 } // namespace class_sh_unique_ptr_member

View File

@ -4,6 +4,9 @@ import pytest
from pybind11_tests import class_sh_unique_ptr_member as m 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(): def test_make_unique_pointee():
obj = m.make_unique_pointee() obj = m.make_unique_pointee()

View File

@ -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; } 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 { struct BaseVirtualOverrider : Base, py::trampoline_self_life_support {
using Base::Base; using Base::Base;
@ -43,6 +45,8 @@ struct CppDerivedVirtualOverrider : CppDerived, py::trampoline_self_life_support
int get() const override { PYBIND11_OVERRIDE(int, CppDerived, get); } int get() const override { PYBIND11_OVERRIDE(int, CppDerived, get); }
}; };
#endif
} // namespace class_sh_virtual_py_cpp_mix } // namespace class_sh_virtual_py_cpp_mix
} // namespace pybind11_tests } // namespace pybind11_tests
@ -53,6 +57,12 @@ PYBIND11_SMART_HOLDER_TYPE_CASTERS(CppDerivedPlain)
PYBIND11_SMART_HOLDER_TYPE_CASTERS(CppDerived) PYBIND11_SMART_HOLDER_TYPE_CASTERS(CppDerived)
TEST_SUBMODULE(class_sh_virtual_py_cpp_mix, m) { 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<Base, BaseVirtualOverrider>(m, "Base").def(py::init<>()).def("get", &Base::get);
py::classh<CppDerivedPlain, Base>(m, "CppDerivedPlain").def(py::init<>()); 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_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")); m.def("get_from_cpp_unique_ptr", get_from_cpp_unique_ptr, py::arg("b"));
#endif // PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
} }

View File

@ -4,6 +4,9 @@ import pytest
from pybind11_tests import class_sh_virtual_py_cpp_mix as m 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. class PyBase(m.Base): # Avoiding name PyDerived, for more systematic naming.
def __init__(self): def __init__(self):

View File

@ -1,18 +1,20 @@
#include "pybind11_tests.h" #include "pybind11_tests.h"
// The main purpose of this test is to ensure the suggested BOILERPLATE code block below is // The main purpose of this test was to ensure that the suggested
// correct. // BOILERPLATE code block (NOW DEPRECATED!) block below is correct.
// Copy this block of code into your project. // Copy this block of code into your project.
// Replace FOOEXT with the name of 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 #ifdef FOOEXT_USING_PYBIND11_SMART_HOLDER
# include <pybind11/smart_holder.h> # include <pybind11/smart_holder.h>
#else #else
# include <pybind11/pybind11.h> # include <pybind11/pybind11.h>
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
# ifndef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
template <typename type_, typename... options> template <typename type_, typename... options>
using classh = class_<type_, options...>; using classh = class_<type_, options...>;
# endif
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
# ifndef PYBIND11_SH_AVL # ifndef PYBIND11_SH_AVL
# define PYBIND11_SH_AVL(...) std::shared_ptr<__VA_ARGS__> // "Smart_Holder if AVaiLable" # 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(...) # define PYBIND11_TYPE_CASTER_BASE_HOLDER(...)
# endif # endif
#endif #endif
// BOILERPLATE END // BOILERPLATE END DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED
namespace { namespace {
struct FooUc {}; struct FooUc {};
@ -37,8 +39,8 @@ struct FooSc {};
struct FooSp {}; struct FooSp {};
} // namespace } // namespace
PYBIND11_SMART_HOLDER_TYPE_CASTERS(FooUp) PYBIND11_SMART_HOLDER_TYPE_CASTERS(FooUp) // DEPRECATED
PYBIND11_SMART_HOLDER_TYPE_CASTERS(FooSp) PYBIND11_SMART_HOLDER_TYPE_CASTERS(FooSp) // DEPRECATED
PYBIND11_TYPE_CASTER_BASE_HOLDER(FooSa, std::shared_ptr<FooSa>) PYBIND11_TYPE_CASTER_BASE_HOLDER(FooSa, std::shared_ptr<FooSa>)

View File

@ -66,7 +66,7 @@ PYBIND11_EMBEDDED_MODULE(widget_module, m) {
PYBIND11_EMBEDDED_MODULE(trampoline_module, m) { PYBIND11_EMBEDDED_MODULE(trampoline_module, m) {
py::class_<test_override_cache_helper, py::class_<test_override_cache_helper,
test_override_cache_helper_trampoline, 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(py::init_alias<>())
.def("func", &test_override_cache_helper::func); .def("func", &test_override_cache_helper::func);
} }

View File

@ -250,7 +250,7 @@ TEST_SUBMODULE(factory_constructors, m) {
}; };
// test_init_factory_basic, test_init_factory_casting // 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 pyTestFactory3
.def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct3(v); })) .def(py::init([](pointer_tag, int v) { return TestFactoryHelper::construct3(v); }))
.def(py::init([](shared_ptr_tag) { return TestFactoryHelper::construct3(); })); .def(py::init([](shared_ptr_tag) { return TestFactoryHelper::construct3(); }));
@ -277,12 +277,12 @@ TEST_SUBMODULE(factory_constructors, m) {
.def_readwrite("value", &TestFactory3::value); .def_readwrite("value", &TestFactory3::value);
// test_init_factory_casting // 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 .def(py::init(c4a)) // pointer
; ;
// Doesn't need to be registered, but registering makes getting ConstructorStats easier: // 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 // test_init_factory_alias
// Alias testing // Alias testing
@ -305,7 +305,7 @@ TEST_SUBMODULE(factory_constructors, m) {
// test_init_factory_dual // test_init_factory_dual
// Separate alias constructor testing // 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([](int i) { return TestFactory7(i); }, [](int i) { return PyTF7(i); }))
.def(py::init([](pointer_tag, int i) { return new TestFactory7(i); }, .def(py::init([](pointer_tag, int i) { return new TestFactory7(i); },
[](pointer_tag, int i) { return new PyTF7(i); })) [](pointer_tag, int i) { return new PyTF7(i); }))

View File

@ -280,11 +280,10 @@ def test_init_factory_dual():
assert not g1.has_alias() assert not g1.has_alias()
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
PythFactory7(tag.shared_ptr, tag.invalid_base, 14) PythFactory7(tag.shared_ptr, tag.invalid_base, 14)
assert str(excinfo.value) in ( assert (
"pybind11::init(): construction failed: returned holder-wrapped instance is not an " str(excinfo.value)
"alias instance", == "pybind11::init(): construction failed: returned holder-wrapped instance is not an "
"pybind11::init(): construction failed: returned std::shared_ptr pointee is not an " "alias instance"
"alias instance",
) )
assert [i.alive() for i in cstats] == [13, 7] assert [i.alive() for i in cstats] == [13, 7]

View File

@ -121,7 +121,7 @@ public:
}; };
int none1(const NoneTester &obj) { return obj.answer; } int none1(const NoneTester &obj) { return obj.answer; }
int none2(NoneTester *obj) { return obj ? obj->answer : -1; } 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 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; } 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. // [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works.
// test_accepts_none // 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_none1", &none1, py::arg{}.none(false));
m.def("no_none2", &none2, 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_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("no_none5", &none5, py::arg{}.none(false));
m.def("ok_none1", &none1); m.def("ok_none1", &none1);
m.def("ok_none2", &none2, py::arg{}.none(true)); m.def("ok_none2", &none2, py::arg{}.none(true));
m.def("ok_none3", &none3); 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)); 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", &none2, "a"_a.none(false));
m.def("no_none_kwarg_kw_only", &none2, py::kw_only(), "a"_a.none(false)); m.def("no_none_kwarg_kw_only", &none2, py::kw_only(), "a"_a.none(false));

View File

@ -387,10 +387,12 @@ def test_accepts_none(msg):
assert m.no_none1(a) == 42 assert m.no_none1(a) == 42
assert m.no_none2(a) == 42 assert m.no_none2(a) == 42
assert m.no_none3(a) == 42 assert m.no_none3(a) == 42
assert m.no_none4(a) == 42
assert m.no_none5(a) == 42 assert m.no_none5(a) == 42
assert m.ok_none1(a) == 42 assert m.ok_none1(a) == 42
assert m.ok_none2(a) == 42 assert m.ok_none2(a) == 42
assert m.ok_none3(a) == 42 assert m.ok_none3(a) == 42
assert m.ok_none4(a) == 42
assert m.ok_none5(a) == 42 assert m.ok_none5(a) == 42
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
@ -402,6 +404,9 @@ def test_accepts_none(msg):
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
m.no_none3(None) m.no_none3(None)
assert "incompatible function arguments" in str(excinfo.value) 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: with pytest.raises(TypeError) as excinfo:
m.no_none5(None) m.no_none5(None)
assert "incompatible function arguments" in str(excinfo.value) 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: # The rest take the argument as pointer or holder, and accept None:
assert m.ok_none2(None) == -1 assert m.ok_none2(None) == -1
assert m.ok_none3(None) == -1 assert m.ok_none3(None) == -1
assert m.ok_none4(None) == -1
assert m.ok_none5(None) == -1 assert m.ok_none5(None) == -1
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
@ -437,14 +443,6 @@ def test_accepts_none(msg):
m.no_none_kwarg_kw_only(a=None) m.no_none_kwarg_kw_only(a=None)
assert "incompatible function arguments" in str(excinfo.value) 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(): def test_casts_none():
"""#2778: implicit casting from None to object (not pointer)""" """#2778: implicit casting from None to object (not pointer)"""

View File

@ -148,15 +148,15 @@ TEST_SUBMODULE(multiple_inheritance, m) {
// test_multiple_inheritance_virtbase // test_multiple_inheritance_virtbase
// Test the case where not all base classes are specified, and where pybind11 requires the // 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::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(py::init<int>())
.def("foo", &Base1a::foo); .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(py::init<int>())
.def("bar", &Base2a::bar); .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()) m, "Base12a", py::multiple_inheritance())
.def(py::init<int, int>()); .def(py::init<int, int>());
@ -173,14 +173,14 @@ TEST_SUBMODULE(multiple_inheritance, m) {
}; };
struct I801E : I801B3, I801D {}; 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(py::init<>())
.def_readonly("a", &I801B1::a); .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(py::init<>())
.def_readonly("b", &I801B2::b); .def_readonly("b", &I801B2::b);
py::class_<I801C, I801B1, I801B2, PYBIND11_SH_DEF(I801C)>(m, "I801C").def(py::init<>()); py::class_<I801C, I801B1, I801B2, std::shared_ptr<I801C>>(m, "I801C").def(py::init<>());
py::class_<I801D, I801C, PYBIND11_SH_DEF(I801D)>(m, "I801D").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 // 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 // known instance even when the pointer value is unequal (i.e. due to a non-first

View File

@ -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, shared_ptr_with_addressof_operator<T>);
PYBIND11_DECLARE_HOLDER_TYPE(T, unique_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) { TEST_SUBMODULE(smart_ptr, m) {
// Please do not interleave `struct` and `class` definitions with bindings code, // Please do not interleave `struct` and `class` definitions with bindings code,
// but implement `struct`s and `class`es in the anonymous namespace above. // but implement `struct`s and `class`es in the anonymous namespace above.

View File

@ -301,9 +301,9 @@ def test_smart_ptr_from_default():
instance = m.HeldByDefaultHolder() instance = m.HeldByDefaultHolder()
with pytest.raises(RuntimeError) as excinfo: with pytest.raises(RuntimeError) as excinfo:
m.HeldByDefaultHolder.load_shared_ptr(instance) m.HeldByDefaultHolder.load_shared_ptr(instance)
assert str(excinfo.value) in ( assert (
"Unable to load a smart-pointer type from a non-smart_holder instance.", "Unable to load a custom holder type from a "
"Unable to load a custom holder type from a default-holder instance", "default-holder instance" in str(excinfo.value)
) )

View File

@ -406,7 +406,7 @@ TEST_SUBMODULE(virtual_functions, m) {
py::class_<test_override_cache_helper, py::class_<test_override_cache_helper,
test_override_cache_helper_trampoline, 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(py::init_alias<>())
.def("func", &test_override_cache_helper::func); .def("func", &test_override_cache_helper::func);

View File

@ -1,4 +1,4 @@
#include <pybind11/smart_holder.h> #include <pybind11/pybind11.h>
#include "number_bucket.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")); .def("add", &WrappedType::add, py::arg("other"));
} }
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
template <typename T> template <typename T>
class padded_unique_ptr { class padded_unique_ptr {
std::unique_ptr<T> ptr; std::unique_ptr<T> ptr;
@ -35,20 +37,21 @@ public:
static_assert(sizeof(padded_unique_ptr<nb_pu>) == sizeof(py::smart_holder), static_assert(sizeof(padded_unique_ptr<nb_pu>) == sizeof(py::smart_holder),
"Unexpected sizeof mismatch."); "Unexpected sizeof mismatch.");
#endif
} // namespace hc } // namespace hc
#ifdef PYBIND11_HAVE_INTERNALS_WITH_SMART_HOLDER_SUPPORT
PYBIND11_DECLARE_HOLDER_TYPE(T, hc::padded_unique_ptr<T>); PYBIND11_DECLARE_HOLDER_TYPE(T, hc::padded_unique_ptr<T>);
#endif
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)
PYBIND11_MODULE(pybind11_ubench_holder_comparison, m) { PYBIND11_MODULE(pybind11_ubench_holder_comparison, m) {
using namespace hc; 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_up, std::unique_ptr<nb_up>>(m, "number_bucket_up");
wrap_number_bucket<nb_sp, std::shared_ptr<nb_sp>>(m, "number_bucket_sp"); 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_pu, padded_unique_ptr<nb_pu>>(m, "number_bucket_pu");
wrap_number_bucket<nb_sh, py::smart_holder>(m, "number_bucket_sh"); wrap_number_bucket<nb_sh, py::smart_holder>(m, "number_bucket_sh");
#endif
} }