void init_ex6(py::module &m) { py::class_<Sequence> seq(m, "Sequence"); seq.def(py::init<size_t>()) .def(py::init<const std::vector<float>&>()) /// Bare bones interface .def("__getitem__", [](const Sequence &s, size_t i) { if (i >= s.size()) throw py::index_error(); return s[i]; }) .def("__setitem__", [](Sequence &s, size_t i, float v) { if (i >= s.size()) throw py::index_error(); s[i] = v; }) .def("__len__", &Sequence::size) /// Optional sequence protocol operations .def("__iter__", [](py::object s) { return PySequenceIterator(s.cast<const Sequence &>(), s); }) .def("__contains__", [](const Sequence &s, float v) { return s.contains(v); }) .def("__reversed__", [](const Sequence &s) -> Sequence { return s.reversed(); }) /// Slicing protocol (optional) .def("__getitem__", [](const Sequence &s, py::slice slice) -> Sequence* { py::ssize_t start, stop, step, slicelength; if (!slice.compute(s.size(), &start, &stop, &step, &slicelength)) throw py::error_already_set(); Sequence *seq = new Sequence(slicelength); for (int i=0; i<slicelength; ++i) { (*seq)[i] = s[start]; start += step; } return seq; }) .def("__setitem__", [](Sequence &s, py::slice slice, const Sequence &value) { py::ssize_t start, stop, step, slicelength; if (!slice.compute(s.size(), &start, &stop, &step, &slicelength)) throw py::error_already_set(); if ((size_t) slicelength != value.size()) throw std::runtime_error("Left and right hand size of slice assignment have different sizes!"); for (int i=0; i<slicelength; ++i) { s[start] = value[i]; start += step; } }) /// Comparisons .def(py::self == py::self) .def(py::self != py::self); // Could also define py::self + py::self for concatenation, etc. py::class_<PySequenceIterator>(seq, "Iterator") .def("__iter__", [](PySequenceIterator &it) -> PySequenceIterator& { return it; }) .def("__next__", &PySequenceIterator::next); }
void init_ex_sequences_and_iterators(py::module &m) { py::class_<Sequence> seq(m, "Sequence"); seq.def(py::init<size_t>()) .def(py::init<const std::vector<float>&>()) /// Bare bones interface .def("__getitem__", [](const Sequence &s, size_t i) { if (i >= s.size()) throw py::index_error(); return s[i]; }) .def("__setitem__", [](Sequence &s, size_t i, float v) { if (i >= s.size()) throw py::index_error(); s[i] = v; }) .def("__len__", &Sequence::size) /// Optional sequence protocol operations .def("__iter__", [](const Sequence &s) { return py::make_iterator(s.begin(), s.end()); }, py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */) .def("__contains__", [](const Sequence &s, float v) { return s.contains(v); }) .def("__reversed__", [](const Sequence &s) -> Sequence { return s.reversed(); }) /// Slicing protocol (optional) .def("__getitem__", [](const Sequence &s, py::slice slice) -> Sequence* { size_t start, stop, step, slicelength; if (!slice.compute(s.size(), &start, &stop, &step, &slicelength)) throw py::error_already_set(); Sequence *seq = new Sequence(slicelength); for (size_t i=0; i<slicelength; ++i) { (*seq)[i] = s[start]; start += step; } return seq; }) .def("__setitem__", [](Sequence &s, py::slice slice, const Sequence &value) { size_t start, stop, step, slicelength; if (!slice.compute(s.size(), &start, &stop, &step, &slicelength)) throw py::error_already_set(); if (slicelength != value.size()) throw std::runtime_error("Left and right hand size of slice assignment have different sizes!"); for (size_t i=0; i<slicelength; ++i) { s[start] = value[i]; start += step; } }) /// Comparisons .def(py::self == py::self) .def(py::self != py::self); // Could also define py::self + py::self for concatenation, etc. #if 0 // Obsolete: special data structure for exposing custom iterator types to python // kept here for illustrative purposes because there might be some use cases which // are not covered by the much simpler py::make_iterator struct PySequenceIterator { PySequenceIterator(const Sequence &seq, py::object ref) : seq(seq), ref(ref) { } float next() { if (index == seq.size()) throw py::stop_iteration(); return seq[index++]; } const Sequence &seq; py::object ref; // keep a reference size_t index = 0; }; py::class_<PySequenceIterator>(seq, "Iterator") .def("__iter__", [](PySequenceIterator &it) -> PySequenceIterator& { return it; }) .def("__next__", &PySequenceIterator::next); On the actual Sequence object, the iterator would be constructed as follows: .def("__iter__", [](py::object s) { return PySequenceIterator(s.cast<const Sequence &>(), s); }) #endif }