Lagrange
Loading...
Searching...
No Matches
bind_safe_vector.h
1/*
2 * Copyright 2025 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12#pragma once
13
14#include <lagrange/python/binding.h>
15
16#include <type_traits>
17
18NAMESPACE_BEGIN(NB_NAMESPACE)
19
20template <typename Vector, rv_policy Policy = rv_policy::automatic_reference, typename... Args>
21class_<Vector> bind_safe_vector(handle scope, const char* name, Args&&... args)
22{
23 using ValueRef = typename detail::iterator_access<typename Vector::iterator>::result_type;
24 using Value = std::decay_t<ValueRef>;
25 using ValueType = typename Value::element_type;
26
27 static_assert(
28 std::is_same_v<std::shared_ptr<ValueType>, Value>,
29 "bind_safe_vector(): Value type must be a std::shared_ptr<>");
30
31 static_assert(
32 !detail::is_base_caster_v<detail::make_caster<Value>> ||
33 detail::is_copy_constructible_v<Value> ||
34 (Policy != rv_policy::automatic_reference && Policy != rv_policy::copy),
35 "bind_safe_vector(): the generated __getitem__ would copy elements, so the "
36 "element type must be copy-constructible");
37
38 handle cl_cur = type<Vector>();
39 if (cl_cur.is_valid()) {
40 // Binding already exists, don't re-create
41 return borrow<class_<Vector>>(cl_cur);
42 }
43
44 auto cl = class_<Vector>(scope, name, std::forward<Args>(args)...)
45 .def(init<>(), "Default constructor")
46
47 .def("__len__", [](const Vector& v) { return v.size(); })
48
49 .def(
50 "__bool__",
51 [](const Vector& v) { return !v.empty(); },
52 "Check whether the vector is nonempty")
53
54 .def(
55 "__repr__",
56 [](handle_t<Vector> h) { return steal<str>(detail::repr_list(h.ptr())); })
57
58 .def(
59 "__iter__",
60 [](Vector& v) {
61 return make_iterator<Policy>(
62 type<Vector>(),
63 "Iterator",
64 v.Vector::Super::begin(),
65 v.Vector::Super::end());
66 },
67 keep_alive<0, 1>())
68
69 .def(
70 "__getitem__",
71 [](Vector& v, Py_ssize_t i) -> ValueRef {
72 return v.Vector::Super::operator[](detail::wrap(i, v.size()));
73 },
74 Policy)
75
76 .def("clear", [](Vector& v) { v.clear(); }, "Remove all items from list.");
77
78 if constexpr (detail::is_copy_constructible_v<Value>) {
79 cl.def(init<const Vector&>(), "Copy constructor");
80
81 cl.def(
82 "__init__",
83 [](Vector* v, typed<iterable, Value> seq) {
84 new (v) Vector();
85 v->reserve(len_hint(seq));
86 for (handle h : seq) v->Vector::Super::push_back(cast<Value>(h));
87 },
88 "Construct from an iterable object");
89
90 implicitly_convertible<iterable, Vector>();
91
92 cl.def(
93 "append",
94 [](Vector& v, const Value& value) { v.Vector::Super::push_back(value); },
95 "Append `arg` to the end of the list.")
96
97 .def(
98 "insert",
99 [](Vector& v, Py_ssize_t i, const Value& x) {
100 if (i < 0) i += (Py_ssize_t)v.size();
101 if (i < 0 || (size_t)i > v.size()) throw index_error();
102 v.insert(v.Vector::Super::begin() + i, x);
103 },
104 "Insert object `arg1` before index `arg0`.")
105
106 .def(
107 "pop",
108 [](Vector& v, Py_ssize_t i) {
109 size_t index = detail::wrap(i, v.size());
110 Value result = std::move(v.Vector::Super::operator[](index));
111 v.erase(v.Vector::Super::begin() + index);
112 return result;
113 },
114 arg("index") = -1,
115 "Remove and return item at `index` (default last).")
116
117 .def(
118 "extend",
119 [](Vector& v, const Vector& src) {
120 v.insert(
121 v.Vector::Super::end(),
122 src.Vector::Super::begin(),
123 src.Vector::Super::end());
124 },
125 "Extend `self` by appending elements from `arg`.")
126
127 .def(
128 "__setitem__",
129 [](Vector& v, Py_ssize_t i, const Value& value) {
130 v.Vector::Super::operator[](detail::wrap(i, v.size())) = value;
131 })
132
133 .def(
134 "__delitem__",
135 [](Vector& v, Py_ssize_t i) {
136 v.erase(v.Vector::Super::begin() + detail::wrap(i, v.size()));
137 })
138
139 .def(
140 "__getitem__",
141 [](const Vector& v, const slice& slice) -> Vector* {
142 auto [start, stop, step, length] = slice.compute(v.size());
143 auto* seq = new Vector();
144 seq->reserve(length);
145
146 for (size_t i = 0; i < length; ++i) {
147 seq->Vector::Super::push_back(v.Vector::Super::operator[](start));
148 start += step;
149 }
150
151 return seq;
152 })
153
154 .def(
155 "__setitem__",
156 [](Vector& v, const slice& slice, const Vector& value) {
157 auto [start, stop, step, length] = slice.compute(v.size());
158
159 if (length != value.size())
160 throw index_error(
161 "The left and right hand side of the slice "
162 "assignment have mismatched sizes!");
163
164 for (size_t i = 0; i < length; ++i) {
165 v.Vector::Super::operator[](start) = value.Vector::Super::operator[](i);
166 start += step;
167 }
168 })
169
170 .def("__delitem__", [](Vector& v, const slice& slice) {
171 auto [start, stop, step, length] = slice.compute(v.size());
172 if (length == 0) return;
173
174 stop = start + (length - 1) * step;
175 if (start > stop) {
176 std::swap(start, stop);
177 step = -step;
178 }
179
180 if (step == 1) {
181 v.erase(v.Vector::Super::begin() + start, v.Vector::Super::begin() + stop + 1);
182 } else {
183 for (size_t i = 0; i < length; ++i) {
184 v.erase(v.Vector::Super::begin() + stop);
185 stop -= step;
186 }
187 }
188 });
189 }
190
191 if constexpr (detail::is_equality_comparable_v<Value>) {
192 cl.def(self == self, sig("def __eq__(self, arg: object, /) -> bool"))
193 .def(self != self, sig("def __ne__(self, arg: object, /) -> bool"))
194
195 .def(
196 "__contains__",
197 [](const Vector& v, const Value& x) {
198 return std::find(v.Vector::Super::begin(), v.Vector::Super::end(), x) !=
199 v.Vector::Super::end();
200 })
201
202 .def(
203 "__contains__", // fallback for incompatible types
204 [](const Vector&, handle) { return false; })
205
206 .def(
207 "count",
208 [](const Vector& v, const Value& x) {
209 return std::count(v.Vector::Super::begin(), v.Vector::Super::end(), x);
210 },
211 "Return number of occurrences of `arg`.")
212
213 .def(
214 "remove",
215 [](Vector& v, const Value& x) {
216 auto p = std::find(v.Vector::Super::begin(), v.Vector::Super::end(), x);
217 if (p != v.Vector::Super::end())
218 v.erase(p);
219 else
220 throw value_error();
221 },
222 "Remove first occurrence of `arg`.");
223 }
224
225 return cl;
226}
227
228NAMESPACE_END(NB_NAMESPACE)
@ Value
Values that are not attached to a specific element.
Definition AttributeFwd.h:42
Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > Vector
Type alias for one-dimensional column Eigen vectors.
Definition views.h:79
Definition project.cpp:27