| 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 | ✗ | static void register_converter() { | |
| 57 | ✗ | bp::converter::registry::push_back(&set_to_set::convertible, | |
| 58 | &set_to_set::construct, | ||
| 59 | bp::type_id<Container>()); | ||
| 60 | ✗ | } | |
| 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 | ✗ | static void expose(const std::string& class_name, | |
| 136 | const std::string& doc_string = "") { | ||
| 137 | ✗ | bp::class_<Container>(class_name.c_str(), doc_string.c_str()) | |
| 138 | ✗ | .def(StdSetPythonVisitor()) | |
| 139 | ✗ | .def("toset", &FromPythonSetConverter::toset, bp::arg("self"), | |
| 140 | "Returns the std::set as a Python set.") | ||
| 141 | ✗ | .def_pickle(PickleSet<Container>()); | |
| 142 | // Register conversion | ||
| 143 | ✗ | FromPythonSetConverter::register_converter(); | |
| 144 | ✗ | } | |
| 145 | }; | ||
| 146 | |||
| 147 | } // namespace python | ||
| 148 | } // namespace crocoddyl | ||
| 149 | |||
| 150 | #endif // BINDINGS_PYTHON_CROCODDYL_UTILS_SET_CONVERTER_HPP_ | ||
| 151 |