Pybind11: Python-defined function: Unable to cast from non-held to held instance (T& to Holder<T>)

Created on 21 Jul 2017  Â·  4Comments  Â·  Source: pybind/pybind11

I have a C++ that takes an object of the abstract DomainBase class,

void
generate(const std::shared_ptr<foo::DomainBase> & domain)

Since it's using a shared_ptr here, I specified that in the pybind11 wrapper code for DomainBase like

py::class_<DomainBase, std::shared_ptr<DomainBase>>(m, "DomainBase");

I can now call generate for all classes derived in C++ from DomainBase, e.g.,

import foo
b = foo.Ball([0.0, 0.0, 0.0], 1.0)
foo.generate(b)

Now, I would like to extend this by deriving a class _within Python_, e.g.,

class Hyperboloid(foo.DomainBase):
    def __init__(self):
        return

    def eval(self, x):
         if self.z0 < x[2] and x[2] < self.z1:
            r2 = x[0]**2 + x[1]**2
            return r2 - (x[2]**2 + 0.5)**2
        else:
            return 1.0

h = Hyperboloid()
foo.generate(h)

This fails with the

RuntimeError: Unable to cast from non-held to held instance (T& to Holder<T>) (compile in debug mode for type information)

I can only guess that this has to do with the usage of shared_ptrs, and that, just like DomainBase and Ball, I would have to declare Hyperboloid a "shared_ptr" class.

Any hints?

Most helpful comment

    def __init__(self):
        return

This is missing the superclass initializer and so the C++ instance doesn't get initialized:

    def __init__(self):
        foo.DomainBase.__init__(self)
        # or `super(Hyperboloid, self).__init__()`, or in Python 3: `super().__init__()`

All 4 comments

    def __init__(self):
        return

This is missing the superclass initializer and so the C++ instance doesn't get initialized:

    def __init__(self):
        foo.DomainBase.__init__(self)
        # or `super(Hyperboloid, self).__init__()`, or in Python 3: `super().__init__()`

This is giving my some trouble. Some members of base class DomainBase are purely virtual, e.g.,

virtual double eval(const std::array<double, 3> & x) const = 0;

so adding an init to the pybind11 interface

    py::class_<DomainBase, std::shared_ptr<DomainBase>>(m, "DomainBase")
      .def(py::init<>());

gives me the compile error

invalid new-expression of abstract class type ‘foo::DomainBase’

I guess as a workaround I could replace the purely virtual members by members that just throw an exception. Unless there is another way...

For virtual methods you'll need to use a trampoline class that implements the virtual methods and forwards them to Python. See the docs

I'll close this, with a recommendation to use the gitter channel for how-do-I-do-x type questions (to keep issue reports for genuine bugs/issues rather than help questions).

Was this page helpful?
0 / 5 - 0 ratings