Skip to content
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

Return type that is implemented in another library with another set of python bindings #616

Closed
gedemagt opened this issue Jan 24, 2017 · 5 comments

Comments

@gedemagt
Copy link

Hi,

I am trying to use this project to make python bindings to my library. I do, however, use another library, which already have a set of python bindings. I would then like to return an object from the 3rd party library in my python bindings. Perhaps a little example is justified:

In my library I have something like

TVector3 getCenter(){...} // TVector3 is 3rd party library type

This 3rd party already have a set of python bindings:

import ROOT
v = ROOT.TVector3() # 3rd party object
v.Print() # 3rd party method

What I would like is now to write python bindings to my method, which returns an object from the already-implemented set of python bindings such that I can do something like this:

import MyLib

v = MyLib.getCenter() # My library returning a 3rd party object
v.Print() # 3rd party method

I have tried to poke around in your section about custom type casts (http://pybind11.readthedocs.io/en/latest/advanced/cast/custom.html) and also the chrono.h and eigen.h to get some inspiration, but I can't seem to find a proper way of doing this. I am a bit confused about lines like

return PyLong_FromLong(src.long_value)

and how I can construct a method like that for my problem.

Is this even feasible?

@jagerman
Copy link
Member

jagerman commented Jan 24, 2017

It's going to depend on what the other library exposes to you on the C++ side. If it can give you a PyObject * from a C++ instance, and can give you the C++ instance from a PyObject *, this is pretty simple (untested, but this is the basic idea):

namespace pybind11 { namespace detail {
template <> struct type_caster<TVector3> {
    // set things up and gives you a `TVector3 value;` member
    PYBIND11_TYPE_CASTER(TVector3, _("TVector3"));

    bool load(handle src, bool) {
        if (!src) return false;
        value = get_TVector3_from_pyobject(src.ptr());
        return true;
    }

    static handle cast(TVector3 v, return_value_policy /*policy*/, handle /*parent*/) {
        return get_pyobjectptr_from_TVector3(v);
    }
}
}} // end namespaces

If that isn't exposed, but the data isn't too complicated (which I'm guessing it might not be, from the class name), you can access the Python object from C++ to copy out the values yourself with something like:

namespace pybind11 { namespace detail {
template <> struct type_caster<TVector3> {
    // set things up and gives you a `TVector3 value;` member
    PYBIND11_TYPE_CASTER(TVector3, _("TVector3"));

    bool load(handle src, bool) {
        if (!src) return false;
        value.some_cpp_member = src.attr("some_python_member").cast<double>();
        value.setSomethingElse(src.attr("some_python_method")().cast<std::string>();
        // etc.
        return true;
    }

    static handle cast(TVector3 v, return_value_policy /*policy*/, handle /*parent*/) {
        py::object tv_py = py::module::import("ROOT").attr("TVector3")(); // Construct new python obj
        tv_py.attr("some_python_member") = py::cast(v.some_cpp_member);
        tv_py.attr("some_python_method")(py::cast(v.getSomethingElse());
        return tv_py.release();
    }
}
}} // end namespaces

@gedemagt
Copy link
Author

Thanks, that cleared up a lot! It now works like a charm! Thanks a lot, and thanks for a great library!

@Munken
Copy link

Munken commented Jan 24, 2017

Just an additional note. It would be beneficial to also have this more complex example in the documentation on custom type casters.

@BotellaA
Copy link

BotellaA commented Feb 4, 2020

How would you do the same thing with a pointer/reference only type of a non copyable object instead of a TVector3?

namespace pybind11 { namespace detail {
template <> struct type_caster<TVector3> {
    // set things up and gives you a `TVector3 value;` member
    PYBIND11_TYPE_CASTER(TVector3, _("TVector3"));

    bool load(handle src, bool) {
        if (!src) return false;
        value = get_TVector3_from_pyobject(src.ptr());
        return true;
    }

    static handle cast(TVector3 v, return_value_policy /*policy*/, handle /*parent*/) {
        return get_pyobjectptr_from_TVector3(v);
    }
}
}} // end namespaces

@hzhangxyz
Copy link

I have the similar issue, but my object is not a simple object so I cannot use the second way introduced by @jagerman, What should I do? How to expose c++ type/py object caster?

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

5 participants