GCC Code Coverage Report


Directory: ./
File: bindings/python/crocoddyl/utils/set-converter.hpp
Date: 2025-01-16 08:47:40
Exec Total Coverage
Lines: 10 40 25.0%
Branches: 4 70 5.7%

Line Branch Exec Source
1 ///////////////////////////////////////////////////////////////////////////////
2 // BSD 3-Clause License
3 //
4 // Copyright (C) 2019-2022, LAAS-CNRS, University of Edinburgh, INRIA
5 // University of Oxford
6 // Copyright note valid unless otherwise stated in individual files.
7 // All rights reserved.
8 ///////////////////////////////////////////////////////////////////////////////
9
10 #ifndef BINDINGS_PYTHON_CROCODDYL_UTILS_SET_CONVERTER_HPP_
11 #define BINDINGS_PYTHON_CROCODDYL_UTILS_SET_CONVERTER_HPP_
12
13 #include <boost/python/stl_iterator.hpp>
14 #include <boost/python/to_python_converter.hpp>
15 #include <set>
16
17 #include "set_indexing_suite.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::set
26 *
27 * @param[in] Container Set type to be pickled
28 * \sa Pickle
29 */
30 template <typename Container>
31 struct PickleSet : bp::pickle_suite {
32 static bp::tuple getinitargs(const Container&) { return bp::make_tuple(); }
33
34 static bp::tuple getstate(bp::object op) {
35 bp::list list;
36 const Container& ret = bp::extract<const Container&>(op);
37 for (const auto& it : ret) {
38 list.append(it);
39 }
40 return bp::make_tuple(list);
41 }
42
43 static void setstate(bp::object op, bp::tuple tup) {
44 Container& o = bp::extract<Container&>(op)();
45 bp::stl_input_iterator<typename Container::value_type> begin(tup[0]), end;
46 o.insert(begin, end);
47 }
48 };
49
50 /** @brief Type that allows for registration of conversions from python iterable
51 * types. */
52 template <typename Container>
53 struct set_to_set {
54 /** @note Registers converter from a python iterable type to the provided
55 * type. */
56 10 static void register_converter() {
57 10 bp::converter::registry::push_back(&set_to_set::convertible,
58 &set_to_set::construct,
59 bp::type_id<Container>());
60 10 }
61
62 /** @brief Check if PyObject is iterable. */
63 static void* convertible(PyObject* object) {
64 // Check if it is a set
65 if (!PySet_Check(object)) return 0;
66
67 PyObject* iter = PyObject_GetIter(object);
68 PyObject* item;
69 while ((item = PyIter_Next(iter))) {
70 bp::extract<typename Container::value_type> elt(iter);
71 // Py_DECREF(item);
72 if (!elt.check()) return 0;
73 }
74 Py_DECREF(iter);
75
76 return object;
77 }
78
79 /** @brief Convert iterable PyObject to C++ container type.
80 *
81 * Container Concept requirements:
82 * * Container::value_type is CopyConstructable.
83 */
84 static void construct(PyObject* object,
85 bp::converter::rvalue_from_python_stage1_data* data) {
86 // Object is a borrowed reference, so create a handle indicting it is
87 // borrowed for proper reference counting.
88 bp::handle<> handle(bp::borrowed(object));
89
90 // Obtain a handle to the memory block that the converter has allocated
91 // for the C++ type.
92 typedef bp::converter::rvalue_from_python_storage<Container> storage_type;
93 void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes;
94
95 typedef bp::stl_input_iterator<typename Container::value_type> iterator;
96
97 // Allocate the C++ type into the converter's memory block, and assign
98 // its handle to the converter's convertible variable. The C++
99 // container is populated by passing the begin and end iterators of
100 // the python object to the container's constructor.
101 new (storage) Container(iterator(bp::object(handle)), // begin
102 iterator()); // end
103 data->convertible = storage;
104 }
105
106 static bp::object toset(Container& self) {
107 PyObject* set = PySet_New(NULL);
108 for (auto it = self.begin(); it != self.end(); ++it) {
109 PySet_Add(set, bp::object(*it).ptr());
110 }
111 return bp::object(bp::handle<>(set));
112 }
113 };
114
115 /**
116 * @brief Expose an std::set from a type given as template argument.
117 *
118 * @param[in] T Type to expose as std::set<T>.
119 * @param[in] Compare Type for the comparison function in
120 * std::set<T,Compare,Allocator>.
121 * @param[in] Allocator Type for the Allocator in
122 * std::set<T,Compare,Allocator>.
123 * @param[in] NoProxy When set to false, the elements will be copied when
124 * returned to Python.
125 */
126 template <class T, class Compare = std::less<T>,
127 class Allocator = std::allocator<T>, bool NoProxy = false>
128 struct StdSetPythonVisitor
129 : public set_indexing_suite<typename std::set<T, Compare, Allocator>,
130 NoProxy>,
131 set_to_set<std::set<T, Compare, Allocator>> {
132 typedef std::set<T, Compare, Allocator> Container;
133 typedef set_to_set<Container> FromPythonSetConverter;
134
135 10 static void expose(const std::string& class_name,
136 const std::string& doc_string = "") {
137 10 bp::class_<Container>(class_name.c_str(), doc_string.c_str())
138
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 .def(StdSetPythonVisitor())
139
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
20 .def("toset", &FromPythonSetConverter::toset, bp::arg("self"),
140 "Returns the std::set as a Python set.")
141
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 .def_pickle(PickleSet<Container>());
142 // Register conversion
143 10 FromPythonSetConverter::register_converter();
144 10 }
145 };
146
147 } // namespace python
148 } // namespace crocoddyl
149
150 #endif // BINDINGS_PYTHON_CROCODDYL_UTILS_SET_CONVERTER_HPP_
151