GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bindings/python/crocoddyl/utils/set-converter.hpp Lines: 8 38 21.1 %
Date: 2024-02-13 11:12:33 Branches: 4 68 5.9 %

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
    bp::class_<Container>(class_name.c_str(), doc_string.c_str())
138
        .def(StdSetPythonVisitor())
139
10
        .def("toset", &FromPythonSetConverter::toset, bp::arg("self"),
140
             "Returns the std::set as a Python set.")
141

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_