Commit 7e06d464 authored by Joseph Mirabel's avatar Joseph Mirabel
Browse files

[python] Update pybind11 headers.

parent f326fc11
......@@ -260,4 +260,18 @@ IF(BUILD_PYTHON_INTERFACE)
FILES "${CMAKE_CURRENT_BINARY_DIR}/pinocchiopy.pc"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
PERMISSIONS OWNER_READ GROUP_READ WORLD_READ OWNER_WRITE)
IF(DOXYGEN_FOUND AND DOXYGEN_VERSION VERSION_GREATER 1.8.17)
SET(DOXYGEN_GENERATE_HTML YES)
SET(DOXYGEN_GENERATE_LATEX NO)
SET(DOXYGEN_PROJECT_NAME "Pinocchio PyBind11 helpers.")
message("DOXYGEN_GENERATE_HTML: ${DOXYGEN_GENERATE_HTML}")
cmake_policy(PUSH)
cmake_policy(SET CMP0054 NEW)
doxygen_add_docs(doc_pybind11
pybind11.hpp pybind11-all.hpp
USE_STAMP_FILE
COMMENT "Generating documentation of the PyBind11 helpers.")
cmake_policy(POP)
ENDIF()
ENDIF(BUILD_PYTHON_INTERFACE)
#ifndef __pinocchio_python_pybind11_hpp__
#define __pinocchio_python_pybind11_hpp__
/// \mainpage Pinocchio PyBind11 helpers
///
/// This package provides utilities to ease the use of Pinocchio objects when
/// using PyBind11.
/// There are two methods:
/// 1. The developer-friendly but likely less user-friendly method: \ref
/// PINOCCHIO_PYBIND11_TYPE_CASTER
/// 2. The user-friendly but less developer-friendly method: \ref
/// pinocchio::python::make_pybind11_function
///
/// Both methods can be mixed. For both cases, you may
/// \code
/// // with necessary #define. See below
/// #include <pinocchio/bindings/python/pybind11-all.hpp>
/// \endcode
/// to get some \subpage default_type_caster.
///
/// \section example Example
/// \code
/// #define SCALAR double
/// #define OPTIONS 0
/// #define JOINT_MODEL_COLLECTION ::pinocchio::JointCollectionDefaultTpl
/// #include <pinocchio/bindings/python/pybind11-all.hpp>
///
/// ...
/// // method 1
/// m.def("function", my_function);
/// // method 2
/// m.def("function", pinocchio::python::make_pybind11_function(my_function));
/// \endcode
///
#include <iostream>
#include <pinocchio/fwd.hpp>
// This lines forces clang-format to keep the include split here
#include <pybind11/pybind11.h>
#include <pinocchio/multibody/model.hpp>
#include <boost/python.hpp>
namespace pinocchio {
namespace cpp2pybind11 {
namespace python {
namespace bp = boost::python;
namespace py = pybind11;
template<typename T> pybind11::object to(T& t)
{
template <typename T>
void _print(T const&) {}
template <typename T>
inline py::object to(T& t) {
// Create PyObject using boost Python
typename bp::reference_existing_object::apply<T*>::type converter;
PyObject* pyobj = converter(t);
// Create the Pybind11 object
return pybind11::reinterpret_borrow<pybind11::object>(pyobj);
bp::object obj = bp::api::object(t);
PyObject* pyobj = obj.ptr();
return pybind11::reinterpret_borrow<py::object>(pyobj);
}
template <typename T>
auto to(T* t) {
inline py::object to(T* t) {
// Create PyObject using boost Python
typename bp::manage_new_object::apply<T*>::type converter;
PyObject* pyobj = converter(t);
// Create the Pybind11 object
return pybind11::reinterpret_steal<pybind11::object>(pyobj);
return py::reinterpret_borrow<py::object>(pyobj);
}
template <typename ReturnType>
ReturnType& from(pybind11::object model) {
return boost::python::extract<ReturnType&>(model.ptr());
inline ReturnType& from(py::handle model) {
return bp::extract<ReturnType&>(model.ptr());
}
template <typename T>
struct convert_type {
typedef T type;
static inline auto _to(T t) { return t; }
static inline auto _from(type t) { return t; }
static inline T _to(T t) { return t; }
static inline type _from(type t) { return t; }
};
template <>
struct convert_type<void> {
static inline void _to() {}
// typedef void type;
// static inline void _to() {}
};
template <typename T>
struct convert_boost_python_object {
typedef pybind11::object type;
static inline auto _to(T t) {
typedef py::object type;
static inline type _to(T t) {
return to<typename std::remove_pointer<typename std::remove_reference<
typename std::remove_cv<T>::type>::type>::type>(t);
typename std::remove_cv<T>::type>::type>::type>(t);
}
static inline T _from(type t) {
return from<
......@@ -53,14 +96,24 @@ struct convert_boost_python_object {
}
};
#define ADD_CONVERT_TYPE(CLASS) \
template <> \
struct convert_type<pinocchio::CLASS> \
: convert_boost_python_object<pinocchio::CLASS> {}
/// \brief Defines a conversion used by \ref make_pybind11_function
#define PINOCCHIO_PYBIND11_ADD_CONVERT_TYPE(CLASS) \
namespace pinocchio { \
namespace python { \
template <> \
struct convert_type<CLASS> : convert_boost_python_object<CLASS> {}; \
} \
}
ADD_CONVERT_TYPE(Model*);
ADD_CONVERT_TYPE(Model&);
ADD_CONVERT_TYPE(Model const&);
/// \brief Defines a set of conversion used by \ref make_pybind11_function
#define _SINGLE_ARG(...) __VA_ARGS__
#define PINOCCHIO_PYBIND11_ADD_ALL_CONVERT_TYPE(CLASS) \
PINOCCHIO_PYBIND11_ADD_CONVERT_TYPE(_SINGLE_ARG(CLASS)) \
PINOCCHIO_PYBIND11_ADD_CONVERT_TYPE(_SINGLE_ARG(CLASS const)) \
PINOCCHIO_PYBIND11_ADD_CONVERT_TYPE(_SINGLE_ARG(CLASS&)) \
PINOCCHIO_PYBIND11_ADD_CONVERT_TYPE(_SINGLE_ARG(CLASS const&)) \
PINOCCHIO_PYBIND11_ADD_CONVERT_TYPE(_SINGLE_ARG(CLASS*)) \
PINOCCHIO_PYBIND11_ADD_CONVERT_TYPE(_SINGLE_ARG(CLASS const*))
namespace internal {
......@@ -70,7 +123,7 @@ auto call(R (*f)(Args...), typename convert_type<Args>::type... args) {
}
template <typename... Args>
void call(void (*f)(Args...), typename convert_type<Args>::type... args) {
return f(convert_type<Args>::_from(args)...);
f(convert_type<Args>::_from(args)...);
}
template <typename T>
......@@ -91,19 +144,72 @@ struct function_wrapper<R (*)(Args...)> {
func_type f;
// typename convert_type<result_type>::type
auto operator()(typename convert_type<Args>::type... args) {
// return convert_type<R>::_to(f(convert_type<Args>::_from(args)...));
return call(f, args...);
}
};
} // namespace internal
/// \brief Creates a function wrapper.
///
/// Using function wrapper has the advantage of being copy-less when possible
/// but the disadvantage of requiring to wrap the exposed function.
///
/// The wrapper does:
/// - converts the argument if a conversion has been previously declared,
/// - call the wrapped function
/// - converts the result if a conversion has been previously declared.
template <typename R, typename... Args>
internal::function_wrapper<R (*)(Args...)> make_function(R (*func)(Args...)) {
internal::function_wrapper<R (*)(Args...)> make_pybind11_function(
R (*func)(Args...)) {
internal::function_wrapper<R (*)(Args...)> wrapper;
wrapper.f = func;
return wrapper;
}
} // namespace cpp2pybind11
template <typename T>
py::object default_arg(T t) {
py::object obj = to<T>(t);
//obj.inc_ref();
return obj;
}
/// \brief Add a PyBind11 type caster.
///
/// Using type caster has the advantage of not requiring to wrap the exposed
/// functions but the disadvantage of systematically requiring a copy.
///
/// See \ref https://pybind11.readthedocs.io/en/stable/advanced/cast/custom.html
/// "PyBind11 documentation"
#define PINOCCHIO_PYBIND11_TYPE_CASTER(native_type, boost_python_name) \
namespace pybind11 { \
namespace detail { \
template <> \
struct type_caster<native_type> { \
PYBIND11_TYPE_CASTER(_SINGLE_ARG(native_type), boost_python_name); \
\
/* Python -> C++ */ \
bool load(pybind11::handle src, bool) { \
PyObject* source = src.ptr(); \
value = boost::python::extract<native_type>(source); \
return !PyErr_Occurred(); \
} \
/* C++ -> Python */ \
static pybind11::handle cast(native_type src, \
pybind11::return_value_policy /*policy*/, \
pybind11::handle /*parent*/) { \
typename boost::python::manage_new_object::apply<native_type*>::type \
converter; \
return boost::python::api::object(src).ptr(); \
} \
}; \
} /* namespace detail */ \
} /* namespace pybind11 */
} // namespace python
} // namespace pinocchio
#undef _SINGLE_ARG
#endif // #ifndef __pinocchio_python_pybind11_hpp__
......@@ -78,3 +78,5 @@ FOREACH(TEST ${${PROJECT_NAME}_PYTHON_TESTS})
ENDFOREACH(TEST ${${PROJECT_NAME}_PYTHON_TESTS})
MAKE_DIRECTORY("${CMAKE_CURRENT_BINARY_DIR}/serialization-data")
ADD_SUBDIRECTORY(pybind11)
find_package(pybind11 REQUIRED)
pybind11_add_module(cpp2pybind11 cpp2pybind11.cpp)
target_link_libraries(cpp2pybind11 PRIVATE pinocchio_pywrap)
if(CMAKE_CXX_STANDARD LESS 14)
message(STATUS "CXX_STANDARD for cpp2pybind11 set changed from ${CMAKE_CXX_STANDARD} to 14")
set_target_properties(cpp2pybind11 PROPERTIES CXX_STANDARD 14)
endif()
#include <pinocchio/bindings/python/pybind11.hpp>
// This lines forces clang-format to keep the include split here
#include <pybind11/pybind11.h>
#include <boost/python.hpp>
#define SCALAR double
#define OPTIONS 0
#define JOINT_MODEL_COLLECTION ::pinocchio::JointCollectionDefaultTpl
#include <pinocchio/bindings/python/pybind11-all.hpp>
pinocchio::Model* make_model() {
pinocchio::Model* model = new pinocchio::Model;
std::cout << "make_model: " << reinterpret_cast<intptr_t>(model) << std::endl;
return model;
}
pinocchio::Model& return_same_model_copy(pinocchio::Model& m) { return m; }
pinocchio::Model* return_same_model_nocopy(pinocchio::Model& m) { return &m; }
pinocchio::SE3 multiply_se3(pinocchio::SE3 const& a, pinocchio::SE3 const& b) {
return a * b;
}
template <typename T>
intptr_t get_ptr(T& m) {
std::cout << &m << '\n' << m << std::endl;
return reinterpret_cast<intptr_t>(&m);
}
void test1(int i) { std::cout << "no conversion: " << ' ' << i << std::endl; }
void testModel1(pinocchio::Model& model) {
std::cout << "testModel1: " << &model << std::endl;
model.name = "testModel1: I modified the model name";
}
intptr_t testModel2(pinocchio::Model& model, int i) {
std::cout << "testModel2: " << &model << ' ' << i << std::endl;
model.name = "testModel2: I modified the model name";
return reinterpret_cast<intptr_t>(&model);
}
intptr_t testModel3(pinocchio::Model const& model, int i) {
std::cout << "testModel3: " << &model << ' ' << i << std::endl;
return reinterpret_cast<intptr_t>(&model);
}
void testModel_manual(pybind11::object model) {
testModel1(pinocchio::python::from<pinocchio::Model&>(model));
}
using pinocchio::python::make_pybind11_function;
PYBIND11_MODULE(cpp2pybind11, m) {
using namespace pybind11::literals; // For _a
pybind11::module::import("pinocchio");
m.def("testModel_manual", testModel_manual);
m.def("test1", make_pybind11_function(&test1));
m.def("make_model", make_pybind11_function(&make_model));
m.def("return_same_model_broken", make_pybind11_function(&return_same_model_copy),
pybind11::return_value_policy::reference);
m.def("return_same_model", make_pybind11_function(&return_same_model_nocopy),
pybind11::return_value_policy::reference);
m.def("get_ptr", make_pybind11_function(&get_ptr<pinocchio::Model>));
m.def("get_se3_ptr", make_pybind11_function(&get_ptr<pinocchio::SE3>));
m.def("multiply_se3_1", make_pybind11_function(&multiply_se3), "a"_a, "b"_a);
m.def("multiply_se3", make_pybind11_function(&multiply_se3), "a"_a,
"b"_a = pinocchio::python::default_arg(pinocchio::SE3::Identity()));
m.def("testModel1", make_pybind11_function(&testModel1));
m.def("testModel2", make_pybind11_function(&testModel2));
m.def("testModel3", make_pybind11_function(&testModel3));
pybind11::module no_wrapper = m.def_submodule("no_wrapper");
no_wrapper.def("multiply_se3", &multiply_se3, "a"_a,
"b"_a
// does not work = pinocchio::SE3::Identity()
);
no_wrapper.def("testModel1", &testModel1);
no_wrapper.def("testModel2", &testModel2);
no_wrapper.def("testModel3", &testModel3);
}
import cpp2pybind11, sys, gc
import pinocchio
a = pinocchio.SE3.Random()
b = pinocchio.SE3.Random()
assert cpp2pybind11.multiply_se3_1(a, b) == a * b
assert cpp2pybind11.multiply_se3(a,b) == a * b
assert cpp2pybind11.no_wrapper.multiply_se3(a, b) == a * b
assert cpp2pybind11.multiply_se3(a) == a
#assert cpp2pybind11.no_wrapper.multiply_se3(a) == a
def print_ref_count(v,what=""):
# - 2 because one for variable v and one for variable inside getrefcount
......@@ -26,6 +35,9 @@ addr3 = cpp2pybind11.testModel3(m, 2)
print_ref_count(m, "m")
assert addr2 == addr3
mm = cpp2pybind11.return_same_model_broken(m)
assert cpp2pybind11.get_ptr(m) != cpp2pybind11.get_ptr(mm)
mm = cpp2pybind11.return_same_model(m)
# Not sure why but the ref count of m and mm sticks to one
print_ref_count(m, "m")
......@@ -34,7 +46,7 @@ print_ref_count(m, "m")
print_ref_count(mm, "mm")
assert cpp2pybind11.get_ptr(m) == cpp2pybind11.get_ptr(mm)
if True:
if False:
print("deleting m")
del m
print("deleted m")
......
cmake_minimum_required(VERSION 3.4)
project(test_boost_pybind11 LANGUAGES CXX)
find_package(pybind11 REQUIRED)
find_package(pinocchio REQUIRED)
execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import sys; sys.stdout.write(sys.version[:3])"
OUTPUT_VARIABLE PYTHON_MAJOR_MINOR_VERSION)
set(BOOST_PYTHON python${PYTHON_MAJOR_MINOR_VERSION})
find_package(Boost 1.70 REQUIRED COMPONENTS ${BOOST_PYTHON})
pybind11_add_module(cpp2pybind11 cpp2pybind11.cpp)
target_link_libraries(cpp2pybind11 PRIVATE pinocchio::pinocchio Boost::${BOOST_PYTHON})
#include <pinocchio/multibody/model.hpp>
#include <pybind11/pybind11.h>
#include <boost/python.hpp>
#include <pinocchio/bindings/python/pybind11.hpp>
pinocchio::Model* make_model()
{
pinocchio::Model* model = new pinocchio::Model;
std::cout << "make_model: " << reinterpret_cast<intptr_t>(model) << std::endl;
return model;
}
pinocchio::Model& return_same_model(pinocchio::Model& m)
{
return m;
}
intptr_t get_ptr(pinocchio::Model& model)
{
return reinterpret_cast<intptr_t>(&model);
}
void test1(int i)
{
std::cout << "no conversion: " << ' ' << i << std::endl;
}
void testModel1(pinocchio::Model& model)
{
std::cout << "testModel1: " << &model << std::endl;
model.name = "testModel1: I modified the model name";
}
intptr_t testModel2(pinocchio::Model& model, int i)
{
std::cout << "testModel2: " << &model << ' ' << i << std::endl;
model.name = "testModel2: I modified the model name";
return reinterpret_cast<intptr_t>(&model);
}
intptr_t testModel3(pinocchio::Model const& model, int i)
{
std::cout << "testModel3: " << &model << ' ' << i << std::endl;
return reinterpret_cast<intptr_t>(&model);
}
void testModel_manual(pybind11::object model) {
testModel1(pinocchio::cpp2pybind11::from<pinocchio::Model&>(model));
}
PYBIND11_MODULE(cpp2pybind11, m) {
pybind11::module::import("pinocchio");
m.def("testModel_manual", testModel_manual);
m.def("test1", pinocchio::cpp2pybind11::make_function(&test1));
m.def("make_model", pinocchio::cpp2pybind11::make_function(&make_model));
m.def("return_same_model", pinocchio::cpp2pybind11::make_function(&return_same_model),
pybind11::return_value_policy::reference);
m.def("get_ptr", pinocchio::cpp2pybind11::make_function(&get_ptr));
m.def("testModel1", pinocchio::cpp2pybind11::make_function(&testModel1));
m.def("testModel2", pinocchio::cpp2pybind11::make_function(&testModel2));
m.def("testModel3", pinocchio::cpp2pybind11::make_function(&testModel3));
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment