Skip to content

Commit 7437c69

Browse files
committed
Add py::module_local() attribute for module-local type bindings
This commit adds a `py::module_local` attribute that lets you confine a registered type to the module (more technically, the shared object) in which it is defined, by registering it with: py::class_<C>(m, "C", py::module_local()) This will allow the same C++ class `C` to be registered in different modules with independent sets of class definitions. On the Python side, two such types will be completely distinct; on the C++ side, the C++ type resolves to a different Python type in each module. This applies `py::module_local` automatically to `stl_bind.h` bindings when the container value type looks like something global: i.e. when it is a converting type (for example, when binding a `std::vector<int>`), or when it is a registered type itself bound with `py::module_local`. This should help resolve potential future conflicts (e.g. if two completely unrelated modules both try to bind a `std::vector<int>`. Users can override the automatic selection by adding a `py::module_local()` or `py::module_local(false)`. Note that this does mildly break backwards compatibility: bound stl containers of basic types like `std::vector<int>` cannot be bound in one module and returned in a different module. (This can be re-enabled with `py::module_local(false)` as described above, but with the potential for eventual load conflicts).
1 parent d598172 commit 7437c69

File tree

13 files changed

+491
-32
lines changed

13 files changed

+491
-32
lines changed

docs/advanced/cast/stl.rst

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,10 @@ the declaration
150150
before any binding code (e.g. invocations to ``class_::def()``, etc.). This
151151
macro must be specified at the top level (and outside of any namespaces), since
152152
it instantiates a partial template overload. If your binding code consists of
153-
multiple compilation units, it must be present in every file preceding any
154-
usage of ``std::vector<int>``. Opaque types must also have a corresponding
155-
``class_`` declaration to associate them with a name in Python, and to define a
156-
set of available operations, e.g.:
153+
multiple compilation units, it must be present in every file (typically via a
154+
common header) preceding any usage of ``std::vector<int>``. Opaque types must
155+
also have a corresponding ``class_`` declaration to associate them with a name
156+
in Python, and to define a set of available operations, e.g.:
157157

158158
.. code-block:: cpp
159159
@@ -167,6 +167,20 @@ set of available operations, e.g.:
167167
}, py::keep_alive<0, 1>()) /* Keep vector alive while iterator is used */
168168
// ....
169169
170+
Please take a look at the :ref:`macro_notes` before using the
171+
``PYBIND11_MAKE_OPAQUE`` macro.
172+
173+
.. seealso::
174+
175+
The file :file:`tests/test_opaque_types.cpp` contains a complete
176+
example that demonstrates how to create and expose opaque types using
177+
pybind11 in more detail.
178+
179+
.. _stl_bind:
180+
181+
Binding STL containers
182+
======================
183+
170184
The ability to expose STL containers as native Python objects is a fairly
171185
common request, hence pybind11 also provides an optional header file named
172186
:file:`pybind11/stl_bind.h` that does exactly this. The mapped containers try
@@ -188,14 +202,34 @@ The following example showcases usage of :file:`pybind11/stl_bind.h`:
188202
py::bind_vector<std::vector<int>>(m, "VectorInt");
189203
py::bind_map<std::map<std::string, double>>(m, "MapStringDouble");
190204
191-
Please take a look at the :ref:`macro_notes` before using the
192-
``PYBIND11_MAKE_OPAQUE`` macro.
205+
When binding STL containers pybind11 considers the types of the container's
206+
elements to decide whether the container should be confined to the local module
207+
(via the :ref:`module_local` feature). If the container element types are
208+
anything other than already-bound custom types bound without
209+
``py::module_local()`` the container binding will have ``py::module_local()``
210+
applied. This includes converting types such as numeric types, strings, Eigen
211+
types; and types that have not yet been bound at the time of the stl container
212+
binding. This module-local binding is designed to avoid potential conflicts
213+
between module bindings (for example, from two separate modules each attempting
214+
to bind ``std::vector<int>`` as a python type).
215+
216+
It is possible to override this behavior to force a definition to be either
217+
module-local or global. To do so, you can pass the attributes
218+
``py::module_local()`` (to make the binding module-local) or
219+
``py::module_local(false)`` (to make the binding global) into the
220+
``py::bind_vector`` or ``py::bind_map`` arguments:
193221

194-
.. seealso::
222+
.. code-block:: cpp
195223
196-
The file :file:`tests/test_opaque_types.cpp` contains a complete
197-
example that demonstrates how to create and expose opaque types using
198-
pybind11 in more detail.
224+
py::bind_vector<std::vector<int>>(m, "VectorInt", py::module_local(false));
225+
226+
Note, however, that such a global binding would make it impossible to load this
227+
module at the same time as any other pybind module that also attempts to bind
228+
the same container type (``std::vector<int>`` in the above example).
229+
230+
See :ref:`module_local` for more details on module-local bindings.
231+
232+
.. seealso::
199233

200234
The file :file:`tests/test_stl_binders.cpp` shows how to use the
201235
convenience STL container wrappers.

docs/advanced/classes.rst

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,3 +635,139 @@ inheritance, which can lead to undefined behavior. In such cases, add the tag
635635
636636
The tag is redundant and does not need to be specified when multiple base types
637637
are listed.
638+
639+
.. _module_local:
640+
641+
Module-local class bindings
642+
===========================
643+
644+
When creating a binding for a class, pybind by default makes that binding
645+
"global" across modules. What this means is that a type defined in one module
646+
can be passed to functions of other modules that expect the same C++ type. For
647+
example, this allows the following:
648+
649+
.. code-block:: cpp
650+
651+
// In the module1.cpp binding code for module1:
652+
py::class_<Pet>(m, "Pet")
653+
.def(py::init<std::string>());
654+
655+
.. code-block:: cpp
656+
657+
// In the module2.cpp binding code for module2:
658+
m.def("pet_name", [](Pet &p) { return p.name(); });
659+
660+
.. code-block:: pycon
661+
662+
>>> from module1 import Pet
663+
>>> from module2 import pet_name
664+
>>> mypet = Pet("Kitty")
665+
>>> pet_name(mypet)
666+
'Kitty'
667+
668+
When writing binding code for a library, this is usually desirable: this
669+
allows, for example, splitting up a complex library into multiple Python
670+
modules.
671+
672+
In some cases, however, this can cause conflicts. For example, suppose two
673+
unrelated modules make use of an external C++ library and each provide custom
674+
bindings for one of that library's classes. This will result in an error when
675+
a Python program attempts to import both modules (directly or indirectly)
676+
because of conflicting definitions on the external type:
677+
678+
.. code-block:: cpp
679+
680+
// dogs.cpp
681+
682+
// Binding for external library class:
683+
py::class<pets::Pet>(m, "Pet")
684+
.def("name", &pets::Pet::name);
685+
686+
// Binding for local extension class:
687+
py::class<Dog, pets::Pet>(m, "Dog")
688+
.def(py::init<std::string>());
689+
690+
.. code-block:: cpp
691+
692+
// cats.cpp, in a completely separate project from the above dogs.cpp.
693+
694+
// Binding for external library class:
695+
py::class<pets::Pet>(m, "Pet")
696+
.def("get_name", &pets::Pet::name);
697+
698+
// Binding for local extending class:
699+
py::class<Cat, pets::Pet>(m, "Cat")
700+
.def(py::init<std::string>());
701+
702+
.. code-block:: pycon
703+
704+
>>> import cats
705+
>>> import dogs
706+
Traceback (most recent call last):
707+
File "<stdin>", line 1, in <module>
708+
ImportError: generic_type: type "Pet" is already registered!
709+
710+
To get around this, you can tell pybind11 to keep the external class binding
711+
localized to the module by passing the ``py::module_local()`` attribute into
712+
the ``py::class_`` constructor:
713+
714+
.. code-block:: cpp
715+
716+
// Pet binding in dogs.cpp:
717+
py::class<pets::Pet>(m, "Pet", py::module_local())
718+
.def("name", &pets::Pet::name);
719+
720+
.. code-block:: cpp
721+
722+
// Pet binding in cats.cpp:
723+
py::class<pets::Pet>(m, "Pet", py::module_local())
724+
.def("get_name", &pets::Pet::name);
725+
726+
This makes the Python-side ``dogs.Pet`` and ``cats.Pet`` into distinct classes
727+
that can only be accepted as ``Pet`` arguments within those classes. This
728+
avoids the conflict and allows both modules to be loaded.
729+
730+
One limitation of this approach is that because ``py::module_local`` types are
731+
distinct on the Python side, it is not possible to pass such a module-local
732+
type as a C++ ``Pet``-taking function outside that module. For example, if the
733+
above ``cats`` and ``dogs`` module are each extended with a function:
734+
735+
.. code-block:: cpp
736+
737+
m.def("petname", [](pets::Pet &p) { return p.name(); });
738+
739+
you will only be able to call the function with the local module's class:
740+
741+
.. code-block:: pycon
742+
743+
>>> import cats, dogs # No error because of the added py::module_local()
744+
>>> mycat, mydog = cats.Cat("Fluffy"), dogs.Dog("Rover")
745+
>>> (cats.petname(mycat), dogs.petname(mydog))
746+
('Fluffy', 'Rover')
747+
>>> cats.petname(mydog)
748+
Traceback (most recent call last):
749+
File "<stdin>", line 1, in <module>
750+
TypeError: petname(): incompatible function arguments. The following argument types are supported:
751+
1. (arg0: cats.Pet) -> str
752+
753+
Invoked with: <dogs.Dog object at 0x123>
754+
755+
.. note::
756+
757+
STL bindings (as provided via the optional :file:`pybind11/stl_bind.h`
758+
header) apply ``py::module_local`` by default when the bound type might
759+
conflict with other modules; see :ref:`stl_bind` for details.
760+
761+
.. note::
762+
763+
The localization of the bound types is actually tied to the shared object
764+
or binary generated by the compiler/linker. For typical modules created
765+
with ``PYBIND11_MODULE()``, this distinction is not significant. It is
766+
possible, however, when :ref:`embedding` to embed multiple modules in the
767+
same binary (see :ref:`embedding_modules`). In such a case, the
768+
localization will apply across all embedded modules within the same binary.
769+
770+
.. seealso::
771+
772+
The file :file:`tests/test_local_bindings.cpp` contains additional examples
773+
that demonstrate how ``py::module_local()`` works.

docs/advanced/embedding.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
.. _embedding:
2+
13
Embedding the interpreter
24
#########################
35

@@ -131,6 +133,7 @@ embedding the interpreter. This makes it easy to import local Python files:
131133
int n = result.cast<int>();
132134
assert(n == 3);
133135
136+
.. _embedding_modules:
134137

135138
Adding embedded modules
136139
=======================

include/pybind11/attr.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ struct metaclass {
6464
explicit metaclass(handle value) : value(value) { }
6565
};
6666

67+
/// Annotation that marks a class as local to the module:
68+
struct module_local { const bool value; constexpr module_local(bool v = true) : value(v) { } };
69+
6770
/// Annotation to mark enums as an arithmetic type
6871
struct arithmetic { };
6972

@@ -196,7 +199,7 @@ struct function_record {
196199
/// Special data structure which (temporarily) holds metadata about a bound class
197200
struct type_record {
198201
PYBIND11_NOINLINE type_record()
199-
: multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false) { }
202+
: multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), module_local(false) { }
200203

201204
/// Handle to the parent scope
202205
handle scope;
@@ -243,6 +246,9 @@ struct type_record {
243246
/// Is the default (unique_ptr) holder type used?
244247
bool default_holder : 1;
245248

249+
/// Is the class definition local to the module shared object?
250+
bool module_local : 1;
251+
246252
PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) {
247253
auto base_info = detail::get_type_info(base, false);
248254
if (!base_info) {
@@ -408,6 +414,10 @@ struct process_attribute<metaclass> : process_attribute_default<metaclass> {
408414
static void init(const metaclass &m, type_record *r) { r->metaclass = m.value; }
409415
};
410416

417+
template <>
418+
struct process_attribute<module_local> : process_attribute_default<module_local> {
419+
static void init(const module_local &l, type_record *r) { r->module_local = l.value; }
420+
};
411421

412422
/// Process an 'arithmetic' attribute for enums (does nothing here)
413423
template <>

include/pybind11/cast.h

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ struct type_info {
5858
bool simple_ancestors : 1;
5959
/* for base vs derived holder_type checks */
6060
bool default_holder : 1;
61+
/* true if this is a type registered with py::module_local */
62+
bool module_local : 1;
6163
};
6264

6365
PYBIND11_UNSHARED_STATIC_LOCALS PYBIND11_NOINLINE inline internals *&get_internals_ptr() {
@@ -126,6 +128,12 @@ PYBIND11_NOINLINE inline internals &get_internals() {
126128
return *internals_ptr;
127129
}
128130

131+
// Works like internals.registered_types_cpp, but for module-local registered types:
132+
PYBIND11_NOINLINE PYBIND11_UNSHARED_STATIC_LOCALS inline type_map<void *> &registered_local_types_cpp() {
133+
static type_map<void *> locals{};
134+
return locals;
135+
}
136+
129137
/// A life support system for temporary objects created by `type_caster::load()`.
130138
/// Adding a patient will keep it alive up until the enclosing function returns.
131139
class loader_life_support {
@@ -213,7 +221,7 @@ PYBIND11_NOINLINE inline void all_type_info_populate(PyTypeObject *t, std::vecto
213221
// registered types
214222
if (i + 1 == check.size()) {
215223
// When we're at the end, we can pop off the current element to avoid growing
216-
// `check` when adding just one base (which is typical--.e. when there is no
224+
// `check` when adding just one base (which is typical--i.e. when there is no
217225
// multiple inheritance)
218226
check.pop_back();
219227
i--;
@@ -257,13 +265,18 @@ PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) {
257265
return bases.front();
258266
}
259267

260-
PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_info &tp,
268+
/// Return the type info for a given C++ type; on lookup failure can either throw or return nullptr.
269+
PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_index &tp,
261270
bool throw_if_missing = false) {
271+
std::type_index type_idx(tp);
262272
auto &types = get_internals().registered_types_cpp;
263-
264-
auto it = types.find(std::type_index(tp));
273+
auto it = types.find(type_idx);
265274
if (it != types.end())
266275
return (detail::type_info *) it->second;
276+
auto &locals = registered_local_types_cpp();
277+
it = locals.find(type_idx);
278+
if (it != locals.end())
279+
return (detail::type_info *) it->second;
267280
if (throw_if_missing) {
268281
std::string tname = tp.name();
269282
detail::clean_type_id(tname);
@@ -731,10 +744,8 @@ class type_caster_generic {
731744
// with .second = nullptr. (p.first = nullptr is not an error: it becomes None).
732745
PYBIND11_NOINLINE static std::pair<const void *, const type_info *> src_and_type(
733746
const void *src, const std::type_info &cast_type, const std::type_info *rtti_type = nullptr) {
734-
auto &internals = get_internals();
735-
auto it = internals.registered_types_cpp.find(std::type_index(cast_type));
736-
if (it != internals.registered_types_cpp.end())
737-
return {src, (const type_info *) it->second};
747+
if (auto *tpi = get_type_info(cast_type))
748+
return {src, const_cast<const type_info *>(tpi)};
738749

739750
// Not found, set error:
740751
std::string tname = rtti_type ? rtti_type->name() : cast_type.name();
@@ -819,7 +830,6 @@ template <typename type> class type_caster_base : public type_caster_generic {
819830
template <typename T = itype, enable_if_t<std::is_polymorphic<T>::value, int> = 0>
820831
static std::pair<const void *, const type_info *> src_and_type(const itype *src) {
821832
const void *vsrc = src;
822-
auto &internals = get_internals();
823833
auto &cast_type = typeid(itype);
824834
const std::type_info *instance_type = nullptr;
825835
if (vsrc) {
@@ -828,9 +838,8 @@ template <typename type> class type_caster_base : public type_caster_generic {
828838
// This is a base pointer to a derived type; if it is a pybind11-registered type, we
829839
// can get the correct derived pointer (which may be != base pointer) by a
830840
// dynamic_cast to most derived type:
831-
auto it = internals.registered_types_cpp.find(std::type_index(*instance_type));
832-
if (it != internals.registered_types_cpp.end())
833-
return {dynamic_cast<const void *>(src), (const type_info *) it->second};
841+
if (auto *tpi = get_type_info(*instance_type))
842+
return {dynamic_cast<const void *>(src), const_cast<const type_info *>(tpi)};
834843
}
835844
}
836845
// Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so

0 commit comments

Comments
 (0)