μHAL (v2.8.17)
Part of the IPbus software repository
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
test_buffers.cpp
Go to the documentation of this file.
1/*
2 tests/test_buffers.cpp -- supporting Pythons' buffer protocol
3
4 Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
5
6 All rights reserved. Use of this source code is governed by a
7 BSD-style license that can be found in the LICENSE file.
8*/
9
10#include <pybind11/stl.h>
11
12#include "constructor_stats.h"
13#include "pybind11_tests.h"
14
15TEST_SUBMODULE(buffers, m) {
16 // test_from_python / test_to_python:
17 class Matrix {
18 public:
19 Matrix(py::ssize_t rows, py::ssize_t cols) : m_rows(rows), m_cols(cols) {
20 print_created(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
21 // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
22 m_data = new float[(size_t) (rows * cols)];
23 memset(m_data, 0, sizeof(float) * (size_t) (rows * cols));
24 }
25
26 Matrix(const Matrix &s) : m_rows(s.m_rows), m_cols(s.m_cols) {
28 std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
29 // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
30 m_data = new float[(size_t) (m_rows * m_cols)];
31 memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols));
32 }
33
34 Matrix(Matrix &&s) noexcept : m_rows(s.m_rows), m_cols(s.m_cols), m_data(s.m_data) {
36 s.m_rows = 0;
37 s.m_cols = 0;
38 s.m_data = nullptr;
39 }
40
41 ~Matrix() {
42 print_destroyed(this,
43 std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
44 delete[] m_data;
45 }
46
47 Matrix &operator=(const Matrix &s) {
48 if (this == &s) {
49 return *this;
50 }
52 std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
53 delete[] m_data;
54 m_rows = s.m_rows;
55 m_cols = s.m_cols;
56 m_data = new float[(size_t) (m_rows * m_cols)];
57 memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols));
58 return *this;
59 }
60
61 Matrix &operator=(Matrix &&s) noexcept {
63 std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
64 if (&s != this) {
65 delete[] m_data;
66 m_rows = s.m_rows;
67 m_cols = s.m_cols;
68 m_data = s.m_data;
69 s.m_rows = 0;
70 s.m_cols = 0;
71 s.m_data = nullptr;
72 }
73 return *this;
74 }
75
76 float operator()(py::ssize_t i, py::ssize_t j) const {
77 return m_data[(size_t) (i * m_cols + j)];
78 }
79
80 float &operator()(py::ssize_t i, py::ssize_t j) {
81 return m_data[(size_t) (i * m_cols + j)];
82 }
83
84 float *data() { return m_data; }
85
86 py::ssize_t rows() const { return m_rows; }
87 py::ssize_t cols() const { return m_cols; }
88
89 private:
90 py::ssize_t m_rows;
91 py::ssize_t m_cols;
92 float *m_data;
93 };
94 py::class_<Matrix>(m, "Matrix", py::buffer_protocol())
95 .def(py::init<py::ssize_t, py::ssize_t>())
97 .def(py::init([](const py::buffer &b) {
98 py::buffer_info info = b.request();
99 if (info.format != py::format_descriptor<float>::format() || info.ndim != 2) {
100 throw std::runtime_error("Incompatible buffer format!");
101 }
102
103 auto *v = new Matrix(info.shape[0], info.shape[1]);
104 memcpy(v->data(), info.ptr, sizeof(float) * (size_t) (v->rows() * v->cols()));
105 return v;
106 }))
107
108 .def("rows", &Matrix::rows)
109 .def("cols", &Matrix::cols)
110
112 .def("__getitem__",
113 [](const Matrix &m, std::pair<py::ssize_t, py::ssize_t> i) {
114 if (i.first >= m.rows() || i.second >= m.cols()) {
115 throw py::index_error();
116 }
117 return m(i.first, i.second);
118 })
119 .def("__setitem__",
120 [](Matrix &m, std::pair<py::ssize_t, py::ssize_t> i, float v) {
121 if (i.first >= m.rows() || i.second >= m.cols()) {
122 throw py::index_error();
123 }
124 m(i.first, i.second) = v;
125 })
127 .def_buffer([](Matrix &m) -> py::buffer_info {
128 return py::buffer_info(
129 m.data(), /* Pointer to buffer */
130 {m.rows(), m.cols()}, /* Buffer dimensions */
131 {sizeof(float) * size_t(m.cols()), /* Strides (in bytes) for each index */
132 sizeof(float)});
133 });
134
135 // test_inherited_protocol
136 class SquareMatrix : public Matrix {
137 public:
138 explicit SquareMatrix(py::ssize_t n) : Matrix(n, n) {}
139 };
140 // Derived classes inherit the buffer protocol and the buffer access function
141 py::class_<SquareMatrix, Matrix>(m, "SquareMatrix").def(py::init<py::ssize_t>());
142
143 // test_pointer_to_member_fn
144 // Tests that passing a pointer to member to the base class works in
145 // the derived class.
146 struct Buffer {
147 int32_t value = 0;
148
149 py::buffer_info get_buffer_info() {
150 return py::buffer_info(
151 &value, sizeof(value), py::format_descriptor<int32_t>::format(), 1);
152 }
153 };
154 py::class_<Buffer>(m, "Buffer", py::buffer_protocol())
155 .def(py::init<>())
156 .def_readwrite("value", &Buffer::value)
157 .def_buffer(&Buffer::get_buffer_info);
158
159 class ConstBuffer {
160 std::unique_ptr<int32_t> value;
161
162 public:
163 int32_t get_value() const { return *value; }
164 void set_value(int32_t v) { *value = v; }
165
166 py::buffer_info get_buffer_info() const {
167 return py::buffer_info(
168 value.get(), sizeof(*value), py::format_descriptor<int32_t>::format(), 1);
169 }
170
171 ConstBuffer() : value(new int32_t{0}) {}
172 };
173 py::class_<ConstBuffer>(m, "ConstBuffer", py::buffer_protocol())
174 .def(py::init<>())
175 .def_property("value", &ConstBuffer::get_value, &ConstBuffer::set_value)
176 .def_buffer(&ConstBuffer::get_buffer_info);
177
178 struct DerivedBuffer : public Buffer {};
179 py::class_<DerivedBuffer>(m, "DerivedBuffer", py::buffer_protocol())
180 .def(py::init<>())
181 .def_readwrite("value", (int32_t DerivedBuffer::*) &DerivedBuffer::value)
182 .def_buffer(&DerivedBuffer::get_buffer_info);
183
184 struct BufferReadOnly {
185 const uint8_t value = 0;
186 explicit BufferReadOnly(uint8_t value) : value(value) {}
187
188 py::buffer_info get_buffer_info() { return py::buffer_info(&value, 1); }
189 };
190 py::class_<BufferReadOnly>(m, "BufferReadOnly", py::buffer_protocol())
191 .def(py::init<uint8_t>())
192 .def_buffer(&BufferReadOnly::get_buffer_info);
193
194 struct BufferReadOnlySelect {
195 uint8_t value = 0;
196 bool readonly = false;
197
198 py::buffer_info get_buffer_info() { return py::buffer_info(&value, 1, readonly); }
199 };
200 py::class_<BufferReadOnlySelect>(m, "BufferReadOnlySelect", py::buffer_protocol())
201 .def(py::init<>())
202 .def_readwrite("value", &BufferReadOnlySelect::value)
203 .def_readwrite("readonly", &BufferReadOnlySelect::readonly)
204 .def_buffer(&BufferReadOnlySelect::get_buffer_info);
205
206 // Expose buffer_info for testing.
207 py::class_<py::buffer_info>(m, "buffer_info")
208 .def(py::init<>())
209 .def_readonly("itemsize", &py::buffer_info::itemsize)
210 .def_readonly("size", &py::buffer_info::size)
211 .def_readonly("format", &py::buffer_info::format)
212 .def_readonly("ndim", &py::buffer_info::ndim)
213 .def_readonly("shape", &py::buffer_info::shape)
214 .def_readonly("strides", &py::buffer_info::strides)
215 .def_readonly("readonly", &py::buffer_info::readonly)
216 .def("__repr__", [](py::handle self) {
217 return py::str("itemsize={0.itemsize!r}, size={0.size!r}, format={0.format!r}, "
218 "ndim={0.ndim!r}, shape={0.shape!r}, strides={0.strides!r}, "
219 "readonly={0.readonly!r}")
220 .format(self);
221 });
222
223 m.def("get_buffer_info", [](const py::buffer &buffer) { return buffer.request(); });
224}
buffer_info request(bool writable=false) const
Definition: pytypes.h:1826
std::size_t size_t
Definition: common.h:461
static const self_t self
Definition: operators.h:72
void print_copy_created(T *inst, Values &&...values)
void print_copy_assigned(T *inst, Values &&...values)
void print_created(T *inst, Values &&...values)
void print_destroyed(T *inst, Values &&...values)
void print_move_assigned(T *inst, Values &&...values)
void print_move_created(T *inst, Values &&...values)
#define TEST_SUBMODULE(name, variable)
arr data(const arr &a, Ix... index)