test_sequences_and_iterators.cpp
Go to the documentation of this file.
1 /*
2  tests/test_sequences_and_iterators.cpp -- supporting Pythons' sequence protocol, iterators,
3  etc.
4 
5  Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
6 
7  All rights reserved. Use of this source code is governed by a
8  BSD-style license that can be found in the LICENSE file.
9 */
10 
11 #include <pybind11/operators.h>
12 #include <pybind11/stl.h>
13 
14 #include "constructor_stats.h"
15 #include "pybind11_tests.h"
16 
17 #include <algorithm>
18 #include <utility>
19 #include <vector>
20 
21 #ifdef PYBIND11_HAS_OPTIONAL
22 # include <optional>
23 #endif // PYBIND11_HAS_OPTIONAL
24 
25 template <typename T>
27  const T *ptr_;
28 
29 public:
30  explicit NonZeroIterator(const T *ptr) : ptr_(ptr) {}
31  const T &operator*() const { return *ptr_; }
33  ++ptr_;
34  return *this;
35  }
36 };
37 
38 class NonZeroSentinel {};
39 
40 template <typename A, typename B>
41 bool operator==(const NonZeroIterator<std::pair<A, B>> &it, const NonZeroSentinel &) {
42  return !(*it).first || !(*it).second;
43 }
44 
45 /* Iterator where dereferencing returns prvalues instead of references. */
46 template <typename T>
48  const T *ptr_;
49 
50 public:
51  explicit NonRefIterator(const T *ptr) : ptr_(ptr) {}
52  T operator*() const { return T(*ptr_); }
54  ++ptr_;
55  return *this;
56  }
57  bool operator==(const NonRefIterator &other) const { return ptr_ == other.ptr_; }
58 };
59 
61 public:
62  explicit NonCopyableInt(int value) : value_(value) {}
63  NonCopyableInt(const NonCopyableInt &) = delete;
64  NonCopyableInt(NonCopyableInt &&other) noexcept : value_(other.value_) {
65  other.value_ = -1; // detect when an unwanted move occurs
66  }
67  NonCopyableInt &operator=(const NonCopyableInt &) = delete;
69  value_ = other.value_;
70  other.value_ = -1; // detect when an unwanted move occurs
71  return *this;
72  }
73  int get() const { return value_; }
74  void set(int value) { value_ = value; }
75  ~NonCopyableInt() = default;
76 
77 private:
78  int value_;
79 };
80 using NonCopyableIntPair = std::pair<NonCopyableInt, NonCopyableInt>;
81 PYBIND11_MAKE_OPAQUE(std::vector<NonCopyableInt>);
82 PYBIND11_MAKE_OPAQUE(std::vector<NonCopyableIntPair>);
83 
84 template <typename PythonType>
85 py::list test_random_access_iterator(PythonType x) {
86  if (x.size() < 5) {
87  throw py::value_error("Please provide at least 5 elements for testing.");
88  }
89 
90  auto checks = py::list();
91  auto assert_equal = [&checks](py::handle a, py::handle b) {
92  auto result = PyObject_RichCompareBool(a.ptr(), b.ptr(), Py_EQ);
93  if (result == -1) {
94  throw py::error_already_set();
95  }
96  checks.append(result != 0);
97  };
98 
99  auto it = x.begin();
100  assert_equal(x[0], *it);
101  assert_equal(x[0], it[0]);
102  assert_equal(x[1], it[1]);
103 
104  assert_equal(x[1], *(++it));
105  assert_equal(x[1], *(it++));
106  assert_equal(x[2], *it);
107  assert_equal(x[3], *(it += 1));
108  assert_equal(x[2], *(--it));
109  assert_equal(x[2], *(it--));
110  assert_equal(x[1], *it);
111  assert_equal(x[0], *(it -= 1));
112 
113  assert_equal(it->attr("real"), x[0].attr("real"));
114  assert_equal((it + 1)->attr("real"), x[1].attr("real"));
115 
116  assert_equal(x[1], *(it + 1));
117  assert_equal(x[1], *(1 + it));
118  it += 3;
119  assert_equal(x[1], *(it - 2));
120 
121  checks.append(static_cast<std::size_t>(x.end() - x.begin()) == x.size());
122  checks.append((x.begin() + static_cast<std::ptrdiff_t>(x.size())) == x.end());
123  checks.append(x.begin() < x.end());
124 
125  return checks;
126 }
127 
128 TEST_SUBMODULE(sequences_and_iterators, m) {
129  // test_sliceable
130  class Sliceable {
131  public:
132  explicit Sliceable(int n) : size(n) {}
133  int start, stop, step;
134  int size;
135  };
136  py::class_<Sliceable>(m, "Sliceable")
137  .def(py::init<int>())
138  .def("__getitem__", [](const Sliceable &s, const py::slice &slice) {
139  py::ssize_t start = 0, stop = 0, step = 0, slicelength = 0;
140  if (!slice.compute(s.size, &start, &stop, &step, &slicelength)) {
141  throw py::error_already_set();
142  }
143  int istart = static_cast<int>(start);
144  int istop = static_cast<int>(stop);
145  int istep = static_cast<int>(step);
146  return std::make_tuple(istart, istop, istep);
147  });
148 
149  m.def("make_forward_slice_size_t", []() { return py::slice(0, -1, 1); });
150  m.def("make_reversed_slice_object",
151  []() { return py::slice(py::none(), py::none(), py::int_(-1)); });
152 #ifdef PYBIND11_HAS_OPTIONAL
153  m.attr("has_optional") = true;
154  m.def("make_reversed_slice_size_t_optional_verbose",
155  []() { return py::slice(std::nullopt, std::nullopt, -1); });
156  // Warning: The following spelling may still compile if optional<> is not present and give
157  // wrong answers. Please use with caution.
158  m.def("make_reversed_slice_size_t_optional", []() { return py::slice({}, {}, -1); });
159 #else
160  m.attr("has_optional") = false;
161 #endif
162 
163  // test_sequence
164  class Sequence {
165  public:
166  explicit Sequence(size_t size) : m_size(size) {
167  print_created(this, "of size", m_size);
168  // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
169  m_data = new float[size];
170  memset(m_data, 0, sizeof(float) * size);
171  }
172  explicit Sequence(const std::vector<float> &value) : m_size(value.size()) {
173  print_created(this, "of size", m_size, "from std::vector");
174  // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
175  m_data = new float[m_size];
176  memcpy(m_data, &value[0], sizeof(float) * m_size);
177  }
178  Sequence(const Sequence &s) : m_size(s.m_size) {
179  print_copy_created(this);
180  // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
181  m_data = new float[m_size];
182  memcpy(m_data, s.m_data, sizeof(float) * m_size);
183  }
184  Sequence(Sequence &&s) noexcept : m_size(s.m_size), m_data(s.m_data) {
185  print_move_created(this);
186  s.m_size = 0;
187  s.m_data = nullptr;
188  }
189 
190  ~Sequence() {
191  print_destroyed(this);
192  delete[] m_data;
193  }
194 
195  Sequence &operator=(const Sequence &s) {
196  if (&s != this) {
197  delete[] m_data;
198  m_size = s.m_size;
199  m_data = new float[m_size];
200  memcpy(m_data, s.m_data, sizeof(float) * m_size);
201  }
202  print_copy_assigned(this);
203  return *this;
204  }
205 
206  Sequence &operator=(Sequence &&s) noexcept {
207  if (&s != this) {
208  delete[] m_data;
209  m_size = s.m_size;
210  m_data = s.m_data;
211  s.m_size = 0;
212  s.m_data = nullptr;
213  }
214  print_move_assigned(this);
215  return *this;
216  }
217 
218  bool operator==(const Sequence &s) const {
219  if (m_size != s.size()) {
220  return false;
221  }
222  for (size_t i = 0; i < m_size; ++i) {
223  if (m_data[i] != s[i]) {
224  return false;
225  }
226  }
227  return true;
228  }
229  bool operator!=(const Sequence &s) const { return !operator==(s); }
230 
231  float operator[](size_t index) const { return m_data[index]; }
232  float &operator[](size_t index) { return m_data[index]; }
233 
234  bool contains(float v) const {
235  for (size_t i = 0; i < m_size; ++i) {
236  if (v == m_data[i]) {
237  return true;
238  }
239  }
240  return false;
241  }
242 
243  Sequence reversed() const {
244  Sequence result(m_size);
245  for (size_t i = 0; i < m_size; ++i) {
246  result[m_size - i - 1] = m_data[i];
247  }
248  return result;
249  }
250 
251  size_t size() const { return m_size; }
252 
253  const float *begin() const { return m_data; }
254  const float *end() const { return m_data + m_size; }
255 
256  private:
257  size_t m_size;
258  float *m_data;
259  };
260  py::class_<Sequence>(m, "Sequence")
261  .def(py::init<size_t>())
262  .def(py::init<const std::vector<float> &>())
264  .def("__getitem__",
265  [](const Sequence &s, size_t i) {
266  if (i >= s.size()) {
267  throw py::index_error();
268  }
269  return s[i];
270  })
271  .def("__setitem__",
272  [](Sequence &s, size_t i, float v) {
273  if (i >= s.size()) {
274  throw py::index_error();
275  }
276  s[i] = v;
277  })
278  .def("__len__", &Sequence::size)
280  .def(
281  "__iter__",
282  [](const Sequence &s) { return py::make_iterator(s.begin(), s.end()); },
283  py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */)
284  .def("__contains__", [](const Sequence &s, float v) { return s.contains(v); })
285  .def("__reversed__", [](const Sequence &s) -> Sequence { return s.reversed(); })
287  .def("__getitem__",
288  [](const Sequence &s, const py::slice &slice) -> Sequence * {
289  size_t start = 0, stop = 0, step = 0, slicelength = 0;
290  if (!slice.compute(s.size(), &start, &stop, &step, &slicelength)) {
291  throw py::error_already_set();
292  }
293  auto *seq = new Sequence(slicelength);
294  for (size_t i = 0; i < slicelength; ++i) {
295  (*seq)[i] = s[start];
296  start += step;
297  }
298  return seq;
299  })
300  .def("__setitem__",
301  [](Sequence &s, const py::slice &slice, const Sequence &value) {
302  size_t start = 0, stop = 0, step = 0, slicelength = 0;
303  if (!slice.compute(s.size(), &start, &stop, &step, &slicelength)) {
304  throw py::error_already_set();
305  }
306  if (slicelength != value.size()) {
307  throw std::runtime_error(
308  "Left and right hand size of slice assignment have different sizes!");
309  }
310  for (size_t i = 0; i < slicelength; ++i) {
311  s[start] = value[i];
312  start += step;
313  }
314  })
316  .def(py::self == py::self)
317  .def(py::self != py::self)
318  // Could also define py::self + py::self for concatenation, etc.
319  ;
320 
321  // test_map_iterator
322  // Interface of a map-like object that isn't (directly) an unordered_map, but provides some
323  // basic map-like functionality.
324  class StringMap {
325  public:
326  StringMap() = default;
327  explicit StringMap(std::unordered_map<std::string, std::string> init)
328  : map(std::move(init)) {}
329 
330  void set(const std::string &key, std::string val) { map[key] = std::move(val); }
331  std::string get(const std::string &key) const { return map.at(key); }
332  size_t size() const { return map.size(); }
333 
334  private:
335  std::unordered_map<std::string, std::string> map;
336 
337  public:
338  decltype(map.cbegin()) begin() const { return map.cbegin(); }
339  decltype(map.cend()) end() const { return map.cend(); }
340  };
341  py::class_<StringMap>(m, "StringMap")
342  .def(py::init<>())
343  .def(py::init<std::unordered_map<std::string, std::string>>())
344  .def("__getitem__",
345  [](const StringMap &map, const std::string &key) {
346  try {
347  return map.get(key);
348  } catch (const std::out_of_range &) {
349  throw py::key_error("key '" + key + "' does not exist");
350  }
351  })
352  .def("__setitem__", &StringMap::set)
353  .def("__len__", &StringMap::size)
354  .def(
355  "__iter__",
356  [](const StringMap &map) { return py::make_key_iterator(map.begin(), map.end()); },
357  py::keep_alive<0, 1>())
358  .def(
359  "items",
360  [](const StringMap &map) { return py::make_iterator(map.begin(), map.end()); },
361  py::keep_alive<0, 1>())
362  .def(
363  "values",
364  [](const StringMap &map) { return py::make_value_iterator(map.begin(), map.end()); },
365  py::keep_alive<0, 1>());
366 
367  // test_generalized_iterators
368  class IntPairs {
369  public:
370  explicit IntPairs(std::vector<std::pair<int, int>> data) : data_(std::move(data)) {}
371  const std::pair<int, int> *begin() const { return data_.data(); }
372  // .end() only required for py::make_iterator(self) overload
373  const std::pair<int, int> *end() const { return data_.data() + data_.size(); }
374 
375  private:
376  std::vector<std::pair<int, int>> data_;
377  };
378  py::class_<IntPairs>(m, "IntPairs")
379  .def(py::init<std::vector<std::pair<int, int>>>())
380  .def(
381  "nonzero",
382  [](const IntPairs &s) {
383  return py::make_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()),
384  NonZeroSentinel());
385  },
386  py::keep_alive<0, 1>())
387  .def(
388  "nonzero_keys",
389  [](const IntPairs &s) {
390  return py::make_key_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()),
391  NonZeroSentinel());
392  },
393  py::keep_alive<0, 1>())
394  .def(
395  "nonzero_values",
396  [](const IntPairs &s) {
397  return py::make_value_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()),
398  NonZeroSentinel());
399  },
400  py::keep_alive<0, 1>())
401 
402  // test iterator that returns values instead of references
403  .def(
404  "nonref",
405  [](const IntPairs &s) {
406  return py::make_iterator(NonRefIterator<std::pair<int, int>>(s.begin()),
407  NonRefIterator<std::pair<int, int>>(s.end()));
408  },
409  py::keep_alive<0, 1>())
410  .def(
411  "nonref_keys",
412  [](const IntPairs &s) {
413  return py::make_key_iterator(NonRefIterator<std::pair<int, int>>(s.begin()),
414  NonRefIterator<std::pair<int, int>>(s.end()));
415  },
416  py::keep_alive<0, 1>())
417  .def(
418  "nonref_values",
419  [](const IntPairs &s) {
420  return py::make_value_iterator(NonRefIterator<std::pair<int, int>>(s.begin()),
421  NonRefIterator<std::pair<int, int>>(s.end()));
422  },
423  py::keep_alive<0, 1>())
424 
425  // test single-argument make_iterator
426  .def(
427  "simple_iterator",
428  [](IntPairs &self) { return py::make_iterator(self); },
429  py::keep_alive<0, 1>())
430  .def(
431  "simple_keys",
432  [](IntPairs &self) { return py::make_key_iterator(self); },
433  py::keep_alive<0, 1>())
434  .def(
435  "simple_values",
436  [](IntPairs &self) { return py::make_value_iterator(self); },
437  py::keep_alive<0, 1>())
438 
439  // Test iterator with an Extra (doesn't do anything useful, so not used
440  // at runtime, but tests need to be able to compile with the correct
441  // overload. See PR #3293.
442  .def(
443  "_make_iterator_extras",
444  [](IntPairs &self) { return py::make_iterator(self, py::call_guard<int>()); },
445  py::keep_alive<0, 1>())
446  .def(
447  "_make_key_extras",
448  [](IntPairs &self) { return py::make_key_iterator(self, py::call_guard<int>()); },
449  py::keep_alive<0, 1>())
450  .def(
451  "_make_value_extras",
452  [](IntPairs &self) { return py::make_value_iterator(self, py::call_guard<int>()); },
453  py::keep_alive<0, 1>());
454 
455  // test_iterator_referencing
456  py::class_<NonCopyableInt>(m, "NonCopyableInt")
457  .def(py::init<int>())
458  .def("set", &NonCopyableInt::set)
459  .def("__int__", &NonCopyableInt::get);
460  py::class_<std::vector<NonCopyableInt>>(m, "VectorNonCopyableInt")
461  .def(py::init<>())
462  .def("append",
463  [](std::vector<NonCopyableInt> &vec, int value) { vec.emplace_back(value); })
464  .def("__iter__", [](std::vector<NonCopyableInt> &vec) {
465  return py::make_iterator(vec.begin(), vec.end());
466  });
467  py::class_<std::vector<NonCopyableIntPair>>(m, "VectorNonCopyableIntPair")
468  .def(py::init<>())
469  .def("append",
470  [](std::vector<NonCopyableIntPair> &vec, const std::pair<int, int> &value) {
471  vec.emplace_back(NonCopyableInt(value.first), NonCopyableInt(value.second));
472  })
473  .def("keys",
474  [](std::vector<NonCopyableIntPair> &vec) {
475  return py::make_key_iterator(vec.begin(), vec.end());
476  })
477  .def("values", [](std::vector<NonCopyableIntPair> &vec) {
478  return py::make_value_iterator(vec.begin(), vec.end());
479  });
480 
481 #if 0
482  // Obsolete: special data structure for exposing custom iterator types to python
483  // kept here for illustrative purposes because there might be some use cases which
484  // are not covered by the much simpler py::make_iterator
485 
486  struct PySequenceIterator {
487  PySequenceIterator(const Sequence &seq, py::object ref) : seq(seq), ref(ref) { }
488 
489  float next() {
490  if (index == seq.size())
491  throw py::stop_iteration();
492  return seq[index++];
493  }
494 
495  const Sequence &seq;
496  py::object ref; // keep a reference
497  size_t index = 0;
498  };
499 
500  py::class_<PySequenceIterator>(seq, "Iterator")
501  .def("__iter__", [](PySequenceIterator &it) -> PySequenceIterator& { return it; })
502  .def("__next__", &PySequenceIterator::next);
503 
504  On the actual Sequence object, the iterator would be constructed as follows:
505  .def("__iter__", [](py::object s) { return PySequenceIterator(s.cast<const Sequence &>(), s); })
506 #endif
507 
508  // test_python_iterator_in_cpp
509  m.def("object_to_list", [](const py::object &o) {
510  auto l = py::list();
511  for (auto item : o) {
512  l.append(item);
513  }
514  return l;
515  });
516 
517  m.def("iterator_to_list", [](py::iterator it) {
518  auto l = py::list();
519  while (it != py::iterator::sentinel()) {
520  l.append(*it);
521  ++it;
522  }
523  return l;
524  });
525 
526  // test_sequence_length: check that Python sequences can be converted to py::sequence.
527  m.def("sequence_length", [](const py::sequence &seq) { return seq.size(); });
528 
529  // Make sure that py::iterator works with std algorithms
530  m.def("count_none", [](const py::object &o) {
531  return std::count_if(o.begin(), o.end(), [](py::handle h) { return h.is_none(); });
532  });
533 
534  m.def("find_none", [](const py::object &o) {
535  auto it = std::find_if(o.begin(), o.end(), [](py::handle h) { return h.is_none(); });
536  return it->is_none();
537  });
538 
539  m.def("count_nonzeros", [](const py::dict &d) {
540  return std::count_if(d.begin(), d.end(), [](std::pair<py::handle, py::handle> p) {
541  return p.second.cast<int>() != 0;
542  });
543  });
544 
545  m.def("tuple_iterator", &test_random_access_iterator<py::tuple>);
546  m.def("list_iterator", &test_random_access_iterator<py::list>);
547  m.def("sequence_iterator", &test_random_access_iterator<py::sequence>);
548 
549  // test_iterator_passthrough
550  // #181: iterator passthrough did not compile
551  m.def("iterator_passthrough", [](py::iterator s) -> py::iterator {
552  return py::make_iterator(std::begin(s), std::end(s));
553  });
554 
555  // test_iterator_rvp
556  // #388: Can't make iterators via make_iterator() with different r/v policies
557  static std::vector<int> list = {1, 2, 3};
558  m.def("make_iterator_1",
559  []() { return py::make_iterator<py::return_value_policy::copy>(list); });
560  m.def("make_iterator_2",
561  []() { return py::make_iterator<py::return_value_policy::automatic>(list); });
562 }
const gtsam::Symbol key('X', 0)
Matrix3f m
def step(data, isam, result, truth, currPoseIndex)
Definition: visual_isam.py:82
Scalar * b
Definition: benchVecAdd.cpp:17
tuple make_tuple()
Definition: cast.h:1209
int n
bool assert_equal(const Matrix &expected, const Matrix &actual, double tol)
Definition: Matrix.cpp:40
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool operator!=(const bfloat16 &a, const bfloat16 &b)
Definition: BFloat16.h:221
void print_destroyed(T *inst, Values &&...values)
static const self_t self
Definition: operators.h:72
void print_copy_assigned(T *inst, Values &&...values)
void print_copy_created(T *inst, Values &&...values)
Scalar Scalar int size
Definition: benchVecAdd.cpp:17
PYBIND11_MAKE_OPAQUE(std::vector< NonCopyableInt >)
static const Line3 l(Rot3(), 1, 1)
std::map< double, double > Sequence
Our sequence representation is a map of {x: y} values where y = f(x)
Definition: FitBasis.h:36
Values result
NonCopyableInt & operator=(NonCopyableInt &&other) noexcept
void print_move_assigned(T *inst, Values &&...values)
Array< int, Dynamic, 1 > v
Eigen::Triplet< double > T
py::list test_random_access_iterator(PythonType x)
int data[]
RealScalar s
NonCopyableInt(NonCopyableInt &&other) noexcept
void set(Container &c, Position position, const Value &value)
Reference counting helper.
Definition: object.h:67
NonZeroIterator & operator++()
internal::enable_if<!(symbolic::is_symbolic< FirstType >::value||symbolic::is_symbolic< LastType >::value), ArithmeticSequence< typename internal::cleanup_index_type< FirstType >::type, Index > >::type seq(FirstType f, LastType l)
NonRefIterator & operator++()
bool operator==(const NonRefIterator &other) const
const double h
bool operator==(const NonZeroIterator< std::pair< A, B >> &it, const NonZeroSentinel &)
void print_created(T *inst, Values &&...values)
detail::initimpl::constructor< Args... > init()
Binds an existing constructor taking arguments Args...
Definition: pybind11.h:1882
static EIGEN_DEPRECATED const end_t end
float * p
iterator make_key_iterator(Iterator &&first, Sentinel &&last, Extra &&...extra)
Definition: pybind11.h:2391
iterator make_iterator(Iterator &&first, Sentinel &&last, Extra &&...extra)
Makes a python iterator from a first and past-the-end C++ InputIterator.
Definition: pybind11.h:2373
std::pair< NonCopyableInt, NonCopyableInt > NonCopyableIntPair
iterator make_value_iterator(Iterator &&first, Sentinel &&last, Extra &&...extra)
Definition: pybind11.h:2409
void print_move_created(T *inst, Values &&...values)
set noclip points set clip one set noclip two set bar set border lt lw set xdata set ydata set zdata set x2data set y2data set boxwidth set dummy x
TEST_SUBMODULE(sequences_and_iterators, m)


gtsam
Author(s):
autogenerated on Tue Jul 4 2023 02:37:46