Skip to content

Exposing base member from derived, when base is not wrapped #910

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

Closed
pschella opened this issue Jun 20, 2017 · 3 comments
Closed

Exposing base member from derived, when base is not wrapped #910

pschella opened this issue Jun 20, 2017 · 3 comments

Comments

@pschella
Copy link
Contributor

If I have a base class B with a data member x (of type float, but that is irrelevant) and a derived class D. And if I now expose only D (because B for some other reason should not be exposed) as: py::class_<D>(m, "D").def(py::init<>()).def_readonly("x", &D::x); Accessing x from Python fails with:

E TypeError: (): incompatible function arguments. The following argument types are supported:
E 1. (self: B) -> float
E
E Invoked with: <pybind11_tests.D object at 0x108e2e538>

This can be fixed with an explicit cast as in .def_readonly("x", (float D::*) &D::x);, but that should not really be required (as pointed out my @jagerman on Gitter: Pybind doesn't know anything about the D : B relationship here, which is why it happens. It ought to be possible to make it work properly (and static_assert fail when the member isn't a superclass member)).

@bmerry
Copy link
Contributor

bmerry commented Jun 23, 2017

This looks like a duplicate of #854, and I think #855 should also fix it (and in a more general way).

jagerman added a commit to jagerman/pybind11 that referenced this issue Jun 28, 2017
When defining method from a member function pointer (e.g. `.def("f",
&Derived::f)`) we run into a problem if `&Derived::f` is actually
implemented in some base class `Base` when `Base` isn't
pybind-registered.

This happens because the class type is deduced from the member function
pointer, which then becomes a lambda with first argument this deduced
type.  For a base class implementation, the deduced type is `Base`, not
`Derived`, and so we generate and registered an overload which takes a
`Base *` as first argument.  Trying to call this fails if `Base` isn't
registered (e.g.  because it's an implementation detail class that isn't
intended to be exposed to Python) because the type caster for an
unregistered type always fails.

This commit adds a `method_adaptor` function that rebinds a member
function to a derived type member function and otherwise (i.e. regular
functions/lambda) leaves the argument as-is.  This is now used for class
definitions so that they are bound with type being registered rather
than a potential base type.

A closely related fix in this commit is to similarly update the lambdas
used for `def_readwrite` (and related) to bind to the class type being
registered rather than the deduced type so that registering a property
that resolves to a base class member similarly generates a usable
function.

Fixes pybind#854, pybind#910.

Co-Authored-By: Dean Moldovan <[email protected]>
jagerman added a commit to jagerman/pybind11 that referenced this issue Jun 28, 2017
When defining method from a member function pointer (e.g. `.def("f",
&Derived::f)`) we run into a problem if `&Derived::f` is actually
implemented in some base class `Base` when `Base` isn't
pybind-registered.

This happens because the class type is deduced from the member function
pointer, which then becomes a lambda with first argument this deduced
type.  For a base class implementation, the deduced type is `Base`, not
`Derived`, and so we generate and registered an overload which takes a
`Base *` as first argument.  Trying to call this fails if `Base` isn't
registered (e.g.  because it's an implementation detail class that isn't
intended to be exposed to Python) because the type caster for an
unregistered type always fails.

This commit adds a `method_adaptor` function that rebinds a member
function to a derived type member function and otherwise (i.e. regular
functions/lambda) leaves the argument as-is.  This is now used for class
definitions so that they are bound with type being registered rather
than a potential base type.

A closely related fix in this commit is to similarly update the lambdas
used for `def_readwrite` (and related) to bind to the class type being
registered rather than the deduced type so that registering a property
that resolves to a base class member similarly generates a usable
function.

Fixes pybind#854, pybind#910.

Co-Authored-By: Dean Moldovan <[email protected]>
jagerman added a commit to jagerman/pybind11 that referenced this issue Jul 3, 2017
When defining method from a member function pointer (e.g. `.def("f",
&Derived::f)`) we run into a problem if `&Derived::f` is actually
implemented in some base class `Base` when `Base` isn't
pybind-registered.

This happens because the class type is deduced from the member function
pointer, which then becomes a lambda with first argument this deduced
type.  For a base class implementation, the deduced type is `Base`, not
`Derived`, and so we generate and registered an overload which takes a
`Base *` as first argument.  Trying to call this fails if `Base` isn't
registered (e.g.  because it's an implementation detail class that isn't
intended to be exposed to Python) because the type caster for an
unregistered type always fails.

This commit adds a `method_adaptor` function that rebinds a member
function to a derived type member function and otherwise (i.e. regular
functions/lambda) leaves the argument as-is.  This is now used for class
definitions so that they are bound with type being registered rather
than a potential base type.

A closely related fix in this commit is to similarly update the lambdas
used for `def_readwrite` (and related) to bind to the class type being
registered rather than the deduced type so that registering a property
that resolves to a base class member similarly generates a usable
function.

Fixes pybind#854, pybind#910.

Co-Authored-By: Dean Moldovan <[email protected]>
jagerman added a commit to jagerman/pybind11 that referenced this issue Jul 3, 2017
When defining method from a member function pointer (e.g. `.def("f",
&Derived::f)`) we run into a problem if `&Derived::f` is actually
implemented in some base class `Base` when `Base` isn't
pybind-registered.

This happens because the class type is deduced from the member function
pointer, which then becomes a lambda with first argument this deduced
type.  For a base class implementation, the deduced type is `Base`, not
`Derived`, and so we generate and registered an overload which takes a
`Base *` as first argument.  Trying to call this fails if `Base` isn't
registered (e.g.  because it's an implementation detail class that isn't
intended to be exposed to Python) because the type caster for an
unregistered type always fails.

This commit adds a `method_adaptor` function that rebinds a member
function to a derived type member function and otherwise (i.e. regular
functions/lambda) leaves the argument as-is.  This is now used for class
definitions so that they are bound with type being registered rather
than a potential base type.

A closely related fix in this commit is to similarly update the lambdas
used for `def_readwrite` (and related) to bind to the class type being
registered rather than the deduced type so that registering a property
that resolves to a base class member similarly generates a usable
function.

Fixes pybind#854, pybind#910.

Co-Authored-By: Dean Moldovan <[email protected]>
jagerman added a commit that referenced this issue Jul 3, 2017
When defining method from a member function pointer (e.g. `.def("f",
&Derived::f)`) we run into a problem if `&Derived::f` is actually
implemented in some base class `Base` when `Base` isn't
pybind-registered.

This happens because the class type is deduced from the member function
pointer, which then becomes a lambda with first argument this deduced
type.  For a base class implementation, the deduced type is `Base`, not
`Derived`, and so we generate and registered an overload which takes a
`Base *` as first argument.  Trying to call this fails if `Base` isn't
registered (e.g.  because it's an implementation detail class that isn't
intended to be exposed to Python) because the type caster for an
unregistered type always fails.

This commit adds a `method_adaptor` function that rebinds a member
function to a derived type member function and otherwise (i.e. regular
functions/lambda) leaves the argument as-is.  This is now used for class
definitions so that they are bound with type being registered rather
than a potential base type.

A closely related fix in this commit is to similarly update the lambdas
used for `def_readwrite` (and related) to bind to the class type being
registered rather than the deduced type so that registering a property
that resolves to a base class member similarly generates a usable
function.

Fixes #854, #910.

Co-Authored-By: Dean Moldovan <[email protected]>
@dean0x7d
Copy link
Member

dean0x7d commented Jul 3, 2017

Fixed in #855.

@dean0x7d dean0x7d closed this as completed Jul 3, 2017
@pschella
Copy link
Contributor Author

pschella commented Jul 4, 2017

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants