77#define __eigenpy_utils_map_hpp__
88
99#include < boost/python/suite/indexing/map_indexing_suite.hpp>
10+ #include " eigenpy/pickle-vector.hpp"
11+ #include < boost/python/stl_iterator.hpp>
12+ #include < boost/python/suite/indexing/map_indexing_suite.hpp>
13+ #include < boost/python/to_python_converter.hpp>
14+ #include < map>
1015
1116namespace eigenpy {
1217namespace details {
@@ -31,31 +36,165 @@ struct overload_base_get_item_for_std_map
3136 typename Container::iterator i = container.get ().find (idx);
3237 if (i == container.get ().end ()) {
3338 PyErr_SetString (PyExc_KeyError, " Invalid key" );
34- bp ::throw_error_already_set ();
39+ boost::python ::throw_error_already_set ();
3540 }
3641
37- typename bp:: to_python_indirect<data_type&,
38- bp ::detail::make_reference_holder>
42+ typename boost::python:: to_python_indirect<
43+ data_type&, boost::python ::detail::make_reference_holder>
3944 convert;
40- return bp:: object (bp ::handle<>(convert (i->second )));
45+ return boost::python:: object (boost::python ::handle<>(convert (i->second )));
4146 }
4247
4348 static index_type convert_index (Container& /* container*/ , PyObject* i_) {
44- bp ::extract<key_type const &> i (i_);
49+ boost::python ::extract<key_type const &> i (i_);
4550 if (i.check ()) {
4651 return i ();
4752 } else {
48- bp ::extract<key_type> i (i_);
53+ boost::python ::extract<key_type> i (i_);
4954 if (i.check ()) return i ();
5055 }
5156
5257 PyErr_SetString (PyExc_TypeError, " Invalid index type" );
53- bp ::throw_error_already_set ();
58+ boost::python ::throw_error_already_set ();
5459 return index_type ();
5560 }
5661};
5762
5863} // namespace details
64+
65+ // /////////////////////////////////////////////////////////////////////////////
66+ // The following snippet of code has been taken from the header
67+ // https://github.com/loco-3d/crocoddyl/blob/v2.1.0/bindings/python/crocoddyl/utils/map-converter.hpp
68+ // The Crocoddyl library is written by Carlos Mastalli, Nicolas Mansard and
69+ // Rohan Budhiraja.
70+ // /////////////////////////////////////////////////////////////////////////////
71+
72+ namespace python {
73+
74+ namespace bp = boost::python;
75+
76+ /* *
77+ * @brief Create a pickle interface for the std::map
78+ *
79+ * @param[in] Container Map type to be pickled
80+ * \sa Pickle
81+ */
82+ template <typename Container>
83+ struct PickleMap : public PickleVector <Container> {
84+ static void setstate (bp::object op, bp::tuple tup) {
85+ Container& o = bp::extract<Container&>(op)();
86+ bp::stl_input_iterator<typename Container::value_type> begin (tup[0 ]), end;
87+ o.insert (begin, end);
88+ }
89+ };
90+
91+ // / Conversion from dict to map solution proposed in
92+ // / https://stackoverflow.com/questions/6116345/boostpython-possible-to-automatically-convert-from-dict-stdmap
93+ // / This template encapsulates the conversion machinery.
94+ template <typename Container>
95+ struct dict_to_map {
96+ static void register_converter () {
97+ bp::converter::registry::push_back (&dict_to_map::convertible,
98+ &dict_to_map::construct,
99+ bp::type_id<Container>());
100+ }
101+
102+ // / Check if conversion is possible
103+ static void * convertible (PyObject* object) {
104+ // Check if it is a list
105+ if (!PyObject_GetIter (object)) return 0 ;
106+ return object;
107+ }
108+
109+ // / Perform the conversion
110+ static void construct (PyObject* object,
111+ bp::converter::rvalue_from_python_stage1_data* data) {
112+ // convert the PyObject pointed to by `object` to a bp::dict
113+ bp::handle<> handle (bp::borrowed (object)); // "smart ptr"
114+ bp::dict dict (handle);
115+
116+ // get a pointer to memory into which we construct the map
117+ // this is provided by the Python runtime
118+ typedef bp::converter::rvalue_from_python_storage<Container> storage_type;
119+ void * storage = reinterpret_cast <storage_type*>(data)->storage .bytes ;
120+
121+ // placement-new allocate the result
122+ new (storage) Container ();
123+
124+ // iterate over the dictionary `dict`, fill up the map `map`
125+ Container& map (*(static_cast <Container*>(storage)));
126+ bp::list keys (dict.keys ());
127+ int keycount (static_cast <int >(bp::len (keys)));
128+ for (int i = 0 ; i < keycount; ++i) {
129+ // get the key
130+ bp::object keyobj (keys[i]);
131+ bp::extract<typename Container::key_type> keyproxy (keyobj);
132+ if (!keyproxy.check ()) {
133+ PyErr_SetString (PyExc_KeyError, " Bad key type" );
134+ bp::throw_error_already_set ();
135+ }
136+ typename Container::key_type key = keyproxy ();
137+
138+ // get the corresponding value
139+ bp::object valobj (dict[keyobj]);
140+ bp::extract<typename Container::mapped_type> valproxy (valobj);
141+ if (!valproxy.check ()) {
142+ PyErr_SetString (PyExc_ValueError, " Bad value type" );
143+ bp::throw_error_already_set ();
144+ }
145+ typename Container::mapped_type val = valproxy ();
146+ map[key] = val;
147+ }
148+
149+ // remember the location for later
150+ data->convertible = storage;
151+ }
152+
153+ static bp::dict todict (Container& self) {
154+ bp::dict dict;
155+ typename Container::const_iterator it;
156+ for (it = self.begin (); it != self.end (); ++it) {
157+ dict.setdefault (it->first , it->second );
158+ }
159+ return dict;
160+ }
161+ };
162+
163+ /* *
164+ * @brief Expose an std::map from a type given as template argument.
165+ *
166+ * @param[in] T Type to expose as std::map<T>.
167+ * @param[in] Compare Type for the Compare in std::map<T,Compare,Allocator>.
168+ * @param[in] Allocator Type for the Allocator in
169+ * std::map<T,Compare,Allocator>.
170+ * @param[in] NoProxy When set to false, the elements will be copied when
171+ * returned to Python.
172+ */
173+ template <class Key , class T , class Compare = std::less<Key>,
174+ class Allocator = std::allocator<std::pair<const Key, T> >,
175+ bool NoProxy = false >
176+ struct StdMapPythonVisitor
177+ : public bp::map_indexing_suite<
178+ typename std::map<Key, T, Compare, Allocator>, NoProxy>,
179+ public dict_to_map<std::map<Key, T, Compare, Allocator> > {
180+ typedef std::map<Key, T, Compare, Allocator> Container;
181+ typedef dict_to_map<Container> FromPythonDictConverter;
182+
183+ static void expose (const std::string& class_name,
184+ const std::string& doc_string = " " ) {
185+ namespace bp = bp;
186+
187+ bp::class_<Container>(class_name.c_str (), doc_string.c_str ())
188+ .def (StdMapPythonVisitor ())
189+ .def (" todict" , &FromPythonDictConverter::todict, bp::arg (" self" ),
190+ " Returns the std::map as a Python dictionary." )
191+ .def_pickle (PickleMap<Container>());
192+ // Register conversion
193+ FromPythonDictConverter::register_converter ();
194+ }
195+ };
196+
197+ } // namespace python
59198} // namespace eigenpy
60199
61200#endif // ifndef __eigenpy_utils_map_hpp__
0 commit comments