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

[python] Add pybind11 header. #1519

Merged
merged 5 commits into from
Oct 15, 2021
Merged

Conversation

jmirabel
Copy link
Contributor

Follows discussion #1518

I think the minimum C++ standard of my solution is 14. Not sure it works with 11.

@wxmerkt I would be happy to see your solution.

Copy link
Contributor

@jcarpent jcarpent left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @jmirabel for providing this.
I made some comments that are easy to handle.

bindings/python/pybind11.hpp Outdated Show resolved Hide resolved
bindings/python/pybind11.hpp Outdated Show resolved Hide resolved
template <typename T>
struct convert_type {
typedef T type;
static inline auto _to(T t) { return t; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inline is not needed here.

bindings/python/pybind11.hpp Outdated Show resolved Hide resolved
bindings/python/pybind11.hpp Outdated Show resolved Hide resolved
unittest/python_pybind11/CMakeLists.txt Outdated Show resolved Hide resolved
unittest/python_pybind11/CMakeLists.txt Outdated Show resolved Hide resolved
Copy link
Contributor

@jcarpent jcarpent left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jmirabel
Could you also add Doxygen info?

@jmirabel
Copy link
Contributor Author

I'll wait to see @wxmerkt proposal before addressing the requested changes.

Could you also add Doxygen info?

You mean documenting functions in Doxygen format ?

@jcarpent
Copy link
Contributor

You mean documenting functions in Doxygen format ?

Yes, exactly.

@jcarpent jcarpent requested a review from wxmerkt August 31, 2021 14:19
@wxmerkt
Copy link
Member

wxmerkt commented Sep 1, 2021

Apologies for the late response to this PR - this week is particularly busy and I wanted to experiment with combining the nice C++->Python caster with an implicit typecaster I've been using(*) - this would avoid having to wrap function calls. I might get to it tomorrow evening or Friday afternoon - however, please don't consider my slow response blocking. Would that be alright or would you like to move ahead before? In general, this PR is very nice and clean, particularly the re-use when going from C++ back to Python.

(*) This raises the question if an implicit type_caster is even desired since it will lead to temporaries when passing objects from Python to C++. It does make it easier to deal with Pinocchio objects for people working on modules with Pybind11 though (no wrapped calls).

@jmirabel
Copy link
Contributor Author

jmirabel commented Sep 2, 2021

Before going further, I would prefer to see what you have. Just the C++ file without the whole project is fine. I think it is enough for me to grasp what you do exactly.

@jcarpent
Copy link
Contributor

jcarpent commented Sep 8, 2021

@wxmerkt @jmirabel any update on this PR?

@wxmerkt
Copy link
Member

wxmerkt commented Sep 9, 2021

My apologies, I went quiet on this one as I didn't find time to explore this further (it's ICRA season).

What I've been doing creates a type_caster inside pybind11's detail namespace - this way we don't need to wrap the functions and as soon as the header is included we can bind methods that take pinocchio types as arguments using pybind11. I realised I never finished the C++-back-to-Python part; and directly plugging in Joseph's solution for this part does not appear to work, but I haven't had time to explore this further. I also haven't checked if the load method of the type_caster results in copies.

The code snippet for Python-to-C++ without wrapping the functions is:

#define BOOST_PYTHON_TO_PYBIND11_TYPE_CASTER(native_type, boost_python_name)           \
    namespace pybind11                                                                 \
    {                                                                                  \
    namespace detail                                                                   \
    {                                                                                  \
    template <>                                                                        \
    struct type_caster<native_type>                                                    \
    {                                                                                  \
    public:                                                                            \
        PYBIND11_TYPE_CASTER(native_type, boost_python_name);                          \
        /* Python -> C++ */                                                            \
        bool load(handle src, bool)                                                    \
        {                                                                              \
            PyObject* source = src.ptr();                                              \
            value = boost::python::extract<native_type>(source);                       \
            return !PyErr_Occurred();                                                  \
        }                                                                              \
        /* C++ -> Python */                                                            \
        static handle cast(native_type src, return_value_policy policy, handle parent) \
        {                                                                              \
            /* TODO */                                                                 \
            throw std::runtime_error("C++ to Python not yet implemented!");            \
            return PyLong_FromLong(1);                                                 \
        }                                                                              \
    };                                                                                 \
    } /* namespace detail */                                                           \
    } /* namespace pybind11 */

BOOST_PYTHON_TO_PYBIND11_TYPE_CASTER(pinocchio::Model, _("pinocchio.pinocchio_pywrap.Model"));
// etc

@jmirabel
Copy link
Contributor Author

jmirabel commented Sep 9, 2021

That's neat. I'll give a try to @wxmerkt and update my PR.

@jcarpent
Copy link
Contributor

jcarpent commented Sep 9, 2021

Thanks @jmirabel and @wxmerkt for your contributions.

@jmirabel
Copy link
Contributor Author

I also haven't checked if the load method of the type_caster results in copies.

I had a look and it results in a copy in both directions. Given the definition of PYBIND11_TYPE_CASTER, I don't see any workaround.

I think it is fine to provide both options:

  • no copy with wrapper,
  • copy without wrapper (developer-friendly but likely less user friendly).

@jcarpent
Copy link
Contributor

My apologies, I went quiet on this one as I didn't find time to explore this further (it's ICRA season).

What I've been doing creates a type_caster inside pybind11's detail namespace - this way we don't need to wrap the functions and as soon as the header is included we can bind methods that take pinocchio types as arguments using pybind11. I realised I never finished the C++-back-to-Python part; and directly plugging in Joseph's solution for this part does not appear to work, but I haven't had time to explore this further. I also haven't checked if the load method of the type_caster results in copies.

The code snippet for Python-to-C++ without wrapping the functions is:

#define BOOST_PYTHON_TO_PYBIND11_TYPE_CASTER(native_type, boost_python_name)           \
    namespace pybind11                                                                 \
    {                                                                                  \
    namespace detail                                                                   \
    {                                                                                  \
    template <>                                                                        \
    struct type_caster<native_type>                                                    \
    {                                                                                  \
    public:                                                                            \
        PYBIND11_TYPE_CASTER(native_type, boost_python_name);                          \
        /* Python -> C++ */                                                            \
        bool load(handle src, bool)                                                    \
        {                                                                              \
            PyObject* source = src.ptr();                                              \
            value = boost::python::extract<native_type>(source);                       \
            return !PyErr_Occurred();                                                  \
        }                                                                              \
        /* C++ -> Python */                                                            \
        static handle cast(native_type src, return_value_policy policy, handle parent) \
        {                                                                              \
            /* TODO */                                                                 \
            throw std::runtime_error("C++ to Python not yet implemented!");            \
            return PyLong_FromLong(1);                                                 \
        }                                                                              \
    };                                                                                 \
    } /* namespace detail */                                                           \
    } /* namespace pybind11 */

BOOST_PYTHON_TO_PYBIND11_TYPE_CASTER(pinocchio::Model, _("pinocchio.pinocchio_pywrap.Model"));
// etc

It should be easy to implement the C++ -> Python. @wxmerkt Do you need it?

@jcarpent
Copy link
Contributor

jcarpent commented Oct 1, 2021

@jmirabel Could you fix the CI issues? I would suggest using a CMake external project to find PyBind11 instead of relying on the one installed on the system.

@jmirabel
Copy link
Contributor Author

jmirabel commented Oct 6, 2021

It's in my todo list.

I would suggest using a CMake external project to find PyBind11

You mean let CMake download the sources at configure time ?

@jcarpent
Copy link
Contributor

jcarpent commented Oct 6, 2021

Yes, this is what I mean.

@jmirabel
Copy link
Contributor Author

jmirabel commented Oct 13, 2021

The error on Windows looks unrelated to this PR:

pinocchio/math/fwd.hpp(69,5): error : expected unqualified-id [cpp2pybind11.vcxproj]
pinocchio/math/fwd.hpp(57,68): message : expanded from macro 'PINOCCHIO_OVERLOAD_MATH_BINARY_OPERATOR' [cpp2pybind11.vcxproj]
pinocchio/math/fwd.hpp(69,5): error : expected ')' [cpp2pybind11.vcxproj]
pinocchio/math/fwd.hpp(57,68): message : expanded from macro 'PINOCCHIO_OVERLOAD_MATH_BINARY_OPERATOR' [cpp2pybind11.vcxproj]
pinocchio/math/fwd.hpp(69,45): message : to match this '(' [cpp2pybind11.vcxproj]
pinocchio/math/fwd.hpp(69,45): error : expected ')' [cpp2pybind11.vcxproj]
pinocchio/math/fwd.hpp(69,45): message : to match this '(' [cpp2pybind11.vcxproj]
pinocchio/math/fwd.hpp(69,45): error : expected ')' [cpp2pybind11.vcxproj]
pinocchio/math/fwd.hpp(69,45): message : to match this '(' [cpp2pybind11.vcxproj]

Any hint of what's happening ?

@jcarpent
Copy link
Contributor

It is weird that this is only occurring on this test. Could you check this is not an issue of include?

@jmirabel
Copy link
Contributor Author

It happens from the pybind11 unit test but I don't get what I do wrong.

Could you check this is not an issue of include?

What do you mean ?

@jcarpent
Copy link
Contributor

I think on Windows they might me issue with comma parsing in Macros.
You can check in the macro.hpp file of Pinocchio to check that.

@jmirabel
Copy link
Contributor Author

The issue is with min and max. The expansion of PINOCCHIO_OVERLOAD_MATH_BINARY_OPERATOR(min) fails at (expanded):

    template<typename T1, typename T2> \
    inline typename internal::return_type_min<T1,T2>::type min(const T1 & a, const T2 & b) \
    { return internal::call_min<T1,T2>::run(a,b); }

Precisely, it isn't happy about the first const after (. For some reason, I think it parses min as a macro call.

@jmirabel
Copy link
Contributor Author

CI is now all green except from codacy which shows what appears like irrelevant issues.

@jcarpent jcarpent merged commit 364f573 into stack-of-tasks:devel Oct 15, 2021
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

Successfully merging this pull request may close these issues.

3 participants