pybind / pybind11

Seamless operability between C++11 and Python
https://pybind11.readthedocs.io/
Other
15.77k stars 2.11k forks source link

[QUESTION] Working with a C++ library that has boost::python bindings #2987

Open nachovizzo opened 3 years ago

nachovizzo commented 3 years ago

Problem Descriptiopn

Hello there, I'm actually using a 3dparty library that provides python bindings... but sadly they didn't use pybind11 :cry: ...

I need to interact with the python objects created by the library(from Python) with a C++ API. So far I made it work but I don't like at all how it's looking:

Solution provided so far

template <typename ClassType>
typename ClassType::Ptr convert_to_class_type(py::object py_obj) {
    boost::python::extract<typename ClassType::Ptr> x(py_obj.ptr());
    if (x.check()) {
        return x();
    } else {
        throw std::invalid_argument("ClassType not supported");
    }
}

template <typename ClassType>
void Wrapper(py::object py_obj) {
    auto obj = convert_to_class_type<ClassType>(py_obj);
    // do some stuff with the obj..
}

PYBIND11_MODULE(dummy_module, m) {
    m.def("ClassTypeWrapper", &Wrapper<CustomLibraryType>);
}

What I would like to write:

template <typename ClassType>
void Wrapper(ClassType obj) {
    // do some stuff with the obj... no conversion needed
}

PYBIND11_MODULE(dummy_module, m) {
    m.def("ClassTypeWrapper", &Wrapper<CustomLibraryType>);
}

An extra example

So, I use a lot of the Open3D library. And whenever I write C++ code it just works out of the box with Open3D types:

#include <open3d/geometry/PointCloud.h>
#include <pybind11/pybind11.h>

namespace py = pybind11;

void Process(const o3d::geometry::PointCloud& point_cloud) {}

PYBIND11_MODULE(cloud_module, m) { m.def("process", &Process); }

Whenever I build that small module I can run the following code

import cloud_module
import open3d as o3d

pcd = o3d.geometry.PointCloud()
cloud_module.process(pcd)

If I don't use the right o3d.geometry.PointCloud() type, then there will be a runtime error

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-4f2097197c74> in <module>
      3 
      4 mesh = o3d.geometry.TriangleMesh()
----> 5 cloud_module.process(mesh)

TypeError: process(): incompatible function arguments. The following argument types are supported:
    1. (arg0: open3d::geometry::PointCloud) -> None

Invoked with: TriangleMesh with 0 points and 0 triangles.

Which is the expected behavior.

Final Question

I've checked the Custom type caters but doesn't seem to be the right solution.

Is there any easy way to achieve this?

Thanks in advance!!!

jmirabel commented 3 years ago

I would be interesting in this too. I bumped into this issue several time. https://github.com/stack-of-tasks/pinocchio/pull/1519 is the results of my vagrancy.

jmirabel commented 3 years ago

For your info, the type caster can solve your problem if you accept copies. There is an example in the above mentioned PR.