blob: 952caf0f5d2066abdbf9b57da46c3409b9449993 [file] [log] [blame]
Wenzel Jakob38bd7112015-07-05 20:05:44 +02001/*
Wenzel Jakoba576e6a2015-07-29 17:51:54 +02002 example/example6.cpp -- supporting Pythons' sequence protocol, iterators,
3 etc.
Wenzel Jakob38bd7112015-07-05 20:05:44 +02004
5 Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.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 "example.h"
12#include <pybind/operators.h>
13
14class Sequence {
15public:
16 Sequence(size_t size) : m_size(size) {
17 std::cout << "Value constructor: Creating a sequence with " << m_size << " entries" << std::endl;
18 m_data = new float[size];
19 memset(m_data, 0, sizeof(float) * size);
20 }
21
22 Sequence(const std::vector<float> &value) : m_size(value.size()) {
23 std::cout << "Value constructor: Creating a sequence with " << m_size << " entries" << std::endl;
24 m_data = new float[m_size];
25 memcpy(m_data, &value[0], sizeof(float) * m_size);
26 }
27
28 Sequence(const Sequence &s) : m_size(s.m_size) {
29 std::cout << "Copy constructor: Creating a sequence with " << m_size << " entries" << std::endl;
30 m_data = new float[m_size];
31 memcpy(m_data, s.m_data, sizeof(float)*m_size);
32 }
33
34 Sequence(Sequence &&s) : m_size(s.m_size), m_data(s.m_data) {
35 std::cout << "Move constructor: Creating a sequence with " << m_size << " entries" << std::endl;
36 s.m_size = 0;
37 s.m_data = nullptr;
38 }
39
40 ~Sequence() {
41 std::cout << "Freeing a sequence with " << m_size << " entries" << std::endl;
42 delete[] m_data;
43 }
44
45 Sequence &operator=(const Sequence &s) {
46 std::cout << "Assignment operator: Creating a sequence with " << s.m_size << " entries" << std::endl;
47 delete[] m_data;
48 m_size = s.m_size;
49 m_data = new float[m_size];
50 memcpy(m_data, s.m_data, sizeof(float)*m_size);
51 return *this;
52 }
53
54 Sequence &operator=(Sequence &&s) {
55 std::cout << "Move assignment operator: Creating a sequence with " << s.m_size << " entries" << std::endl;
56 if (&s != this) {
57 delete[] m_data;
58 m_size = s.m_size;
59 m_data = s.m_data;
60 s.m_size = 0;
61 s.m_data = nullptr;
62 }
63 return *this;
64 }
65
66 bool operator==(const Sequence &s) const {
67 if (m_size != s.size())
68 return false;
69 for (size_t i=0; i<m_size; ++i)
70 if (m_data[i] != s[i])
71 return false;
72 return true;
73 }
74
75 bool operator!=(const Sequence &s) const {
76 return !operator==(s);
77 }
78
79 float operator[](size_t index) const {
80 return m_data[index];
81 }
82
83 float &operator[](size_t index) {
84 return m_data[index];
85 }
86
87 bool contains(float v) const {
88 for (size_t i=0; i<m_size; ++i)
89 if (v == m_data[i])
90 return true;
91 return false;
92 }
93
94 Sequence reversed() const {
95 Sequence result(m_size);
96 for (size_t i=0; i<m_size; ++i)
97 result[m_size-i-1] = m_data[i];
98 return result;
99 }
100
101 size_t size() const { return m_size; }
102
103private:
104 size_t m_size;
105 float *m_data;
106};
107
108namespace {
109 // Special iterator data structure for python
110 struct PySequenceIterator {
111 PySequenceIterator(const Sequence &seq, py::object ref) : seq(seq), ref(ref) { }
112
113 float next() {
114 if (index == seq.size())
115 throw py::stop_iteration();
116 return seq[index++];
117 }
118
119 const Sequence &seq;
120 py::object ref; // keep a reference
121 size_t index = 0;
122 };
123};
124
125void init_ex6(py::module &m) {
126 py::class_<Sequence> seq(m, "Sequence");
127
128 seq.def(py::init<size_t>())
129 .def(py::init<const std::vector<float>&>())
130 /// Bare bones interface
131 .def("__getitem__", [](const Sequence &s, size_t i) {
132 if (i >= s.size())
133 throw py::index_error();
134 return s[i];
135 })
136 .def("__setitem__", [](Sequence &s, size_t i, float v) {
137 if (i >= s.size())
138 throw py::index_error();
139 s[i] = v;
140 })
141 .def("__len__", &Sequence::size)
142 /// Optional sequence protocol operations
143 .def("__iter__", [](py::object s) { return PySequenceIterator(s.cast<const Sequence &>(), s); })
144 .def("__contains__", [](const Sequence &s, float v) { return s.contains(v); })
145 .def("__reversed__", [](const Sequence &s) -> Sequence { return s.reversed(); })
146 /// Slicing protocol (optional)
147 .def("__getitem__", [](const Sequence &s, py::slice slice) -> Sequence* {
148 py::ssize_t start, stop, step, slicelength;
149 if (!slice.compute(s.size(), &start, &stop, &step, &slicelength))
150 throw py::error_already_set();
151 Sequence *seq = new Sequence(slicelength);
152 for (int i=0; i<slicelength; ++i) {
153 (*seq)[i] = s[start]; start += step;
154 }
155 return seq;
156 })
157 .def("__setitem__", [](Sequence &s, py::slice slice, const Sequence &value) {
158 py::ssize_t start, stop, step, slicelength;
159 if (!slice.compute(s.size(), &start, &stop, &step, &slicelength))
160 throw py::error_already_set();
161 if ((size_t) slicelength != value.size())
162 throw std::runtime_error("Left and right hand size of slice assignment have different sizes!");
163 for (int i=0; i<slicelength; ++i) {
164 s[start] = value[i]; start += step;
165 }
166 })
167 /// Comparisons
168 .def(py::self == py::self)
169 .def(py::self != py::self);
170 // Could also define py::self + py::self for concatenation, etc.
171
172 py::class_<PySequenceIterator>(seq, "Iterator")
173 .def("__iter__", [](PySequenceIterator &it) -> PySequenceIterator& { return it; })
174 .def("__next__", &PySequenceIterator::next);
175}