Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Stack Of Tasks
pinocchio
Commits
364f5734
Unverified
Commit
364f5734
authored
Oct 15, 2021
by
Justin Carpentier
Committed by
GitHub
Oct 15, 2021
Browse files
Merge pull request #1519 from jmirabel/pybind11
[python] Add pybind11 header.
parents
22959bfb
ce31b402
Pipeline
#16554
passed with stage
in 34 minutes and 12 seconds
Changes
7
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
bindings/python/CMakeLists.txt
View file @
364f5734
...
...
@@ -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
)
bindings/python/pybind11-all.hpp
0 → 100644
View file @
364f5734
// No header guard on purpose because the file can be included several times
// with different value for preprocessor variables SCALAR, OPTIONS and
// JOINT_MODEL_COLLECTION
/// \page default_type_caster Predefined casters
///
/// At the time of writting, this exposes
/// \li ModelTpl
/// \li DataTpl
/// \li SE3Tpl
/// \li MotionTpl
/// \li GeometryModel
/// \li (not yet) GeometryData
#if !defined SCALAR or !defined OPTIONS or !defined JOINT_MODEL_COLLECTION
#error \
"You must define SCALAR, OPTIONS and JOINT_MODEL_COLLECTION before including this file."
#endif
#include
<pinocchio/bindings/python/pybind11.hpp>
#include
<pinocchio/multibody/data.hpp>
#include
<pinocchio/multibody/geometry.hpp>
#include
<pinocchio/multibody/model.hpp>
// Required to be able to pass argument with commas to macros
#define _SINGLE_ARG(...) __VA_ARGS__
#define _PINOCCHIO_PYBIND11_EXPOSE(type, name) \
PINOCCHIO_PYBIND11_ADD_ALL_CONVERT_TYPE(_SINGLE_ARG(type)) \
PINOCCHIO_PYBIND11_TYPE_CASTER(_SINGLE_ARG(type), name)
_PINOCCHIO_PYBIND11_EXPOSE
(
_SINGLE_ARG
(
::
pinocchio
::
SE3Tpl
<
SCALAR
,
OPTIONS
>
),
_
(
"pinocchio.pinocchio_pywrap.SE3"
))
_PINOCCHIO_PYBIND11_EXPOSE
(
_SINGLE_ARG
(
::
pinocchio
::
MotionTpl
<
SCALAR
,
OPTIONS
>
),
_
(
"pinocchio.pinocchio_pywrap.Motion"
))
_PINOCCHIO_PYBIND11_EXPOSE
(
_SINGLE_ARG
(
::
pinocchio
::
ModelTpl
<
SCALAR
,
OPTIONS
,
JOINT_MODEL_COLLECTION
>
),
_
(
"pinocchio.pinocchio_pywrap.Model"
))
_PINOCCHIO_PYBIND11_EXPOSE
(
_SINGLE_ARG
(
::
pinocchio
::
DataTpl
<
SCALAR
,
OPTIONS
,
JOINT_MODEL_COLLECTION
>
),
_
(
"pinocchio.pinocchio_pywrap.Model"
))
_PINOCCHIO_PYBIND11_EXPOSE
(
::
pinocchio
::
GeometryModel
,
_
(
"pinocchio.pinocchio_pywrap.GeometryModel"
))
// \todo this triggers a warning because GeometryData has
// a copy constructor and no operator=
// _PINOCCHIO_PYBIND11_EXPOSE(::pinocchio::GeometryData,
// _("pinocchio.pinocchio_pywrap.GeometryData"))
#undef _PINOCCHIO_PYBIND11_EXPOSE
#undef _SINGLE_ARG
bindings/python/pybind11.hpp
0 → 100644
View file @
364f5734
#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
<boost/python.hpp>
namespace
pinocchio
{
namespace
python
{
namespace
bp
=
boost
::
python
;
namespace
py
=
pybind11
;
template
<
typename
T
>
inline
py
::
object
to
(
T
&
t
)
{
// Create PyObject using boost Python
bp
::
object
obj
=
bp
::
api
::
object
(
t
);
PyObject
*
pyobj
=
obj
.
ptr
();
return
pybind11
::
reinterpret_borrow
<
py
::
object
>
(
pyobj
);
}
template
<
typename
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
py
::
reinterpret_borrow
<
py
::
object
>
(
pyobj
);
}
template
<
typename
ReturnType
>
inline
ReturnType
&
from
(
py
::
handle
model
)
{
return
bp
::
extract
<
ReturnType
&>
(
model
.
ptr
());
}
template
<
typename
T
>
struct
convert_type
{
typedef
T
type
;
static
inline
T
_to
(
T
t
)
{
return
t
;
}
static
inline
type
_from
(
type
t
)
{
return
t
;
}
};
template
<
>
struct
convert_type
<
void
>
{
// typedef void type;
// static inline void _to() {}
};
template
<
typename
T
>
struct
convert_boost_python_object
{
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
);
}
static
inline
T
_from
(
type
t
)
{
return
from
<
typename
std
::
remove_cv
<
typename
std
::
remove_reference
<
T
>::
type
>::
type
>
(
t
);
}
};
/// \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> {}; \
} \
}
/// \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
{
template
<
typename
R
,
typename
...
Args
>
auto
call
(
R
(
*
f
)(
Args
...),
typename
convert_type
<
Args
>::
type
...
args
)
{
return
convert_type
<
R
>::
_to
(
f
(
convert_type
<
Args
>::
_from
(
args
)...));
}
template
<
typename
...
Args
>
void
call
(
void
(
*
f
)(
Args
...),
typename
convert_type
<
Args
>::
type
...
args
)
{
f
(
convert_type
<
Args
>::
_from
(
args
)...);
}
template
<
typename
T
>
struct
function_wrapper
;
template
<
typename
R
,
typename
...
Args
>
struct
function_wrapper
<
R
(
*
)(
Args
...)
>
{
static
const
size_t
nargs
=
sizeof
...(
Args
);
typedef
R
result_type
;
template
<
size_t
i
>
struct
arg
{
typedef
typename
std
::
tuple_element
<
i
,
std
::
tuple
<
Args
...
>>::
type
type
;
};
typedef
R
(
*
func_type
)(
Args
...);
func_type
f
;
// typename convert_type<result_type>::type
auto
operator
()(
typename
convert_type
<
Args
>::
type
...
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_pybind11_function
(
R
(
*
func
)(
Args
...))
{
internal
::
function_wrapper
<
R
(
*
)(
Args
...)
>
wrapper
;
wrapper
.
f
=
func
;
return
wrapper
;
}
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__
unittest/python/CMakeLists.txt
View file @
364f5734
...
...
@@ -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
)
unittest/python/pybind11/CMakeLists.txt
0 → 100644
View file @
364f5734
if
(
CMAKE_VERSION VERSION_GREATER 3.11
)
include
(
FetchContent
)
FetchContent_Declare
(
pybind11
GIT_REPOSITORY https://github.com/pybind/pybind11
GIT_TAG v2.8.0
)
FetchContent_GetProperties
(
pybind11
)
if
(
NOT pybind11_POPULATED
)
FetchContent_Populate
(
pybind11
)
add_subdirectory
(
${
pybind11_SOURCE_DIR
}
${
pybind11_BINARY_DIR
}
)
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
()
if
(
WIN32
)
target_compile_definitions
(
cpp2pybind11 PRIVATE -DNOMINMAX
)
endif
(
WIN32
)
endif
()
endif
()
unittest/python/pybind11/cpp2pybind11.cpp
0 → 100644
View file @
364f5734
#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
);
}
unittest/python/pybind11/test_cpp2pybind11.py
0 → 100644
View file @
364f5734
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
idv
=
id
(
v
)
gc
.
collect
()
#n = len(gc.get_referrers(v))
n
=
sys
.
getrefcount
(
v
)
print
(
"ref count of"
,
what
,
idv
,
n
)
m
=
cpp2pybind11
.
make_model
()
print_ref_count
(
m
,
"m"
)
print
(
cpp2pybind11
.
get_ptr
(
m
))
print_ref_count
(
m
,
"m"
)
m
.
name
=
""
cpp2pybind11
.
testModel1
(
m
)
print_ref_count
(
m
,
"m"
)
assert
m
.
name
.
startswith
(
"testModel1"
)
addr2
=
cpp2pybind11
.
testModel2
(
m
,
1
)
print_ref_count
(
m
,
"m"
)
assert
m
.
name
.
startswith
(
"testModel2"
)
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"
)
mmm
=
m
print_ref_count
(
m
,
"m"
)
print_ref_count
(
mm
,
"mm"
)
assert
cpp2pybind11
.
get_ptr
(
m
)
==
cpp2pybind11
.
get_ptr
(
mm
)
if
False
:
print
(
"deleting m"
)
del
m
print
(
"deleted m"
)
print
(
"mm is"
,
mm
)
else
:
print
(
"deleting mm"
)
del
mm
print
(
"deleted mm"
)
print
(
"m is"
,
m
)
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment