Skip to content

Commit 727fad6

Browse files
Joris Vaillantjcarpent
authored andcommitted
[WIP] core: add methods to already defined vector type
1 parent 6bca832 commit 727fad6

File tree

5 files changed

+206
-26
lines changed

5 files changed

+206
-26
lines changed

include/eigenpy/registration.hpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#define __eigenpy_registration_hpp__
88

99
#include "eigenpy/fwd.hpp"
10+
#include "eigenpy/registration_class.hpp"
1011

1112
namespace eigenpy {
1213

@@ -50,6 +51,25 @@ inline bool register_symbolic_link_to_registered_type() {
5051

5152
return false;
5253
}
54+
55+
/// Same as \see register_symbolic_link_to_registered_type() but apply \p
56+
/// visitor on \tparam T if it already exists
57+
template <typename T, typename Visitor>
58+
inline bool register_symbolic_link_to_registered_type(const Visitor& visitor) {
59+
if (eigenpy::check_registration<T>()) {
60+
const bp::type_info info = bp::type_id<T>();
61+
const bp::converter::registration* reg =
62+
bp::converter::registry::query(info);
63+
bp::handle<> class_obj(reg->get_class_object());
64+
bp::object object(class_obj);
65+
bp::scope().attr(reg->get_class_object()->tp_name) = object;
66+
registration_class<T> cl(object);
67+
cl.def(visitor);
68+
return true;
69+
}
70+
71+
return false;
72+
}
5373
} // namespace eigenpy
5474

5575
#endif // ifndef __eigenpy_registration_hpp__
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright 2023, INRIA
3+
*/
4+
5+
#ifndef __eigenpy_registration_class_hpp__
6+
#define __eigenpy_registration_class_hpp__
7+
8+
#include <boost/python/class.hpp>
9+
10+
#include "eigenpy/fwd.hpp"
11+
12+
namespace eigenpy {
13+
14+
/*! Copy of the \see boost::python::class_
15+
* This class allow to add methods to an existing class without registering it
16+
* again.
17+
**/
18+
template <class W>
19+
class registration_class {
20+
public:
21+
using self = registration_class;
22+
23+
/// \p object Hold the namespace of the class that will be modified
24+
registration_class(bp::object object) : m_object(object) {}
25+
26+
/// \see boost::python::class_::def(bp::def_visitor<Derived> const& visitor)
27+
template <class Visitor>
28+
self& def(Visitor const& visitor) {
29+
visitor.visit(*this);
30+
return *this;
31+
}
32+
33+
/// \see boost::python::class_::def(char const* name, F f)
34+
template <class F>
35+
self& def(char const* name, F f) {
36+
def_impl(bp::detail::unwrap_wrapper((W*)0), name, f,
37+
bp::detail::def_helper<char const*>(0), &f);
38+
return *this;
39+
}
40+
41+
/// \see boost::python::class_::def(char const* name, A1 a1, A2 const& a2)
42+
template <class A1, class A2>
43+
self& def(char const* name, A1 a1, A2 const& a2) {
44+
def_maybe_overloads(name, a1, a2, &a2);
45+
return *this;
46+
}
47+
48+
/// \see boost::python::class_::def(char const* name, Fn fn, A1 const& a1, A2
49+
/// const& a2)
50+
template <class Fn, class A1, class A2>
51+
self& def(char const* name, Fn fn, A1 const& a1, A2 const& a2) {
52+
def_impl(bp::detail::unwrap_wrapper((W*)0), name, fn,
53+
bp::detail::def_helper<A1, A2>(a1, a2), &fn);
54+
55+
return *this;
56+
}
57+
58+
/// \see boost::python::class_::def(char const* name, Fn fn, A1 const& a1, A2
59+
/// const& a2, A3 const& a3)
60+
template <class Fn, class A1, class A2, class A3>
61+
self& def(char const* name, Fn fn, A1 const& a1, A2 const& a2, A3 const& a3) {
62+
def_impl(bp::detail::unwrap_wrapper((W*)0), name, fn,
63+
bp::detail::def_helper<A1, A2, A3>(a1, a2, a3), &fn);
64+
65+
return *this;
66+
}
67+
68+
private:
69+
/// \see boost::python::class_::def_impl(T*, char const* name, Fn fn, Helper
70+
/// const& helper, ...)
71+
template <class T, class Fn, class Helper>
72+
inline void def_impl(T*, char const* name, Fn fn, Helper const& helper, ...) {
73+
bp::objects::add_to_namespace(
74+
m_object, name,
75+
make_function(fn, helper.policies(), helper.keywords(),
76+
bp::detail::get_signature(fn, (T*)0)),
77+
helper.doc());
78+
79+
def_default(name, fn, helper,
80+
boost::mpl::bool_<Helper::has_default_implementation>());
81+
}
82+
83+
/// \see boost::python::class_::def_default(char const* name, Fn, Helper
84+
/// const& helper, boost::mpl::bool_<true>)
85+
template <class Fn, class Helper>
86+
inline void def_default(char const* name, Fn, Helper const& helper,
87+
boost::mpl::bool_<true>) {
88+
bp::detail::error::virtual_function_default<
89+
W, Fn>::must_be_derived_class_member(helper.default_implementation());
90+
91+
bp::objects::add_to_namespace(
92+
m_object, name,
93+
make_function(helper.default_implementation(), helper.policies(),
94+
helper.keywords()));
95+
}
96+
97+
/// \see boost::python::class_::def_default(char const*, Fn, Helper const&,
98+
/// boost::mpl::bool_<false>)
99+
template <class Fn, class Helper>
100+
inline void def_default(char const*, Fn, Helper const&,
101+
boost::mpl::bool_<false>) {}
102+
103+
/// \see boost::python::class_::def_maybe_overloads(char const* name, SigT
104+
/// sig,OverloadsT const& overloads,bp::detail::overloads_base const*)
105+
template <class OverloadsT, class SigT>
106+
void def_maybe_overloads(char const* name, SigT sig,
107+
OverloadsT const& overloads,
108+
bp::detail::overloads_base const*)
109+
110+
{
111+
bp::detail::define_with_defaults(name, overloads, *this,
112+
bp::detail::get_signature(sig));
113+
}
114+
115+
/// \see boost::python::class_::def_maybe_overloads(char const* name, Fn fn,
116+
/// A1 const& a1, ...)
117+
template <class Fn, class A1>
118+
void def_maybe_overloads(char const* name, Fn fn, A1 const& a1, ...) {
119+
def_impl(bp::detail::unwrap_wrapper((W*)0), name, fn,
120+
bp::detail::def_helper<A1>(a1), &fn);
121+
}
122+
123+
private:
124+
bp::object m_object;
125+
};
126+
127+
} // namespace eigenpy
128+
129+
#endif // ifndef __eigenpy_registration_class_hpp__

include/eigenpy/std-vector.hpp

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -360,14 +360,40 @@ struct EmptyPythonVisitor
360360
void visit(classT &) const {}
361361
};
362362

363+
/// \brief Add standard method to a std::vector.
364+
template <typename Container, bool NoProxy = false>
365+
struct add_std_method_to_std_vector
366+
: public boost::python::def_visitor<
367+
add_std_method_to_std_vector<Container> > {
368+
typedef typename Container::value_type value_type;
369+
typedef typename Container::value_type data_type;
370+
typedef size_t index_type;
371+
372+
typedef StdContainerFromPythonList<Container, NoProxy>
373+
FromPythonListConverter;
374+
375+
template <class Class>
376+
void visit(Class &cl) const {
377+
details::overload_base_get_item_for_std_vector<Container> get_item_visitor;
378+
cl.def("tolist", &FromPythonListConverter::tolist, bp::arg("self"),
379+
"Returns the std::vector as a Python list.")
380+
.def(get_item_visitor)
381+
.def("reserve", &Container::reserve,
382+
(bp::arg("self"), bp::arg("new_cap")),
383+
"Increase the capacity of the vector to a value that's greater "
384+
"or equal to new_cap.")
385+
.def(CopyableVisitor<Container>());
386+
}
387+
};
388+
363389
///
364390
/// \brief Expose an std::vector from a type given as template argument.
365391
///
366392
/// \tparam T Type to expose as std::vector<T>.
367393
/// \tparam Allocator Type for the Allocator in std::vector<T,Allocator>.
368-
/// \tparam NoProxy When set to false, the elements will be copied when returned
369-
/// to Python. \tparam EnableFromPythonListConverter Enables the conversion from
370-
/// a Python list to a std::vector<T,Allocator>
394+
/// \tparam NoProxy When set to false, the elements will be copied when
395+
/// returned to Python. \tparam EnableFromPythonListConverter Enables the
396+
/// conversion from a Python list to a std::vector<T,Allocator>
371397
///
372398
/// \sa StdAlignedVectorPythonVisitor
373399
///
@@ -388,18 +414,15 @@ struct StdVectorPythonVisitor
388414
expose(class_name, doc_string, EmptyPythonVisitor());
389415
}
390416

391-
template <typename VisitorDerived>
392-
static void expose(
393-
const std::string &class_name,
394-
const boost::python::def_visitor<VisitorDerived> &visitor) {
417+
template <typename Visitor>
418+
static void expose(const std::string &class_name, const Visitor &visitor) {
395419
expose(class_name, "", visitor);
396420
}
397421

398-
template <typename VisitorDerived>
399-
static void expose(
400-
const std::string &class_name, const std::string &doc_string,
401-
const boost::python::def_visitor<VisitorDerived> &visitor) {
402-
if (!register_symbolic_link_to_registered_type<vector_type>()) {
422+
template <typename Visitor>
423+
static void expose(const std::string &class_name,
424+
const std::string &doc_string, const Visitor &visitor) {
425+
if (!register_symbolic_link_to_registered_type<vector_type>(visitor)) {
403426
bp::class_<vector_type> cl(class_name.c_str(), doc_string.c_str());
404427
cl.def(StdVectorPythonVisitor())
405428

@@ -409,20 +432,11 @@ struct StdVectorPythonVisitor
409432
.def(bp::init<const vector_type &>(bp::args("self", "other"),
410433
"Copy constructor"))
411434

412-
.def("tolist", &FromPythonListConverter::tolist, bp::arg("self"),
413-
"Returns the std::vector as a Python list.")
414435
.def(visitor)
415-
.def("reserve", &vector_type::reserve,
416-
(bp::arg("self"), bp::arg("new_cap")),
417-
"Increase the capacity of the vector to a value that's greater "
418-
"or equal to new_cap.")
419-
.def_pickle(PickleVector<vector_type>())
420-
.def(CopyableVisitor<vector_type>());
421-
422-
// Register conversion
423-
if (EnableFromPythonListConverter)
424-
FromPythonListConverter::register_converter();
436+
.def_pickle(PickleVector<vector_type>());
425437
}
438+
// Register conversion
439+
FromPythonListConverter::register_converter();
426440
}
427441
};
428442

@@ -437,8 +451,7 @@ void exposeStdVectorEigenSpecificType(const char *name) {
437451
std::string full_name = "StdVec_";
438452
full_name += name;
439453
StdVectorPythonVisitor<VecMatType, false>::expose(
440-
full_name.c_str(),
441-
details::overload_base_get_item_for_std_vector<VecMatType>());
454+
full_name.c_str(), add_std_method_to_std_vector<VecMatType, false>());
442455
}
443456

444457
} // namespace eigenpy

unittest/python/test_std_vector.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
l3.append(np.eye(2))
1515
l4 = [np.random.randn(3, 3).T for _ in range(3)]
1616
l4[-1] = l4[-1].T
17+
l5 = [np.random.randn(2, 2).T for _ in range(3)]
1718

1819

1920
def checkAllValues(li1, li2):
@@ -83,3 +84,12 @@ def checkZero(l):
8384
# vector.setZero(l4)
8485
# pprint.pprint(list(l4))
8586
# checkZero(l4)
87+
88+
# TODO fail
89+
l5_copy = std_vector.StdVec_Mat2d(l5)
90+
91+
# test l5 == l5_copy == l5_py
92+
l5_py = l5_copy.tolist()
93+
# Test l5[0] is zero
94+
l5[0].setZero()
95+
# TODO test

unittest/std_vector.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,12 @@ BOOST_PYTHON_MODULE(std_vector) {
4949
typedef Eigen::Ref<Eigen::MatrixXd> RefXd;
5050
StdVectorPythonVisitor<std::vector<RefXd>, true>::expose("StdVec_MatRef");
5151
bp::def("setZero", setZero<Eigen::MatrixXd>, "Sets the coeffs to 0.");
52+
53+
// Test matrix modification
54+
// Mat2d don't have tolist, reserve, mutable __getitem__ and from list
55+
// conversion exposeStdVectorEigenSpecificType must add those methods to Mat2d
56+
bp::class_<std::vector<Eigen::Matrix2d> >("StdVec_Mat2d")
57+
.def(boost::python::vector_indexing_suite<
58+
std::vector<Eigen::Matrix2d> >());
59+
exposeStdVectorEigenSpecificType<Eigen::Matrix3d>("Mat2d");
5260
}

0 commit comments

Comments
 (0)