Skip to content

Commit 647df8b

Browse files
committed
Fix all known bugs with interop support, update to pymetabind 0.3, add docs and tests
1 parent 18e8808 commit 647df8b

File tree

19 files changed

+1979
-282
lines changed

19 files changed

+1979
-282
lines changed

docs/advanced/classes.rst

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -972,9 +972,14 @@ Module-local class bindings
972972
===========================
973973

974974
When creating a binding for a class, pybind11 by default makes that binding
975-
"global" across modules. What this means is that a type defined in one module
976-
can be returned from any module resulting in the same Python type. For
977-
example, this allows the following:
975+
"global" across modules. What this means is that instances whose type is
976+
defined with a ``py::class_`` statement in one module can be passed to or
977+
returned from a function defined in any other module that is "ABI compatible"
978+
with the first, i.e., that was built with sufficiently similar versions of
979+
pybind11 and of the C++ compiler and C++ standard library. The internal data
980+
structures that pybind11 uses to keep track of its types and instances are
981+
shared just as they would be if everything were in the same module.
982+
For example, this allows the following:
978983

979984
.. code-block:: cpp
980985

docs/advanced/interop.rst

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
.. _interop:
2+
3+
Interoperating with foreign bindings
4+
====================================
5+
6+
When you bind a function with pybind11 that has a parameter of type ``T``,
7+
its typical behavior (if ``T`` does not use a :ref:`built-in <conversion_table>`
8+
or :ref:`custom type caster <custom_type_caster>`) is to only accept arguments
9+
for that parameter that are Python instances of the type created by a
10+
``py::class_<T>(...)`` binding statement, or that derive from that type,
11+
or that match a defined :ref:`implicit conversion <implicit_conversions>`
12+
to that type (``py::implicitly_convertible<Something, T>()``). Moreover,
13+
if the ``py::class_<T>(...)`` binding statement was written in a different
14+
pybind11 extension than the function that needs the ``T``, the two extensions
15+
must be ABI-compatible: they must use similar enough versions of pybind11 that
16+
it's safe for their respective copies of pybind11 to share their data
17+
structures with each other.
18+
19+
Sometimes, you might want more flexibility than that:
20+
21+
- Perhaps you have a large codebase containing a number of different pybind11
22+
extension modules that share types with each other, and you want to upgrade
23+
to a new and ABI-incompatible release of pybind11 in some fashion other than
24+
"upgrade every module at the same time".
25+
26+
- Perhaps you need to work with types provided by a third-party extension
27+
such as PyTorch, which uses pybind11 but not the version you prefer.
28+
29+
- Perhaps you'd like to port some of the especially performance-sensitive
30+
parts of your bindings to a faster but less featureful binding framework,
31+
without leaving the comfortable world of pybind11 behind entirely.
32+
33+
To handle such situations, pybind11 can be taught to interoperate with bindings
34+
that were not created using pybind11, or that were created with an
35+
ABI-incompatible version of pybind11 (as long as it is new enough to support
36+
this feature). For example, you can define a class binding for ``Pet`` in one
37+
extension that is written using pybind11, and then write function bindings for
38+
``void groom(Pet&)`` and ``Pet clone(const Pet&)`` in a separate extension
39+
module that is written using `nanobind <https://nanobind.readthedocs.io/>`__, or
40+
vice versa. The interoperability mechanism described here allows each framework
41+
to figure out (among other things) how to get a reference to a C++ ``Pet`` out
42+
of a Python object provided by the other framework that supposedly contains a
43+
Pet, without knowing anything about how that framework lays out its instances.
44+
From pybind11's perspective, nanobind and its bindings are considered "foreign".
45+
46+
In order for pybind11 to interoperate with another framework in this way, the
47+
other framework must support the `pymetabind
48+
<https://github.com/hudson-trading/pymetabind>`__ standard. See that link for
49+
a list of frameworks that claim to do so.
50+
51+
Exporting pybind11 bindings for other frameworks to use
52+
-------------------------------------------------------
53+
54+
In order for a type bound by pybind11 to be usable by other binding frameworks,
55+
pybind11 must allocate a small data structure describing how others should work
56+
with that type. While the overhead of this is low, it is not zero, so pybind11
57+
only does so for types where you request it. Pass the Python type object to
58+
``py::export_for_interop()``, or use ``py::interoperate_by_default()`` if you
59+
want all types to be exported automatically as soon as they are bound.
60+
61+
You can use ``py::type::of<T>()`` to get the Python type object for
62+
a C++ type. For example:
63+
64+
.. code-block:: cpp
65+
66+
PYBIND11_MODULE(my_ext, m) {
67+
auto pet = py::class_<Pet>(m, "Pet")
68+
.def(py::init<std::string>())
69+
.def("speak", &Pet::speak);
70+
71+
// These two lines are equivalent:
72+
py::export_for_interop(pet);
73+
py::export_for_interop(py::type::of<Pet>());
74+
}
75+
76+
77+
Importing other frameworks' bindings for pybind11 to use
78+
--------------------------------------------------------
79+
80+
In order for pybind11 to interoperate with a foreign type, the foreign framework
81+
that bound the type must have created an interoperability record for it.
82+
Depending on the framework, this might occur automatically or might require
83+
an operation similar to the ``py::export_for_interop()`` described in the
84+
previous section. (You can tell if this has happened by checking for the
85+
presence of an attribute on the type object called ``__pymetabind_binding__``.)
86+
Consult the other framework's documentation for details.
87+
88+
Once that's done, you can teach pybind11 about the foreign type by passing its
89+
Python type object to ``py::import_for_interop()``.
90+
This function takes an optional template argument specifying which C++ type to
91+
associate the Python type with. If the foreign type was bound using another
92+
C++ framework, such as nanobind or a different version of pybind11, the template
93+
argument need not be provided because the C++ ``std::type_info`` structure
94+
describing the type can be found by looking at the interoperability record.
95+
On the other hand, if the foreign type is not written in C++ or is bound by
96+
a non-C++ framework that doesn't know about ``std::type_info``, pybind11 won't
97+
be able to figure out what the C++ type is, and needs you to specify it via
98+
a template argument to ``py::import_for_inteorp()``.
99+
100+
If you *don't* supply a template argument (for importing a C++ type), then
101+
pybind11 will check for you that the binding you're adding was compiled using a
102+
platform C++ ABI that is consistent with the build options for your pybind11
103+
extension. This helps to ensure that the exporter and importer mean the same
104+
thing when they say, for example, ``std::vector<std::string>``.
105+
The import will throw an exception if an incompatibility is detected.
106+
107+
If you *do* supply a template argument (for importing a
108+
different-language type and specifying the C++ equivalent), pybind11
109+
will assume that you have validated compatibility yourself. Getting it
110+
wrong can cause crashes and other sorts of undefined behavior, so if
111+
you're working with bindings that were created in another language, make
112+
doubly sure you're specifying a C++ type that is fully ABI-compatible with
113+
the one used by the foreign binding.
114+
115+
You can use ``py::interoperate_by_default()`` if you want pybind11 to
116+
automatically import every compatible C++ type as soon as it has been
117+
exported by another framework.
118+
119+
.. code-block:: cpp
120+
121+
// --- pet.h ---
122+
#pragma once
123+
#include <string>
124+
125+
struct Pet {
126+
std::string name;
127+
std::string sound;
128+
129+
Pet(std::string _name, std::string _sound)
130+
: name(std::move(_name)), sound(std::move(_sound)) {}
131+
132+
std::string speak() const { return name + " goes " + sound + "!"; }
133+
};
134+
135+
// --- pets.cc ---
136+
#include <nanobind/nanobind.h>
137+
#include <nanobind/stl/string.h>
138+
#include "pet.h"
139+
140+
NB_MODULE(pets, m) {
141+
auto pet = nanobind::class_<Pet>(m, "Pet")
142+
.def(nanobind::init<std::string, std::string>())
143+
.def("speak", &Pet::speak);
144+
145+
nanobind::export_for_interop(pet);
146+
}
147+
148+
// --- groomer.cc ---
149+
#include <pybind11/pybind11.h>
150+
#include "pet.h"
151+
152+
std::string groom(const Pet& pet) {
153+
return pet.name + " got a haircut";
154+
}
155+
156+
PYBIND11_MODULE(groomer, m) {
157+
auto pet = pybind11::module_::import_("pets").attr("Pet");
158+
159+
// This could go either before or after the function definition that
160+
// relies on it
161+
pybind11::import_for_interop(pet);
162+
163+
// If Pet were bound by a non-C++ framework, you would instead say:
164+
// pybind11::import_for_interop<Pet>(pet);
165+
166+
m.def("groom", &groom);
167+
}
168+
169+
170+
Automatic communication
171+
-----------------------
172+
173+
In large binding projects, you might prefer to share *all* types rather than
174+
only those you nominate. For that, pybind11 provides the
175+
``py::interoperate_by_default()`` function. It takes two optional bool
176+
parameters that specify whether you want automatic export and/or automatic
177+
import; if you don't specify the parameters, then both are enabled.
178+
179+
Automatic export is equivalent to writing a call to ``py::export_for_interop()``
180+
after every ``py::class_``, ``py::enum_``, or ``py::native_enum`` binding
181+
statement in any pybind11 module that is ABI-compatible with the one in which
182+
you wrote the call.
183+
184+
Automatic import is equivalent to writing a call to ``py::import_for_interop()``
185+
after every export of a type from a different framework. It only import
186+
bindings written in C++ with a compatible platform ABI (the same ones that
187+
``py::import_for_interop()`` can import without a template argument);
188+
bindings written in other languages must always be imported explicitly.
189+
190+
Automatic import and export apply both to types that already exist and
191+
types that will be bound in the future. They cannot be disabled once enabled.
192+
193+
Here is the above example recast to use automatic communication.
194+
195+
.. code-block:: cpp
196+
197+
// (pet.h unchanged)
198+
199+
// --- pets.cc ---
200+
#include <nanobind/nanobind.h>
201+
#include <nanobind/stl/string.h>
202+
#include "pet.h"
203+
204+
NB_MODULE(pets, m) {
205+
nanobind::interoperate_by_default();
206+
nanobind::class_<Pet>(m, "Pet")
207+
.def(nanobind::init<std::string, std::string>())
208+
.def("speak", &Pet::speak);
209+
}
210+
211+
// --- groomer.cc ---
212+
#include <pybind11/pybind11.h>
213+
#include "pet.h"
214+
215+
std::string groom(const Pet& pet) {
216+
return pet.name + " got a haircut";
217+
}
218+
219+
PYBIND11_MODULE(groomer, m) {
220+
pybind11::interoperate_by_default();
221+
m.def("groom", &groom);
222+
}
223+
224+
225+
Conversion semantics and caveats
226+
--------------------------------
227+
228+
Cross-framework inheritance is not supported: a type bound
229+
using pybind11 must only have base classes that were bound using
230+
ABI-compatible versions of pybind11.
231+
232+
A function bound using pybind11 cannot perform a conversion to
233+
``std::unique_ptr<T>`` using a foreign binding for ``T``, because the
234+
interoperability mechanism doesn't provide any way to ask a foreign instance
235+
to relinquish its ownership.
236+
237+
When converting from a foreign instance to ``std::shared_ptr<T>``, pybind11
238+
generally cannot "see inside" the instance to find an existing ``shared_ptr``
239+
to share ownership with, so it will create a new ``shared_ptr`` control block
240+
that owns a reference to the Python object. This is usually not a problem, but
241+
does mean that ``shared_ptr::use_count()`` won't work like you expect. (If
242+
``T`` inherits ``std::enable_shared_from_this``, then pybind11 can use that
243+
to find the existing ``shared_ptr``, and will do so instead.)
244+
245+
Type casters (both :ref:`built-in <conversion_table>` and :ref:`custom
246+
<custom_type_caster>`) execute before the interoperability mechanism
247+
has a chance to step in. pybind11 is not able to execute type casters from
248+
a different framework; you will need to port them to a pybind11 equivalent.
249+
Interoperability only helps with bindings, as produced by ``py::class_`` and
250+
similar statements.
251+
252+
:ref:`Implicit conversion <implicit_conversions>` defined using
253+
``py::implicitly_convertible()`` can convert *from* foreign types.
254+
Implicit conversions *to* a foreign type should be registered with its
255+
binding library, not with pybind11.
256+
257+
When a C++-to-foreign-Python conversion is performed in a context that does
258+
not specify the ``return_value_policy``, the policy to use is inferred using
259+
pybind11's rules, which may differ from the foreign framework's.
260+
261+
It is possible for multiple foreign bindings to exist for the same C++ type,
262+
or for a particular C++ type to have both a native pybind11 binding
263+
and one or more foreign ones. This might occur due to separate Python
264+
extensions each having their own need to bind a common type, as discussed in
265+
the section on :ref:`module-local bindings <module_local>`. In such cases,
266+
pybind11 always tries bindings for a given C++ type ``T`` in the following order:
267+
268+
* the pybind11 binding for ``T`` that was declared with ``py::module_local()``
269+
in this extension module, if any; then
270+
271+
* the pybind11 binding for ``T`` that was declared without ``py::module_local()``
272+
in either this extension module or another ABI-compatible one (drawing no
273+
distinction between the two), if any; then
274+
275+
* if performing a from-Python conversion on an instance of a pybind11 binding
276+
for ``T`` that was declared with ``py::module_local()`` in a different
277+
but ABI-compatible module, that binding; otherwise
278+
279+
* each known foreign binding, in the order in which they were imported,
280+
without making any distinction between other versions of pybind11 and
281+
non-pybind11 frameworks. (If automatic import is enabled, then the import
282+
order will match the original export order.)
283+
284+
You can use the interoperability mechanism to share :ref:`module-local bindings
285+
<module_local>` with other modules. Unlike the sharing that happens by default,
286+
this allows you to return instances of such bindings from outside the module in
287+
which they were defined.
288+
289+
When performing C++-to-Python conversion of a type for which
290+
:ref:`automatic downcasting <inheritance>` is applicable,
291+
the downcast occurs in the binding library that is originally performing the
292+
conversion, even if the result will then be obtained using a foreign binding.
293+
That means foreign frameworks returning pybind11 types might not downcast
294+
them in the same way that pybind11 does; they might only be able to downcast
295+
from a primary base (with no this-pointer adjustment / no multiple inheritance),
296+
or not downcast at all.

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
advanced/smart_ptrs
3535
advanced/cast/index
3636
advanced/pycpp/index
37+
advanced/interop
3738
advanced/embedding
3839
advanced/misc
3940
advanced/deprecated

docs/upgrade.rst

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,55 @@ to a new version. But it goes into more detail. This includes things like
88
deprecated APIs and their replacements, build system changes, general code
99
modernization and other useful information.
1010

11+
v3.1
12+
====
13+
14+
The major new feature in pybind11 v3.1 is support for
15+
:ref:`interoperability with other binding frameworks <interop>` and other
16+
(future) versions of pybind11. See the linked documentation for details.
17+
18+
This support was added in an ABI-compatible way, so you can combine pybind11
19+
v3.1 extensions with v3.0 extensions. Classes and enums bound using pybind11
20+
v3.1 support all interoperability features. Classes and ``py::enum_``\s bound
21+
using pybind11 v3.0 can still be exported manually by a pybind11 v3.1 extension
22+
calling ``py::export_for_interop()``, but they won't be exported automatically
23+
and they can't be returned by value from a foreign binding.
24+
``py::native_enum``\s bound using pybind11 v3.0 don't support the
25+
interoperability mechanism at all.
26+
27+
There is one implication of the new interoperability support that might result
28+
in new compiler errors for some previously-working binding code. Previously,
29+
pybind11 only attempted to call a bound type's copy constructor or move
30+
constructor if that type was ever returned from a pybind11-bound function.
31+
Now, pybind11 must allow for the possibility that a pybind11-bound type is
32+
returned from a foreign framework's bound functions, so it will generate
33+
code that's capable of calling copy and move constructors for any bound type
34+
that satisfies ``std::is_{copy,move}_constructible``. There exist types that
35+
satisfy that type trait but will produce errors if you actually try to copy
36+
them, such as the following:
37+
38+
.. code-block:: cpp
39+
40+
struct MoveOnly { MoveOnly(MoveOnly&&) noexcept = default; }
41+
struct Container {
42+
std::vector<MoveOnly> items;
43+
};
44+
45+
``Container`` in this example satisfies ``std::is_copy_constructible``, but
46+
actually trying to copy it will fail at compile time because the vector element
47+
``MoveOnly`` is not copyable. The solution is to explicitly mark ``Container``
48+
as move-only:
49+
50+
.. code-block:: cpp
51+
52+
struct MoveOnly { MoveOnly(MoveOnly&&) noexcept = default; }
53+
struct Container {
54+
Container() = default;
55+
Container(Container&&) noexcept = default;
56+
57+
std::vector<MoveOnly> items;
58+
};
59+
1160
.. _upgrade-guide-3.0:
1261

1362
v3.0

0 commit comments

Comments
 (0)