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 |
|
|
bp::class_<Container>(class_name.c_str(), doc_string.c_str()) |
136 |
|
|
.def(StdMapPythonVisitor()) |
137 |
✓✗ |
160 |
.def("todict", &FromPythonDictConverter::todict, bp::arg("self"), |
138 |
|
|
"Returns the std::map as a Python dictionary.") |
139 |
✓✗✓✗ ✓✗ |
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_ |