Python Protocol-like support with implicitly_convertible #5651
bpdavis86
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
I wish something like this would have been in the documentation because it took me some time to find just the right way to go about it.
We are attempting a mixed Python/C++ simulation project with the goal of interchangeable components from both sides. There are 4 use cases.
Additionally, both the pure Python and pure C++ simulation should be runnable without the involvement of pybind, so the implementation of the wrappers should be decoupled.
The first two cases are obviously straightforward. The third is not too hard, because we can pybind the C++ modules, and as long as they fulfil the correct interface, we do not care. Python has a nice feature "protocols" to motivate this.
Then, if I implement ISimulation in Python following these interfaces, I do not care what kind of entity comes in, it can be a Python entity, or C++ pybind entity that has the right functions. I can swap these things in and out in loader code.
To do things the other way round, it is harder. I can make an IEntity virtual base class in C++ easily enough.
Then to make this work in Python by the standard documentation, one needs to derive Python implementations from the pybind IEntity class, and add associated "trampoline" class. But, I don't want to do this because it wrecks the concept of Protocol in Python and the Python-only use case 1 (which should not have to load anything pybind-related).
So, I would like to achieve something like "Protocol" in pybind, i.e., I can accept an arbitrary Python object which implicitly fulfils a given interface and have it made compatible with my strongly typed C++ simulation object.
Obviously we will need a "real" C++ derived class of IEntity to make this work. After much meandering, I came up with this:
Now, this can be bound as follows.
We can then do something like this:
This will work, even though MyPyEntity only follows the IEntity "Protocol" rather than being derived from the bound IEntity class. This happens without any explicit wrapping in a C++ type thanks to implicitly_convertible. The only restriction is that the input Python object must support dynamic attributes (so it can be made to own its C++ wrapper). Perhaps there is some clever way around this.
It is a little bit laborious to write the Python attribute dispatch wrappers if you have a large interface, but the result is very nice. You can also cache things in the wrapper object if you have e.g. an API with reference types.
Beta Was this translation helpful? Give feedback.
All reactions