Marc Wang
Marc Wang

Reputation: 171

pybind11: return c++ class (with an existing python binding) to python

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;
};

Upvotes: 4

Views: 4461

Answers (1)

Eric Cousineau
Eric Cousineau

Reputation: 2004

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

Upvotes: 1

Related Questions