@@ -33,7 +33,7 @@ Overview
33
33
What is fundamentally different?
34
34
--------------------------------
35
35
36
- - Traditional pybind11 has the concept of "smart-pointer is holder".
36
+ - Classic pybind11 has the concept of "smart-pointer is holder".
37
37
Interoperability between smart-pointers is completely missing. For
38
38
example, when using ``std::shared_ptr `` as holder, ``return ``-ing
39
39
a ``std::unique_ptr `` leads to undefined runtime behavior
@@ -76,8 +76,8 @@ Conservative or Progressive mode?
76
76
=================================
77
77
78
78
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
81
81
mode may be more practical, at least initially, although it comes with the
82
82
disadvantage of having to use the ``PYBIND11_SMART_HOLDER_TYPE_CASTERS `` macro.
83
83
@@ -101,7 +101,7 @@ holder:
101
101
py::classh<Foo>(m, "Foo");
102
102
}
103
103
104
- There are three small differences compared to traditional pybind11:
104
+ There are three small differences compared to classic pybind11:
105
105
106
106
- ``#include <pybind11/smart_holder.h> `` is used instead of
107
107
``#include <pybind11/pybind11.h> ``.
@@ -120,40 +120,40 @@ could probably be avoided with a little bit of template metaprogramming).
120
120
121
121
To the 3rd bullet point, the macro also needs to appear in other translation
122
122
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 ,
124
124
at a larger scale it is best to work with a pair of `.h ` and `.cpp ` files
125
125
for the bindings code, with the macros in the `.h ` files.
126
126
127
127
128
128
Progressive mode
129
129
----------------
130
130
131
- To work in progressive mode:
131
+ To work in Progressive mode:
132
132
133
133
- Add ``-DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT `` to the compilation commands.
134
134
135
135
- 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.
138
137
139
138
- Only if custom smart-pointers are used: the
140
139
`PYBIND11_TYPE_CASTER_BASE_HOLDER ` macro is needed [`example
141
140
<https://github.com/pybind/pybind11/blob/2f624af1ac8571d603df2d70cb54fc7e2e3a356a/tests/test_multiple_inheritance.cpp#L72> `_].
142
141
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
144
143
145
144
- the macro inconvenience is shifted from ``py::smart_holder `` to custom
146
145
smart-pointers (but probably much more rarely needed).
147
146
148
147
- 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).
150
150
151
151
152
- Transition from conservative to progressive mode
152
+ Transition from Conservative to Progressive mode
153
153
------------------------------------------------
154
154
155
155
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
157
157
fashion. In large-scale situations it seems more likely that an incremental
158
158
approach is needed, which could mean incrementally converting ``py::class_ ``
159
159
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
175
175
tests/test_classh_mock.cpp for an example.
176
176
177
177
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
+
178
255
Trampolines and std::unique_ptr
179
256
-------------------------------
180
257
@@ -191,7 +268,7 @@ inherit from ``py::trampoline_self_life_support``, for example:
191
268
...
192
269
};
193
270
194
- This is the only difference compared to traditional pybind11. A fairly
271
+ This is the only difference compared to classic pybind11. A fairly
195
272
minimal but complete example is tests/test_class_sh_trampoline_unique_ptr.cpp.
196
273
197
274
@@ -200,7 +277,7 @@ Ideas for the long-term
200
277
201
278
The macros are clearly an inconvenience in many situations. Highly
202
279
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
204
281
the ``smart_holder_type_caster ``, but this will probably be very messy and
205
282
not great as a long-term solution. The ``type_caster_base `` code is very
206
283
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
213
290
-----------------------------------------------------
214
291
215
292
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
218
295
testing. Currently this is handled simply by creating a secondary PR with a
219
296
one-line change in ``include/pybind11/detail/smart_holder_sfinae_hooks_only.h ``
220
297
(as in e.g. `PR #2879 <https://github.com/pybind/pybind11/pull/2879 >`_). It
@@ -230,7 +307,7 @@ primary PR as needed:
230
307
git checkout sh_secondary_pr
231
308
git rebase -X theirs sh_primary_pr
232
309
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.
234
311
235
312
The second time through this will only take a minute or two.
236
313
0 commit comments