test_sequences_and_iterators.cpp revision 11986:c12e4625ab56
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_tests.h" 12#include "constructor_stats.h" 13#include <pybind11/operators.h> 14#include <pybind11/stl.h> 15 16class Sequence { 17public: 18 Sequence(size_t size) : m_size(size) { 19 print_created(this, "of size", m_size); 20 m_data = new float[size]; 21 memset(m_data, 0, sizeof(float) * size); 22 } 23 24 Sequence(const std::vector<float> &value) : m_size(value.size()) { 25 print_created(this, "of size", m_size, "from std::vector"); 26 m_data = new float[m_size]; 27 memcpy(m_data, &value[0], sizeof(float) * m_size); 28 } 29 30 Sequence(const Sequence &s) : m_size(s.m_size) { 31 print_copy_created(this); 32 m_data = new float[m_size]; 33 memcpy(m_data, s.m_data, sizeof(float)*m_size); 34 } 35 36 Sequence(Sequence &&s) : m_size(s.m_size), m_data(s.m_data) { 37 print_move_created(this); 38 s.m_size = 0; 39 s.m_data = nullptr; 40 } 41 42 ~Sequence() { 43 print_destroyed(this); 44 delete[] m_data; 45 } 46 47 Sequence &operator=(const Sequence &s) { 48 if (&s != this) { 49 delete[] m_data; 50 m_size = s.m_size; 51 m_data = new float[m_size]; 52 memcpy(m_data, s.m_data, sizeof(float)*m_size); 53 } 54 55 print_copy_assigned(this); 56 57 return *this; 58 } 59 60 Sequence &operator=(Sequence &&s) { 61 if (&s != this) { 62 delete[] m_data; 63 m_size = s.m_size; 64 m_data = s.m_data; 65 s.m_size = 0; 66 s.m_data = nullptr; 67 } 68 69 print_move_assigned(this); 70 71 return *this; 72 } 73 74 bool operator==(const Sequence &s) const { 75 if (m_size != s.size()) 76 return false; 77 for (size_t i=0; i<m_size; ++i) 78 if (m_data[i] != s[i]) 79 return false; 80 return true; 81 } 82 83 bool operator!=(const Sequence &s) const { 84 return !operator==(s); 85 } 86 87 float operator[](size_t index) const { 88 return m_data[index]; 89 } 90 91 float &operator[](size_t index) { 92 return m_data[index]; 93 } 94 95 bool contains(float v) const { 96 for (size_t i=0; i<m_size; ++i) 97 if (v == m_data[i]) 98 return true; 99 return false; 100 } 101 102 Sequence reversed() const { 103 Sequence result(m_size); 104 for (size_t i=0; i<m_size; ++i) 105 result[m_size-i-1] = m_data[i]; 106 return result; 107 } 108 109 size_t size() const { return m_size; } 110 111 const float *begin() const { return m_data; } 112 const float *end() const { return m_data+m_size; } 113 114private: 115 size_t m_size; 116 float *m_data; 117}; 118 119class IntPairs { 120public: 121 IntPairs(std::vector<std::pair<int, int>> data) : data_(std::move(data)) {} 122 const std::pair<int, int>* begin() const { return data_.data(); } 123 124private: 125 std::vector<std::pair<int, int>> data_; 126}; 127 128// Interface of a map-like object that isn't (directly) an unordered_map, but provides some basic 129// map-like functionality. 130class StringMap { 131public: 132 StringMap() = default; 133 StringMap(std::unordered_map<std::string, std::string> init) 134 : map(std::move(init)) {} 135 136 void set(std::string key, std::string val) { 137 map[key] = val; 138 } 139 140 std::string get(std::string key) const { 141 return map.at(key); 142 } 143 144 size_t size() const { 145 return map.size(); 146 } 147 148private: 149 std::unordered_map<std::string, std::string> map; 150 151public: 152 decltype(map.cbegin()) begin() const { return map.cbegin(); } 153 decltype(map.cend()) end() const { return map.cend(); } 154}; 155 156template<typename T> 157class NonZeroIterator { 158 const T* ptr_; 159public: 160 NonZeroIterator(const T* ptr) : ptr_(ptr) {} 161 const T& operator*() const { return *ptr_; } 162 NonZeroIterator& operator++() { ++ptr_; return *this; } 163}; 164 165class NonZeroSentinel {}; 166 167template<typename A, typename B> 168bool operator==(const NonZeroIterator<std::pair<A, B>>& it, const NonZeroSentinel&) { 169 return !(*it).first || !(*it).second; 170} 171 172test_initializer sequences_and_iterators([](py::module &m) { 173 174 py::class_<Sequence> seq(m, "Sequence"); 175 176 seq.def(py::init<size_t>()) 177 .def(py::init<const std::vector<float>&>()) 178 /// Bare bones interface 179 .def("__getitem__", [](const Sequence &s, size_t i) { 180 if (i >= s.size()) 181 throw py::index_error(); 182 return s[i]; 183 }) 184 .def("__setitem__", [](Sequence &s, size_t i, float v) { 185 if (i >= s.size()) 186 throw py::index_error(); 187 s[i] = v; 188 }) 189 .def("__len__", &Sequence::size) 190 /// Optional sequence protocol operations 191 .def("__iter__", [](const Sequence &s) { return py::make_iterator(s.begin(), s.end()); }, 192 py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */) 193 .def("__contains__", [](const Sequence &s, float v) { return s.contains(v); }) 194 .def("__reversed__", [](const Sequence &s) -> Sequence { return s.reversed(); }) 195 /// Slicing protocol (optional) 196 .def("__getitem__", [](const Sequence &s, py::slice slice) -> Sequence* { 197 size_t start, stop, step, slicelength; 198 if (!slice.compute(s.size(), &start, &stop, &step, &slicelength)) 199 throw py::error_already_set(); 200 Sequence *seq = new Sequence(slicelength); 201 for (size_t i=0; i<slicelength; ++i) { 202 (*seq)[i] = s[start]; start += step; 203 } 204 return seq; 205 }) 206 .def("__setitem__", [](Sequence &s, py::slice slice, const Sequence &value) { 207 size_t start, stop, step, slicelength; 208 if (!slice.compute(s.size(), &start, &stop, &step, &slicelength)) 209 throw py::error_already_set(); 210 if (slicelength != value.size()) 211 throw std::runtime_error("Left and right hand size of slice assignment have different sizes!"); 212 for (size_t i=0; i<slicelength; ++i) { 213 s[start] = value[i]; start += step; 214 } 215 }) 216 /// Comparisons 217 .def(py::self == py::self) 218 .def(py::self != py::self); 219 // Could also define py::self + py::self for concatenation, etc. 220 221 py::class_<StringMap> map(m, "StringMap"); 222 223 map .def(py::init<>()) 224 .def(py::init<std::unordered_map<std::string, std::string>>()) 225 .def("__getitem__", [](const StringMap &map, std::string key) { 226 try { return map.get(key); } 227 catch (const std::out_of_range&) { 228 throw py::key_error("key '" + key + "' does not exist"); 229 } 230 }) 231 .def("__setitem__", &StringMap::set) 232 .def("__len__", &StringMap::size) 233 .def("__iter__", [](const StringMap &map) { return py::make_key_iterator(map.begin(), map.end()); }, 234 py::keep_alive<0, 1>()) 235 .def("items", [](const StringMap &map) { return py::make_iterator(map.begin(), map.end()); }, 236 py::keep_alive<0, 1>()) 237 ; 238 239 py::class_<IntPairs>(m, "IntPairs") 240 .def(py::init<std::vector<std::pair<int, int>>>()) 241 .def("nonzero", [](const IntPairs& s) { 242 return py::make_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()), NonZeroSentinel()); 243 }, py::keep_alive<0, 1>()) 244 .def("nonzero_keys", [](const IntPairs& s) { 245 return py::make_key_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()), NonZeroSentinel()); 246 }, py::keep_alive<0, 1>()); 247 248 249#if 0 250 // Obsolete: special data structure for exposing custom iterator types to python 251 // kept here for illustrative purposes because there might be some use cases which 252 // are not covered by the much simpler py::make_iterator 253 254 struct PySequenceIterator { 255 PySequenceIterator(const Sequence &seq, py::object ref) : seq(seq), ref(ref) { } 256 257 float next() { 258 if (index == seq.size()) 259 throw py::stop_iteration(); 260 return seq[index++]; 261 } 262 263 const Sequence &seq; 264 py::object ref; // keep a reference 265 size_t index = 0; 266 }; 267 268 py::class_<PySequenceIterator>(seq, "Iterator") 269 .def("__iter__", [](PySequenceIterator &it) -> PySequenceIterator& { return it; }) 270 .def("__next__", &PySequenceIterator::next); 271 272 On the actual Sequence object, the iterator would be constructed as follows: 273 .def("__iter__", [](py::object s) { return PySequenceIterator(s.cast<const Sequence &>(), s); }) 274#endif 275}); 276