@@ -41,31 +41,58 @@ PYBIND11_MODULE(my_module, m) {
4141```
4242
4343
44- ## Wrapped C++ vs Python Native Types
45-
46- When passing protos between C++ and python, these bindings will convert the
47- protocol buffer into the native python type, which usually involves protocol
48- buffer serialization. The objects returned to python are the native python
49- protocol buffer objects, and so ` isinstance() ` and normal protocol buffer
50- operations and methods will behave as expected.
44+ ## C++ Native vs Python Native Types
45+
46+ When passing protos between C++ and Python, the ` native_proto_caster.h `
47+ bindings will convert protobuf objects to the native type on either side.
48+
49+ While C++ has only one native type, Python has two native types
50+ (https://rules-proto-grpc.com/en/latest/lang/python.html ):
51+
52+ * ` --define=use_fast_cpp_protos=false ` (aka ` use_pure_python_protos ` )
53+ * ` --define=use_fast_cpp_protos=true `
54+
55+ With ` use_pure_python_protos ` , protobuf objects passed between C++ and Python
56+ are always serialized/deserialized between the native C++ type and the pure
57+ Python native type. This is very safe but also slow.
58+
59+ With ` use_fast_cpp_protos ` , the native Python type is internally backed by
60+ the native C++ type, which unlocks various performance benefits, even when
61+ only used from Python. When passing protobuf objects between Python and C++,
62+ in certain situations the serialization/deserialization overhead can be
63+ avoided, but not universally. Fundamentally, sharing C++ native protobuf
64+ objects between C++ and Python is unsafe because C++ assumes that it has
65+ exclusive ownership and may manipulate references in a way that undermines
66+ Python's much safer ownership semantics. Because of this, sharing mutable
67+ references or pointers between C++ and Python is not allowed. However, when
68+ passing a Python protobuf object to C++, the bindings may share the underlying
69+ C++ native protobuf object with C++ when passed by ` const & ` or ` const * ` .
70+
71+ ### Protobuf Extensions
72+
73+ With ` use_fast_cpp_protos ` , any ` py_proto_library ` becomes implicitly dependent
74+ on the corresponding ` cc_proto_library ` , but this is currently not handled
75+ automatically, nor clearly diagnosed or formally enforced. In the absence of
76+ protobuf extensions
77+ usually there is no problem: Python code tends to depend organically on
78+ a given ` py_proto_library ` , and pybind11-wrapped C++ code tends to depend
79+ organically on the corresponding ` cc_proto_library ` . However, when extensions
80+ are involved, a well-known pitfall is to accidentally omit the corresponding
81+ ` cc_proto_library ` . Currently this needs to be kept in mind as a pitfall
82+ (sorry), but the usual best-practice unit testing is likely to catch such
83+ situations. Once discovered, the fix is easy: add the ` cc_proto_library `
84+ to the ` deps ` of the relevant ` pybind_library ` or ` pybind_extension ` .
85+
86+ ### Enumerations
5187
5288Enumerations are passed and returned as integers. You may use the enum values
5389from the native python proto module to set and check the enum values used
5490by a bound proto enum (see ` tests/proto_enum_test.py ` for an example).
5591
56- Fundamentally sharing protocol buffer types between C++ and python runtimes
57- is unsafe because C++ assumes that it has full ownership of a protocol buffer
58- message, and may manipulate references in a way that undermines python
59- ownership semantics. Because of this sharing mutable references or pointers
60- between C++ and python is not allowed. However when using the python fast
61- proto implementation, the bindings may share an underlying protocol buffer
62- with C++ when passed by ` const & ` into a C++ function.
92+ ### In / Out Parameters
6393
6494In cases where a protocol buffer is used as an in/out parameter in C++,
65- additional logic will be required in the wrapper.
66-
67-
68- ### In / Out example
95+ additional logic will be required in the wrapper. For example:
6996
7097``` cpp
7198#include < pybind11/pybind11.h>
@@ -87,39 +114,15 @@ PYBIND11_MODULE(my_module, m) {
87114```
88115
89116
90- ## Integration with the Old Way
91-
92- Due to the nature of pybind11, extension modules built using `pybind11_protobuf/native_proto_caster.h`
93- cannot interoperate with the older bindings (those modules built using `pybind11_protobuf/proto_casters.h`)
94- as that may generate duplicate pybind11 specializations for a given protocol buffer
95- which would violate C++ ODR rules in an undetectable way. In order to workaround
96- that failure mode a nearly-transparent wrapper is also provided.
97-
98- To use the wrappers:
99-
100- 1. Include the header file `pybind11_protobuf/wrapped_proto_caster.h`
101- in the .cc file with your bindings.
102- 1. Call `pybind11_protobuf::ImportWrappedProtoCasters();` in your `PYBIND11_MODULE` definition.
103- 1. Wrap the C++ code with `pybind11_protobuf::WithWrappedProtos`.
104-
105- ### Wrapped Example
106-
107- ```cpp
108- #include <pybind11/pybind11.h>
109-
110- #include "path/to/my/my_message.proto.h"
111- #include "pybind11_protobuf/wrapped_proto_caster.h"
112-
113- // In real use, these 2 functions would probably be defined in a python-agnostic library.
114- MyMessage ReturnMyMessage() { ... }
115- void TakeMyMessage(const MyMessage& in) { ... }
116-
117- PYBIND11_MODULE(my_module, m) {
118- pybind11_protobuf::ImportWrappedProtoCasters();
117+ ### `pybind11_protobuf/wrapped_proto_caster.h`
119118
120- using pybind11_protobuf::WithWrappedProtos;
119+ TL;DR: Ignore `wrapped_proto_caster.h` if you can, this header was added as
120+ a migration aid before the removal of `proto_casters.h`.
121121
122- m.def("return_my_message", WithWrappedProtos(&ReturnMyMessage));
123- m.def("take_my_message", WithWrappedProtos(&TakeMyMessage), pybind11::arg("in"));
124- }
125- ```
122+ Historic background: Due to the nature of pybind11, extension modules
123+ built using `native_proto_caster.h` could not interoperate with the
124+ older `proto_casters.h` bindings, as that would have led to C++ ODR
125+ violations. `wrapped_proto_caster.h` is a nearly-transparent wrapper for
126+ `native_proto_caster.h` to work around the ODR issue. With the migration to
127+ `native_proto_caster.h` now completed, `wrapped_proto_caster.h` is obsolete
128+ and will be removed in the future.
0 commit comments