Skip to content

Commit d368b72

Browse files
authored
Connecting PYBIND11_INTERNALS_VERSION to PYBIND11_USE_SMART_HOLDER_AS_DEFAULT. (#2939)
* Connecting PYBIND11_INTERNALS_VERSION to PYBIND11_USE_SMART_HOLDER_AS_DEFAULT. * Adding section: Classic / Conservative / Progressive cross-module compatibility
1 parent 1c8795a commit d368b72

File tree

2 files changed

+101
-17
lines changed

2 files changed

+101
-17
lines changed

README_smart_holder.rst

+94-17
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Overview
3333
What is fundamentally different?
3434
--------------------------------
3535

36-
- Traditional pybind11 has the concept of "smart-pointer is holder".
36+
- Classic pybind11 has the concept of "smart-pointer is holder".
3737
Interoperability between smart-pointers is completely missing. For
3838
example, when using ``std::shared_ptr`` as holder, ``return``-ing
3939
a ``std::unique_ptr`` leads to undefined runtime behavior
@@ -76,8 +76,8 @@ Conservative or Progressive mode?
7676
=================================
7777

7878
It depends. To a first approximation, for a stand-alone, new project, the
79-
progressive mode will be easiest to use. For larger projects or projects
80-
that integrate with third-party pybind11-based projects, the conservative
79+
Progressive mode will be easiest to use. For larger projects or projects
80+
that integrate with third-party pybind11-based projects, the Conservative
8181
mode may be more practical, at least initially, although it comes with the
8282
disadvantage of having to use the ``PYBIND11_SMART_HOLDER_TYPE_CASTERS`` macro.
8383

@@ -101,7 +101,7 @@ holder:
101101
py::classh<Foo>(m, "Foo");
102102
}
103103
104-
There are three small differences compared to traditional pybind11:
104+
There are three small differences compared to classic pybind11:
105105

106106
- ``#include <pybind11/smart_holder.h>`` is used instead of
107107
``#include <pybind11/pybind11.h>``.
@@ -120,40 +120,40 @@ could probably be avoided with a little bit of template metaprogramming).
120120

121121
To the 3rd bullet point, the macro also needs to appear in other translation
122122
units with pybind11 bindings that involve Python⇄C++ conversions for
123-
`Foo`. This is the biggest inconvenience of the conservative mode. Practially,
123+
`Foo`. This is the biggest inconvenience of the Conservative mode. Practically,
124124
at a larger scale it is best to work with a pair of `.h` and `.cpp` files
125125
for the bindings code, with the macros in the `.h` files.
126126

127127

128128
Progressive mode
129129
----------------
130130

131-
To work in progressive mode:
131+
To work in Progressive mode:
132132

133133
- Add ``-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT`` to the compilation commands.
134134

135135
- Remove any ``std::shared_ptr<...>`` holders from existing ``py::class_``
136-
instantiations (#HelpAppreciated this could probably be avoided with a little
137-
bit of template metaprogramming).
136+
instantiations.
138137

139138
- Only if custom smart-pointers are used: the
140139
`PYBIND11_TYPE_CASTER_BASE_HOLDER` macro is needed [`example
141140
<https://github.com/pybind/pybind11/blob/2f624af1ac8571d603df2d70cb54fc7e2e3a356a/tests/test_multiple_inheritance.cpp#L72>`_].
142141

143-
Overall this is probably easier to work with than the conservative mode, but
142+
Overall this is probably easier to work with than the Conservative mode, but
144143

145144
- the macro inconvenience is shifted from ``py::smart_holder`` to custom
146145
smart-pointers (but probably much more rarely needed).
147146

148147
- it will not interoperate with other extensions built against master or
149-
stable, or extensions built in conservative mode.
148+
stable, or extensions built in Conservative mode (see the cross-module
149+
compatibility section below).
150150

151151

152-
Transition from conservative to progressive mode
152+
Transition from Conservative to Progressive mode
153153
------------------------------------------------
154154

155155
This still has to be tried out more in practice, but in small-scale situations
156-
it may be feasible to switch directly to progressive mode in a break-fix
156+
it may be feasible to switch directly to Progressive mode in a break-fix
157157
fashion. In large-scale situations it seems more likely that an incremental
158158
approach is needed, which could mean incrementally converting ``py::class_``
159159
to ``py::classh`` including addition of the macros, then flip the switch,
@@ -175,6 +175,83 @@ still being able to build the same code with classic pybind11. Please see
175175
tests/test_classh_mock.cpp for an example.
176176

177177

178+
Classic / Conservative / Progressive cross-module compatibility
179+
---------------------------------------------------------------
180+
181+
Currently there are essentially three modes for building a pybind11 extension
182+
module:
183+
184+
- Classic: pybind11 stable (e.g. v2.6.2) or current master branch.
185+
186+
- Conservative: pybind11 smart_holder branch.
187+
188+
- Progressive: pybind11 smart_holder branch with
189+
``-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT``.
190+
191+
In environments that mix extension modules built with different modes,
192+
this is the compatibility matrix for ``py::class_``-wrapped types:
193+
194+
.. list-table:: Compatibility matrix
195+
:widths: auto
196+
:header-rows: 2
197+
198+
* -
199+
-
200+
-
201+
- Module 2
202+
-
203+
* -
204+
-
205+
- Classic
206+
- Conservative
207+
- Progressive
208+
* -
209+
- **Classic**
210+
- full
211+
- one-and-a-half-way
212+
- isolated
213+
* - **Module 1**
214+
- **Conservative**
215+
- one-and-a-half-way
216+
- full
217+
- isolated
218+
* -
219+
- **Progressive**
220+
- isolated
221+
- isolated
222+
- full
223+
224+
Mixing Classic+Progressive or Conservative+Progressive is very easy to
225+
understand: the extension modules are essentially completely isolated from
226+
each other. This is in fact just the same as using pybind11 versions with
227+
differing `"internals version"
228+
<https://github.com/pybind/pybind11/blob/114be7f4ade0ad798cd4c7f5d65ebe4ba8bd892d/include/pybind11/detail/internals.h#L95>`_
229+
in the past. While this is easy to understand, there is also no incremental
230+
transition path between Classic and Progressive.
231+
232+
The Conservative mode enables incremental transitions, but at the cost of
233+
more complexity. Types wrapped in a Classic module are fully compatible with
234+
a Conservative module. However, a type wrapped in a Conservative module is
235+
compatible with a Classic module only if ``py::smart_holder`` is **not** used
236+
(for that type). A type wrapped with ``py::smart_holder`` is incompatible with
237+
a Classic module. This is an important pitfall to keep in mind: attempts to use
238+
``py::smart_holder``-wrapped types in a Classic module will lead to undefined
239+
runtime behavior, such as a SEGFAULT. This is a more general flavor of the
240+
long-standing issue `#1138 <https://github.com/pybind/pybind11/issues/1138>`_,
241+
often referred to as "holder mismatch". It is important to note that the
242+
pybind11 smart_holder branch solves the smart-pointer interoperability issue,
243+
but not the more general holder mismatch issue. — Unfortunately the existing
244+
pybind11 internals do not track holder runtime type information, therefore
245+
the holder mismatch issue cannot be solved in a fashion that would allow
246+
an incremental transition, which is the whole point of the Conservative
247+
mode. Please proceed with caution.
248+
249+
Another pitfall worth pointing out specifically, although it follows
250+
from the previous: mixing base and derived classes between Classic and
251+
Conservative modules means that neither the base nor the derived class can
252+
use ``py::smart_holder``.
253+
254+
178255
Trampolines and std::unique_ptr
179256
-------------------------------
180257

@@ -191,7 +268,7 @@ inherit from ``py::trampoline_self_life_support``, for example:
191268
...
192269
};
193270
194-
This is the only difference compared to traditional pybind11. A fairly
271+
This is the only difference compared to classic pybind11. A fairly
195272
minimal but complete example is tests/test_class_sh_trampoline_unique_ptr.cpp.
196273

197274

@@ -200,7 +277,7 @@ Ideas for the long-term
200277

201278
The macros are clearly an inconvenience in many situations. Highly
202279
speculative: to avoid the need for the macros, a potential approach would
203-
be to combine the traditional implementation (``type_caster_base``) with
280+
be to combine the classic implementation (``type_caster_base``) with
204281
the ``smart_holder_type_caster``, but this will probably be very messy and
205282
not great as a long-term solution. The ``type_caster_base`` code is very
206283
complex already. A more maintainable approach long-term could be to work
@@ -213,8 +290,8 @@ GitHub testing of PRs against the smart_holder branch
213290
-----------------------------------------------------
214291

215292
PRs against the smart_holder branch need to be tested in both
216-
modes (conservative, progressive), with the only difference that
217-
``PYBIND11_USE_SMART_HOLDER_AS_DEFAULT`` is defined for progressive mode
293+
modes (Conservative, Progressive), with the only difference that
294+
``PYBIND11_USE_SMART_HOLDER_AS_DEFAULT`` is defined for Progressive mode
218295
testing. Currently this is handled simply by creating a secondary PR with a
219296
one-line change in ``include/pybind11/detail/smart_holder_sfinae_hooks_only.h``
220297
(as in e.g. `PR #2879 <https://github.com/pybind/pybind11/pull/2879>`_). It
@@ -230,7 +307,7 @@ primary PR as needed:
230307
git checkout sh_secondary_pr
231308
git rebase -X theirs sh_primary_pr
232309
git diff # To verify that the one-line change in smart_holder_sfinae_hooks_only.h is the only diff.
233-
git push --force-with-lease # This will trigger the GitHub Actions for the progressive mode.
310+
git push --force-with-lease # This will trigger the GitHub Actions for the Progressive mode.
234311
235312
The second time through this will only take a minute or two.
236313

include/pybind11/detail/internals.h

+7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#pragma once
1111

1212
#include "../pytypes.h"
13+
#include "smart_holder_sfinae_hooks_only.h"
1314

1415
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
1516
PYBIND11_NAMESPACE_BEGIN(detail)
@@ -150,7 +151,13 @@ struct type_info {
150151
};
151152

152153
/// Tracks the `internals` and `type_info` ABI version independent of the main library version
154+
#ifndef PYBIND11_USE_SMART_HOLDER_AS_DEFAULT
153155
#define PYBIND11_INTERNALS_VERSION 4
156+
#else
157+
// See README_smart_holder.rst:
158+
// Classic / Conservative / Progressive cross-module compatibility
159+
#define PYBIND11_INTERNALS_VERSION 1004
160+
#endif
154161

155162
/// On MSVC, debug and release builds are not ABI-compatible!
156163
#if defined(_MSC_VER) && defined(_DEBUG)

0 commit comments

Comments
 (0)