4

I am trying to create a wrapper for a c++ method that returns a c++ class(vtkPolyData) which comes from an external c++ library (vtk). The same library has python binding available which is already installed in my python environment. How do you tell pybind that the c++ class (vtkPolydata) and its python variant are the same?

I tried to use this custom type caster macro. but I get TypeError: Unable to convert function return value to a Python type! The signature was : (self: Versa3dLib.skeletonizer, offset distance: float) -> vtkPolyData

which is confusing since it looks like the conversion maps to the correct type but python is unable to interpret it. So I am not sure what's wrong since I don't see anything wrong with the macro either. I noticed that in python vtkPolyData has type vtkCommonDataModelPython.vtkPolyData. is that why the conversion is not done correctly?

#include "skeletonizer.h"
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include "PybindVTKTypeCaster.h"
#include <vtkSmartPointer.h>

namespace py = pybind11;

PYBIND11_VTK_TYPECASTER(vtkPolyData)
PYBIND11_DECLARE_HOLDER_TYPE(T, vtkSmartPointer<T>);

namespace pybind11 { namespace detail {
    template <typename T>
    struct holder_helper<vtkSmartPointer<T>> { // <-- specialization
        static const T *get(const vtkSmartPointer<T> &p) { return p.GetPointer(); }
    };
}}

PYBIND11_MODULE(Versa3dLib, m)
{
    py::class_<skeletonizer>(m, "skeletonizer")
        .def(py::init<vtkPolyData *>())
        .def("get_offset", &skeletonizer::get_offset,
             "return vtkPolyData offset",
             py::arg("offset distance"));
}

Skeletonizer

#ifndef SKELETONIZER_H
#define SKELETONIZER_H

#include <vtkPolyData.h>
#include <vector>
#include <vtkSmartPointer.h>


using namespace std;

class skeletonizer
{
    public:
        skeletonizer(vtkPolyData* data);
        vtkSmartPointer<vtkPolyData> get_offset(double dist);
};

#endif

skeletonizer cpp

#include "skeletonizer.h"


skeletonizer::skeletonizer(vtkPolyData* data)
{
};

vtkSmartPointer<vtkPolyData> skeletonizer::get_offset(double dist)
{
    vtkSmartPointer<vtkPolyData> offsets = vtkSmartPointer<vtkPolyData>::New();

    return offsets;
};
Marc Wang
  • 171
  • 2
  • 12
  • Can you add the code where you used this macro? It would help to debug this further (as I'm also curious about this use case). – Eric Cousineau Feb 26 '19 at 14:44
  • Also, from looking at VTK's object model, it looks like most things would inherit from `vtkObjectBase`, so it's perhaps possible to write the type-caster in a more generic fashion, e.g. `std::enable_if::value>`. – Eric Cousineau Feb 26 '19 at 14:45
  • @EricCousineau Hi, thanks for the responce. I updated the post with the macro in question – Marc Wang Feb 26 '19 at 16:43
  • The main thing to notice I guess is: `vtkPythonUtil::GetObjectFromPointer(const_cast< VTK_OBJ *>(&src));` This link to the vtkPythonUtil file is here : https://github.com/Kitware/VTK/blob/master/Wrapping/PythonCore/vtkPythonUtil.h – Marc Wang Feb 26 '19 at 16:43
  • `vtkPythonUtil::GetObjectFromPointer`, returns a `PyObject` which at first glance does not seem wrong. – Marc Wang Feb 26 '19 at 16:47
  • Ah, sorry: I meant can you add the code that is downstream of the macro (where it's used)? BTW, starting to prototype some stuff here: https://github.com/EricCousineau-TRI/repro/commit/72119e7b2260b516d0fcca978fc522eb25d0f443 – Eric Cousineau Feb 26 '19 at 18:03
  • @EricCousineau, my mistake. I updated the post. Your stuff looks good so far – Marc Wang Feb 26 '19 at 19:36

1 Answers1

1

I think this should be a more general solution (hopefully easier to use?):

I believe this should be an improvement on the VTK code by:

  • Generalizing the type casters using SFINAE (rather than requiring explicit instantiations...).
  • Permit direct casting of vtkSmartPointer and vtkNew (assuming the types inside these are VTK types).

Made the code kind-of follow Drake's C++ + Python binding conventions.

For the above solution you had, I think it was close leveraging the SMTK code, but the holder type instantation was incorrect - you'd need type_caster specializations for the smart pointers (which the vtk_pybind code I posted would provide).

I'll see if I can post an issue on SMTK to see if they want to improve / simplify their binding code (esp. if people refer to it!).

EDIT: Posted issue here: https://gitlab.kitware.com/cmb/smtk/issues/228

Eric Cousineau
  • 1,944
  • 14
  • 23
  • I tried to use your `vtk_pybind.h` with your example binding code. I get the error `pybind11.h(167): error C2064: term does not evaluate to a function taking 0 arguments` followed by `pybind11.h(167): error C2248: 'pybind11::detail::descr::descr': cannot access protected member declared in class 'pybind11::detail::descr'` – Marc Wang Feb 28 '19 at 00:36
  • did you guys test it on a windows platform as well? – Marc Wang Feb 28 '19 at 00:55
  • I actually get my vtk files using anaconda. I will try to see if including it as a subfolder in my project makes a difference. – Marc Wang Feb 28 '19 at 00:59
  • Ah, no, I don't use Windows, so I can't say if it'd work there. Can you post a GitHub Gist to the full traceback of the compilation errors? (the error you mentioned doesn't indicate what in `vtk_pybind.h` caused the issue...) And I don't have experience with `conda`, so I can't say what might be going on there. – Eric Cousineau Feb 28 '19 at 15:49
  • [this one is when I try to use your example binding code](https://gist.github.com/rockandsalt/cf78390dca0387695b6cfa56ed8e22df) – Marc Wang Feb 28 '19 at 19:10
  • So I went ahead and tried to compile in an ubuntu environment. [I get the following error](https://gist.github.com/rockandsalt/f68285680aa66a42f4efda3d8b447797). It seems like cast_out::name() doesn't exist. This seems to point to something odd happening or not happening in `static constexpr auto name = _();` – Marc Wang Mar 04 '19 at 02:32
  • Not sure about what's specific to your code. It would be simplest if you went off the minimal example I provided, and try to reproduce the error there. One thing is that you may need to duplicate Line 74 onto Line 92: https://github.com/EricCousineau-TRI/repro/blob/b9e02d6d5a71f6315b80759ba1628b4bb383c0b8/python/vtk_pybind/vtk_pybind.h#L74 – Eric Cousineau Mar 04 '19 at 21:03
  • I cloned your repro and now I get the following [error](https://gist.github.com/rockandsalt/c6034bd319e6f0f385635fa332dbae57). I think I am just going to give up and return my vtkpolydata data as an array and remake it in python. Thank you for helping though. – Marc Wang Mar 05 '19 at 03:02
  • Hm... That's most likely due to either the wrong VTK or CMake version... But I totally understand. Sorry about that! – Eric Cousineau Mar 05 '19 at 16:38