GCC Code Coverage Report


Directory: ./
File: bindings/python/crocoddyl/utils/map-converter.hpp
Date: 2025-02-24 23:41:29
Exec Total Coverage
Lines: 10 46 21.7%
Branches: 4 76 5.3%

Line Branch Exec Source
1 ///////////////////////////////////////////////////////////////////////////////
2 // BSD 3-Clause License
3 //
4 // Copyright (C) 2019-2022, LAAS-CNRS, University of Edinburgh, INRIA
5 // Copyright note valid unless otherwise stated in individual files.
6 // All rights reserved.
7 ///////////////////////////////////////////////////////////////////////////////
8
9 #ifndef BINDINGS_PYTHON_CROCODDYL_UTILS_MAP_CONVERTER_HPP_
10 #define BINDINGS_PYTHON_CROCODDYL_UTILS_MAP_CONVERTER_HPP_
11
12 #include <boost/python/stl_iterator.hpp>
13 #include <boost/python/suite/indexing/map_indexing_suite.hpp>
14 #include <boost/python/to_python_converter.hpp>
15 #include <map>
16
17 #include "python/crocoddyl/utils/vector-converter.hpp"
18
19 namespace crocoddyl {
20 namespace python {
21
22 namespace bp = boost::python;
23
24 /**
25 * @brief Create a pickle interface for the std::map
26 *
27 * @param[in] Container Map type to be pickled
28 * \sa Pickle
29 */
30 template <typename Container>
31 struct PickleMap : public eigenpy::PickleVector<Container> {
32 static void setstate(bp::object op, bp::tuple tup) {
33 Container& o = bp::extract<Container&>(op)();
34 bp::stl_input_iterator<typename Container::value_type> begin(tup[0]), end;
35 o.insert(begin, end);
36 }
37 };
38
39 /// Conversion from dict to map solution proposed in
40 /// https://stackoverflow.com/questions/6116345/boostpython-possible-to-automatically-convert-from-dict-stdmap
41 /// This template encapsulates the conversion machinery.
42 template <typename Container>
43 struct dict_to_map {
44 160 static void register_converter() {
45 160 bp::converter::registry::push_back(&dict_to_map::convertible,
46 &dict_to_map::construct,
47 bp::type_id<Container>());
48 160 }
49
50 /// Check if conversion is possible
51 static void* convertible(PyObject* object) {
52 // Check if it is a list
53 if (!PyObject_GetIter(object)) return 0;
54 return object;
55 }
56
57 /// Perform the conversion
58 static void construct(PyObject* object,
59 bp::converter::rvalue_from_python_stage1_data* data) {
60 // convert the PyObject pointed to by `object` to a bp::dict
61 bp::handle<> handle(bp::borrowed(object)); // "smart ptr"
62 bp::dict dict(handle);
63
64 // get a pointer to memory into which we construct the map
65 // this is provided by the Python runtime
66 typedef bp::converter::rvalue_from_python_storage<Container> storage_type;
67 void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes;
68
69 // placement-new allocate the result
70 new (storage) Container();
71
72 // iterate over the dictionary `dict`, fill up the map `map`
73 Container& map(*(static_cast<Container*>(storage)));
74 bp::list keys(dict.keys());
75 int keycount(static_cast<int>(bp::len(keys)));
76 for (int i = 0; i < keycount; ++i) {
77 // get the key
78 bp::object keyobj(keys[i]);
79 bp::extract<typename Container::key_type> keyproxy(keyobj);
80 if (!keyproxy.check()) {
81 PyErr_SetString(PyExc_KeyError, "Bad key type");
82 bp::throw_error_already_set();
83 }
84 typename Container::key_type key = keyproxy();
85
86 // get the corresponding value
87 bp::object valobj(dict[keyobj]);
88 bp::extract<typename Container::mapped_type> valproxy(valobj);
89 if (!valproxy.check()) {
90 PyErr_SetString(PyExc_ValueError, "Bad value type");
91 bp::throw_error_already_set();
92 }
93 typename Container::mapped_type val = valproxy();
94 map[key] = val;
95 }
96
97 // remember the location for later
98 data->convertible = storage;
99 }
100
101 static bp::dict todict(Container& self) {
102 bp::dict dict;
103 typename Container::const_iterator it;
104 for (it = self.begin(); it != self.end(); ++it) {
105 dict.setdefault(it->first, it->second);
106 }
107 return dict;
108 }
109 };
110
111 /**
112 * @brief Expose an std::map from a type given as template argument.
113 *
114 * @param[in] T Type to expose as std::map<T>.
115 * @param[in] Compare Type for the Compare in std::map<T,Compare,Allocator>.
116 * @param[in] Allocator Type for the Allocator in
117 * std::map<T,Compare,Allocator>.
118 * @param[in] NoProxy When set to false, the elements will be copied when
119 * returned to Python.
120 */
121 template <class Key, class T, class Compare = std::less<Key>,
122 class Allocator = std::allocator<std::pair<const Key, T> >,
123 bool NoProxy = false>
124 struct StdMapPythonVisitor
125 : public bp::map_indexing_suite<
126 typename std::map<Key, T, Compare, Allocator>, NoProxy>,
127 public dict_to_map<std::map<Key, T, Compare, Allocator> > {
128 typedef std::map<Key, T, Compare, Allocator> Container;
129 typedef dict_to_map<Container> FromPythonDictConverter;
130
131 160 static void expose(const std::string& class_name,
132 const std::string& doc_string = "") {
133 namespace bp = bp;
134
135 160 bp::class_<Container>(class_name.c_str(), doc_string.c_str())
136
1/2
✓ Branch 1 taken 80 times.
✗ Branch 2 not taken.
160 .def(StdMapPythonVisitor())
137
2/4
✓ Branch 1 taken 80 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 80 times.
✗ Branch 5 not taken.
320 .def("todict", &FromPythonDictConverter::todict, bp::arg("self"),
138 "Returns the std::map as a Python dictionary.")
139
1/2
✓ Branch 1 taken 80 times.
✗ Branch 2 not taken.
160 .def_pickle(PickleMap<Container>());
140 // Register conversion
141 160 FromPythonDictConverter::register_converter();
142 160 }
143 };
144
145 } // namespace python
146 } // namespace crocoddyl
147
148 #endif // BINDINGS_PYTHON_CROCODDYL_UTILS_MAP_CONVERTER_HPP_
149