Skip to content

Commit 4b9bc48

Browse files
committed
Adding section: Classic / Conservative / Progressive cross-module compatibility
1 parent 9d3a8f9 commit 4b9bc48

File tree

2 files changed

+57
-23
lines changed

2 files changed

+57
-23
lines changed

README_smart_holder.rst

+56-22
Original file line numberDiff line numberDiff line change
@@ -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

@@ -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,17 +175,21 @@ 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
178+
Classic / Conservative / Progressive cross-module compatibility
179179
---------------------------------------------------------------
180180

181-
Currently there are essentially three modes for building a pybind11 extension module:
181+
Currently there are essentially three modes for building a pybind11 extension
182+
module:
182183

183-
* Classic: pybind11 stable (e.g. v2.6.2) or current master
184-
* Conservative: pybind11 smart_holder
185-
* Progressive: pybind11 smart_holder with ``-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT``
184+
- Classic: pybind11 stable (e.g. v2.6.2) or current master branch.
186185

187-
In environments that mix modules built with different modes, this is the compatibility
188-
matrix for ``py::class_``-wrapped types:
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:
189193

190194
.. list-table:: Compatibility matrix
191195
:widths: auto
@@ -204,11 +208,11 @@ matrix for ``py::class_``-wrapped types:
204208
* -
205209
- **Classic**
206210
- full
207-
- one-way
211+
- one-and-a-half-way
208212
- isolated
209213
* - **Module 1**
210214
- **Conservative**
211-
- one-way
215+
- one-and-a-half-way
212216
- full
213217
- isolated
214218
* -
@@ -217,6 +221,36 @@ matrix for ``py::class_``-wrapped types:
217221
- isolated
218222
- full
219223

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+
220254

221255
Trampolines and std::unique_ptr
222256
-------------------------------
@@ -256,8 +290,8 @@ GitHub testing of PRs against the smart_holder branch
256290
-----------------------------------------------------
257291

258292
PRs against the smart_holder branch need to be tested in both
259-
modes (conservative, progressive), with the only difference that
260-
``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
261295
testing. Currently this is handled simply by creating a secondary PR with a
262296
one-line change in ``include/pybind11/detail/smart_holder_sfinae_hooks_only.h``
263297
(as in e.g. `PR #2879 <https://github.com/pybind/pybind11/pull/2879>`_). It
@@ -273,7 +307,7 @@ primary PR as needed:
273307
git checkout sh_secondary_pr
274308
git rebase -X theirs sh_primary_pr
275309
git diff # To verify that the one-line change in smart_holder_sfinae_hooks_only.h is the only diff.
276-
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.
277311
278312
The second time through this will only take a minute or two.
279313

include/pybind11/detail/internals.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ struct type_info {
155155
#define PYBIND11_INTERNALS_VERSION 4
156156
#else
157157
// See README_smart_holder.rst:
158-
// Classic / conservative / progressive cross-module compatibility
158+
// Classic / Conservative / Progressive cross-module compatibility
159159
#define PYBIND11_INTERNALS_VERSION 1004
160160
#endif
161161

0 commit comments

Comments
 (0)