-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Eventual collisions between opaque STL containers #919
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Brainstorming some possible solutions to this:
Any thoughts? (the question is wide open, not aimed at @molpopgen in particular) |
I think that this could make sense for POD types, but perhaps less so for custom structs. I'm getting a lot of use from this feature for the case of an opaque vector of a C++ class that cannot be "dtyped" where I
No specific thoughts on this one for now.
This seems sensible, but I have no idea how intrusive a change this. Any idea how much the additional lookup would "cost" at run time? |
Probably not that much -- a second |
#949 will fix this. It essentially does the third approach I suggested above, (now called |
Cool! Is there a unit test file showing that one can sent an opaque thing from module A to module B without a copy? |
Opaque types will never be copied (assuming you take them by pointer or reference); making them opaque is essentially just turning them into ordinary classes that go through the generic type caster. If they are local, they can't cross the module barrier; if global they can. So in that respect, the |
Hi,
Sorry--I'm unable to follow what you've written.
Am I able to define the same opaque type in two different modules, import
both modules, and pass the type from one module into the next?
I've read through the updates to the docs in the PR, but it'd be nice to
have a plainer-English discussion of what is going on. Looking at the unit
tests isn't clarifying things for me.
On Thu, Aug 3, 2017 at 5:42 PM Jason Rhinelander ***@***.***> wrote:
Opaque types will never be copied (assuming you take them by pointer or
reference); making them opaque is essentially just turning them into
ordinary classes that go through the generic type caster. If they are
local, they can't cross the module barrier; if global they can.
So in that respect, the v4.append in the tests added in #949
<#949> is essentially doing a
cross-module global test, and the tests that raise exceptions in
test_local_bindings() are essentially testing a cross-module failure due
to local types that can't cross the barrier.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#919 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AGHnH3y2I8n8OFjPo9un_y72S2Evar5qks5sUmaigaJpZM4ODqxx>
.
--
Kevin Thornton
Associate Professor
Ecology and Evolutionary Biology
UC Irvine
http://www.molpopgen.org
http://github.com/ThorntonLab
http://github.com/molpopgen
|
No, that won't work, but that's a limitation that applies to ordinary types, too (e.g. So the short answer is that if you want a type to cross the module boundary, it needs to be globally defined in just one module; another module depending on the bindings could do a For basic types like |
Ok, so if two modules declare opaque vector<int>, and both do so locally,
what happens when the type from module A gets passed to a function in
module B with const vector<int> & in its signature?
Still trying to wrap my head around this....
On Fri, Aug 4, 2017, 7:53 AM Jason Rhinelander ***@***.***> wrote:
Closed #919 <#919> via #949
<#949>.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#919 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AGHnHy2_p8srTuhy-bmOfZxvrdiuEmX0ks5sUy8KgaJpZM4ODqxx>
.
--
Kevin Thornton
Associate Professor
Ecology and Evolutionary Biology
UC Irvine
http://www.molpopgen.org
http://github.com/ThorntonLab
http://github.com/molpopgen
|
You'll get a type error: the pybind code in B will see as one of the arguments some custom type (e.g. |
Ok. The proposed changes seem like a regression to me, and raise more
future problems. Really, this appears to just hides the issue that I
raised. Modules based on this will have to come with caveats I'm their
documentations: "If you pass this to another module, you may need to know
if that module is implemented using pybind11. If so, please copy the data
to a list first before passing it on". That's somewhat in jest, but these
changes strongly suggest against using opaque containers of POD, IMO.
On Fri, Aug 4, 2017, 8:46 AM Jason Rhinelander ***@***.***> wrote:
Ok, so if two modules declare opaque vector, and both do so locally, what
happens when the type from module A gets passed to a function in module B
with const vector & in its signature?
You'll get a type error: the pybind code in B will see as one of the
arguments some custom type (e.g. A.intvector) that it doesn't know
anything about, since that type is only registered within A.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#919 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AGHnH_9KqLdQQkAZPNFtpsc_xPCO3uAuks5sUzqdgaJpZM4ODqxx>
.
--
Kevin Thornton
Associate Professor
Ecology and Evolutionary Biology
UC Irvine
http://www.molpopgen.org
http://github.com/ThorntonLab
http://github.com/molpopgen
|
Thought about it more on my way into work. I'll pull the changes
associated with the PR and play around next week.
Thanks.
On Fri, Aug 4, 2017 at 8:53 AM Kevin Thornton ***@***.***> wrote:
Ok. The proposed changes seem like a regression to me, and raise more
future problems. Really, this appears to just hides the issue that I
raised. Modules based on this will have to come with caveats I'm their
documentations: "If you pass this to another module, you may need to know
if that module is implemented using pybind11. If so, please copy the data
to a list first before passing it on". That's somewhat in jest, but these
changes strongly suggest against using opaque containers of POD, IMO.
On Fri, Aug 4, 2017, 8:46 AM Jason Rhinelander ***@***.***>
wrote:
> Ok, so if two modules declare opaque vector, and both do so locally, what
> happens when the type from module A gets passed to a function in module B
> with const vector & in its signature?
>
> You'll get a type error: the pybind code in B will see as one of the
> arguments some custom type (e.g. A.intvector) that it doesn't know
> anything about, since that type is only registered within A.
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <#919 (comment)>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AGHnH_9KqLdQQkAZPNFtpsc_xPCO3uAuks5sUzqdgaJpZM4ODqxx>
> .
>
--
Kevin Thornton
Associate Professor
Ecology and Evolutionary Biology
UC Irvine
http://www.molpopgen.org
http://github.com/ThorntonLab
http://github.com/molpopgen
--
Kevin Thornton
Associate Professor
Ecology and Evolutionary Biology
UC Irvine
http://www.molpopgen.org
http://github.com/ThorntonLab
http://github.com/molpopgen
|
I do understand the limitation. It's a bit outside the scope of #949, but it's not something impossible. I think it might be possible to get it to work, perhaps by making |
I'm not sure if I'm completely following, but I think the approach in #997 should address this concern. E.g. one module can bind a local opaque This doesn't really do anything for the case of two modules which have different local opaque bindings of the same vector -- those aren't compatible, but that's the point of the local bindings. |
I haven't had time to play yet, but this is what I'm worried about. If two different modules define vector as opaque (and local), but add no further .def, my understanding is that they are incompatible, and cannot simply be treated as const std::vector & when passed to/from each module. Is this correct? |
Yes. (The opacity doesn't really matter; this hinges entirely on the module-local status). I believe what you're looking for (and the reason I reopened the issue) is to have this working: #include <pybind11/pybind11.h>
#include <pybind11/stl_bind.h>
namespace py = pybind11;
PYBIND11_MAKE_OPAQUE(std::vector<int>)
PYBIND11_MODULE(v1, m) {
py::bind_vector<std::vector<int>>(m, "IntVec1");
m.def("sequence", [](int n) {
std::vector<int> v;
for (int i = 1; i <= n; i++) v.push_back(i);
return v;
});
m.def("sum", [](const std::vector<int> &v) {
int s = 0; for (auto i : v) s += i; return s;
});
} v2.cpp: #include <pybind11/pybind11.h>
#include <pybind11/stl_bind.h>
namespace py = pybind11;
PYBIND11_MAKE_OPAQUE(std::vector<int>)
PYBIND11_MODULE(v2, m) {
py::bind_vector<std::vector<int>>(m, "IntVec2");
m.def("squares", [](int n) {
std::vector<int> v;
for (int i = 1; i <= n; i++) v.push_back(i*i);
return v;
});
m.def("sum", [](const std::vector<int> &v) {
int s = 0; for (auto i : v) s += i; return s;
});
} v.py: import v1, v2
a = v1.sequence(5)
b = v2.squares(5)
print(a)
print(b)
print(v1.sum(a), v2.sum(b)) # All good (same module types)
print(v1.sum(b), v2.sum(a)) # Failure (incompatible types) which currently gives:
In principle, it seems feasible to make this work. Essentially the locality and incompatibility is only relevant on the Python side (i.e. during type caster |
@jagerman Yes, that would be ideal. |
That wouldn't be too hard to pull off for a solitary type like |
Yes, inheritance should be harder, but that is ok in many cases. Since a class publicly inheriting from std::vector et al is making an error, classes using std containers should be expected to do so via private inheritance, and thus the buffer protocol and opaque stuff will be more "manual" for those types. Except for Eigen/Armadillo types, I'm guessing it'll be fairly rare for two different modules to wrap the same underlying non-std C++ types. My specific case where I ran into issues was in developing two different python packages based on two different C++ packages or genetics research. They are designed so that data can be shuttled to/from via some simple std containers, and it also makes sense for each Python package to provide opaque access w/buffer protocols available for those container types. |
Another possibility would be to smuggle the type loader out (rather than the type information in) by stashing a function pointer to the type loader somewhere associated with the instance for generic types; the caster could then call that (instead of its local type loader). |
#1007 should solve this: it essentially makes |
After some chatting on gitter, @jagerman suggested that I raise an issue here, mostly so that it can be kept track of.
This is related to #439, but has more to do with what happens in the long run when more and more packages are developed using pybind11.
Currently, opaque STL containers are most often generated with a combo of the
PYBIND11_MAKE_OPAQUE
macro and a call topy::bind_vector
.If two different modules by different developers generate the same opaque containers, an import error occurs if both modules are used together. One simple solution is to wrap the binding in try/catch blocks as shown here: https://github.com/molpopgen/opaque_test. (That example uses ccpimport to compile the code.)
However, the try/catch solution is imperfect: the name seen on the Python side depends on the import order. Further, there is the deeper problem that
py::bind_vector
may optionally be invoked withpy::buffer_protocol()
and that the class type returned may be further augmented via.defs
in the usual way. The possible result then is that both the name and features/behavior depend on import order, which seems less than ideal.The gitter discussion ended with the agreement that a solution is needed and that it is not clear what that solution should look like.
The text was updated successfully, but these errors were encountered: