GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: include/pinocchio/bindings/python/pybind11.hpp Lines: 28 28 100.0 %
Date: 2022-07-05 08:28:44 Branches: 14 24 58.3 %

Line Branch Exec Source
1
#ifndef __pinocchio_python_pybind11_hpp__
2
#define __pinocchio_python_pybind11_hpp__
3
4
/// \mainpage Pinocchio PyBind11 helpers
5
///
6
/// This package provides utilities to ease the use of Pinocchio objects when
7
/// using PyBind11.
8
/// There are two methods:
9
/// 1. The developer-friendly but likely less user-friendly method: \ref
10
/// PINOCCHIO_PYBIND11_TYPE_CASTER
11
/// 2. The user-friendly but less developer-friendly method: \ref
12
/// pinocchio::python::make_pybind11_function
13
///
14
/// Both methods can be mixed. For both cases, you may
15
/// \code
16
/// // with necessary #define. See below
17
/// #include <pinocchio/bindings/python/pybind11-all.hpp>
18
/// \endcode
19
/// to get some \subpage default_type_caster.
20
///
21
/// \section example Example
22
/// \code
23
/// #define SCALAR double
24
/// #define OPTIONS 0
25
/// #define JOINT_MODEL_COLLECTION ::pinocchio::JointCollectionDefaultTpl
26
/// #include <pinocchio/bindings/python/pybind11-all.hpp>
27
///
28
/// ...
29
/// // method 1
30
/// m.def("function", my_function);
31
/// // method 2
32
/// m.def("function", pinocchio::python::make_pybind11_function(my_function));
33
/// \endcode
34
///
35
36
#include <iostream>
37
#include <pinocchio/fwd.hpp>
38
39
// This lines forces clang-format to keep the include split here
40
#include <pybind11/pybind11.h>
41
42
#include <boost/python.hpp>
43
44
namespace pinocchio {
45
namespace python {
46
namespace bp = boost::python;
47
namespace py = pybind11;
48
49
template <typename T>
50
10
inline py::object to(T& t) {
51
  // Create PyObject using boost Python
52
10
  bp::object obj = bp::api::object(t);
53
10
  PyObject* pyobj = obj.ptr();
54
20
  return pybind11::reinterpret_borrow<py::object>(pyobj);
55
}
56
template <typename T>
57
2
inline py::object to(T* t) {
58
  // Create PyObject using boost Python
59
  typename bp::manage_new_object::apply<T*>::type converter;
60
2
  PyObject* pyobj = converter(t);
61
  // Create the Pybind11 object
62
2
  return py::reinterpret_borrow<py::object>(pyobj);
63
}
64
65
template <typename ReturnType>
66
32
inline ReturnType& from(py::handle model) {
67
32
  return bp::extract<ReturnType&>(model.ptr());
68
}
69
70
template <typename T>
71
struct convert_type {
72
  typedef T type;
73
7
  static inline T _to(T t) { return t; }
74
2
  static inline type _from(type t) { return t; }
75
};
76
template <>
77
struct convert_type<void> {
78
  // typedef void type;
79
  // static inline void _to() {}
80
};
81
82
template <typename T>
83
struct convert_boost_python_object {
84
  typedef py::object type;
85
12
  static inline type _to(T t) {
86
    return to<typename std::remove_pointer<typename std::remove_reference<
87
12
        typename std::remove_cv<T>::type>::type>::type>(t);
88
  }
89
16
  static inline T _from(type t) {
90
    return from<
91
32
        typename std::remove_cv<typename std::remove_reference<T>::type>::type>(
92
32
        t);
93
  }
94
};
95
96
/// \brief Defines a conversion used by \ref make_pybind11_function
97
#define PINOCCHIO_PYBIND11_ADD_CONVERT_TYPE(CLASS)                    \
98
  namespace pinocchio {                                               \
99
  namespace python {                                                  \
100
  template <>                                                         \
101
  struct convert_type<CLASS> : convert_boost_python_object<CLASS> {}; \
102
  }                                                                   \
103
  }
104
105
/// \brief Defines a set of conversion used by \ref make_pybind11_function
106
#define _SINGLE_ARG(...) __VA_ARGS__
107
#define PINOCCHIO_PYBIND11_ADD_ALL_CONVERT_TYPE(CLASS)           \
108
  PINOCCHIO_PYBIND11_ADD_CONVERT_TYPE(_SINGLE_ARG(CLASS))        \
109
  PINOCCHIO_PYBIND11_ADD_CONVERT_TYPE(_SINGLE_ARG(CLASS const))  \
110
  PINOCCHIO_PYBIND11_ADD_CONVERT_TYPE(_SINGLE_ARG(CLASS&))       \
111
  PINOCCHIO_PYBIND11_ADD_CONVERT_TYPE(_SINGLE_ARG(CLASS const&)) \
112
  PINOCCHIO_PYBIND11_ADD_CONVERT_TYPE(_SINGLE_ARG(CLASS*))       \
113
  PINOCCHIO_PYBIND11_ADD_CONVERT_TYPE(_SINGLE_ARG(CLASS const*))
114
115
namespace internal {
116
117
template <typename R, typename... Args>
118
26
auto call(R (*f)(Args...), typename convert_type<Args>::type... args) {
119


26
  return convert_type<R>::_to(f(convert_type<Args>::_from(args)...));
120
}
121
template <typename... Args>
122
2
void call(void (*f)(Args...), typename convert_type<Args>::type... args) {
123

2
  f(convert_type<Args>::_from(args)...);
124
}
125
126
template <typename T>
127
struct function_wrapper;
128
129
template <typename R, typename... Args>
130
struct function_wrapper<R (*)(Args...)> {
131
  static const size_t nargs = sizeof...(Args);
132
133
  typedef R result_type;
134
135
  template <size_t i>
136
  struct arg {
137
    typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
138
  };
139
140
  typedef R (*func_type)(Args...);
141
142
  func_type f;
143
144
  // typename convert_type<result_type>::type
145
28
  auto operator()(typename convert_type<Args>::type... args) {
146
28
    return call(f, args...);
147
  }
148
};
149
}  // namespace internal
150
151
/// \brief Creates a function wrapper.
152
///
153
/// Using function wrapper has the advantage of being copy-less when possible
154
/// but the disadvantage of requiring to wrap the exposed function.
155
///
156
/// The wrapper does:
157
/// - converts the argument if a conversion has been previously declared,
158
/// - call the wrapped function
159
/// - converts the result if a conversion has been previously declared.
160
template <typename R, typename... Args>
161
22
internal::function_wrapper<R (*)(Args...)> make_pybind11_function(
162
    R (*func)(Args...)) {
163
  internal::function_wrapper<R (*)(Args...)> wrapper;
164
22
  wrapper.f = func;
165
22
  return wrapper;
166
}
167
168
template <typename T>
169
1
py::object default_arg(T t) {
170
1
  py::object obj = to<T>(t);
171
  //obj.inc_ref();
172
1
  return obj;
173
}
174
175
/// \brief Add a PyBind11 type caster.
176
///
177
/// Using type caster has the advantage of not requiring to wrap the exposed
178
/// functions but the disadvantage of systematically requiring a copy.
179
///
180
/// See \ref https://pybind11.readthedocs.io/en/stable/advanced/cast/custom.html
181
/// "PyBind11 documentation"
182
#define PINOCCHIO_PYBIND11_TYPE_CASTER(native_type, boost_python_name)     \
183
  namespace pybind11 {                                                     \
184
  namespace detail {                                                       \
185
  template <>                                                              \
186
  struct type_caster<native_type> {                                        \
187
    PYBIND11_TYPE_CASTER(_SINGLE_ARG(native_type), boost_python_name);     \
188
                                                                           \
189
    /* Python -> C++ */                                                    \
190
    bool load(pybind11::handle src, bool) {                                \
191
      PyObject* source = src.ptr();                                        \
192
      value = boost::python::extract<native_type>(source);                 \
193
      return !PyErr_Occurred();                                            \
194
    }                                                                      \
195
    /* C++ -> Python */                                                    \
196
    static pybind11::handle cast(native_type src,                          \
197
                                 pybind11::return_value_policy /*policy*/, \
198
                                 pybind11::handle /*parent*/) {            \
199
      return boost::python::api::object(src).ptr();                        \
200
    }                                                                      \
201
  };                                                                       \
202
  } /* namespace detail */                                                 \
203
  } /* namespace pybind11 */
204
205
}  // namespace python
206
}  // namespace pinocchio
207
208
#undef _SINGLE_ARG
209
210
#endif  // #ifndef __pinocchio_python_pybind11_hpp__