1 | /* |
2 | pybind11/std_bind.h: Binding generators for STL data types |
3 | |
4 | Copyright (c) 2016 Sergey Lyskov and Wenzel Jakob |
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 | #pragma once |
11 | |
12 | #include "detail/common.h" |
13 | #include "operators.h" |
14 | |
15 | #include <algorithm> |
16 | #include <sstream> |
17 | |
18 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) |
19 | PYBIND11_NAMESPACE_BEGIN(detail) |
20 | |
21 | /* SFINAE helper class used by 'is_comparable */ |
22 | template <typename T> |
23 | struct container_traits { |
24 | template <typename T2> |
25 | static std::true_type |
26 | test_comparable(decltype(std::declval<const T2 &>() == std::declval<const T2 &>()) *); |
27 | template <typename T2> |
28 | static std::false_type test_comparable(...); |
29 | template <typename T2> |
30 | static std::true_type test_value(typename T2::value_type *); |
31 | template <typename T2> |
32 | static std::false_type test_value(...); |
33 | template <typename T2> |
34 | static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *); |
35 | template <typename T2> |
36 | static std::false_type test_pair(...); |
37 | |
38 | static constexpr const bool is_comparable |
39 | = std::is_same<std::true_type, decltype(test_comparable<T>(nullptr))>::value; |
40 | static constexpr const bool is_pair |
41 | = std::is_same<std::true_type, decltype(test_pair<T>(nullptr, nullptr))>::value; |
42 | static constexpr const bool is_vector |
43 | = std::is_same<std::true_type, decltype(test_value<T>(nullptr))>::value; |
44 | static constexpr const bool is_element = !is_pair && !is_vector; |
45 | }; |
46 | |
47 | /* Default: is_comparable -> std::false_type */ |
48 | template <typename T, typename SFINAE = void> |
49 | struct is_comparable : std::false_type {}; |
50 | |
51 | /* For non-map data structures, check whether operator== can be instantiated */ |
52 | template <typename T> |
53 | struct is_comparable< |
54 | T, |
55 | enable_if_t<container_traits<T>::is_element && container_traits<T>::is_comparable>> |
56 | : std::true_type {}; |
57 | |
58 | /* For a vector/map data structure, recursively check the value type |
59 | (which is std::pair for maps) */ |
60 | template <typename T> |
61 | struct is_comparable<T, enable_if_t<container_traits<T>::is_vector>> { |
62 | static constexpr const bool value = is_comparable<typename T::value_type>::value; |
63 | }; |
64 | |
65 | /* For pairs, recursively check the two data types */ |
66 | template <typename T> |
67 | struct is_comparable<T, enable_if_t<container_traits<T>::is_pair>> { |
68 | static constexpr const bool value = is_comparable<typename T::first_type>::value |
69 | && is_comparable<typename T::second_type>::value; |
70 | }; |
71 | |
72 | /* Fallback functions */ |
73 | template <typename, typename, typename... Args> |
74 | void vector_if_copy_constructible(const Args &...) {} |
75 | template <typename, typename, typename... Args> |
76 | void vector_if_equal_operator(const Args &...) {} |
77 | template <typename, typename, typename... Args> |
78 | void vector_if_insertion_operator(const Args &...) {} |
79 | template <typename, typename, typename... Args> |
80 | void vector_modifiers(const Args &...) {} |
81 | |
82 | template <typename Vector, typename Class_> |
83 | void vector_if_copy_constructible(enable_if_t<is_copy_constructible<Vector>::value, Class_> &cl) { |
84 | cl.def(init<const Vector &>(), "Copy constructor" ); |
85 | } |
86 | |
87 | template <typename Vector, typename Class_> |
88 | void vector_if_equal_operator(enable_if_t<is_comparable<Vector>::value, Class_> &cl) { |
89 | using T = typename Vector::value_type; |
90 | |
91 | cl.def(self == self); |
92 | cl.def(self != self); |
93 | |
94 | cl.def( |
95 | "count" , |
96 | [](const Vector &v, const T &x) { return std::count(v.begin(), v.end(), x); }, |
97 | arg("x" ), |
98 | "Return the number of times ``x`` appears in the list" ); |
99 | |
100 | cl.def( |
101 | "remove" , |
102 | [](Vector &v, const T &x) { |
103 | auto p = std::find(v.begin(), v.end(), x); |
104 | if (p != v.end()) { |
105 | v.erase(p); |
106 | } else { |
107 | throw value_error(); |
108 | } |
109 | }, |
110 | arg("x" ), |
111 | "Remove the first item from the list whose value is x. " |
112 | "It is an error if there is no such item." ); |
113 | |
114 | cl.def( |
115 | "__contains__" , |
116 | [](const Vector &v, const T &x) { return std::find(v.begin(), v.end(), x) != v.end(); }, |
117 | arg("x" ), |
118 | "Return true the container contains ``x``" ); |
119 | } |
120 | |
121 | // Vector modifiers -- requires a copyable vector_type: |
122 | // (Technically, some of these (pop and __delitem__) don't actually require copyability, but it |
123 | // seems silly to allow deletion but not insertion, so include them here too.) |
124 | template <typename Vector, typename Class_> |
125 | void vector_modifiers( |
126 | enable_if_t<is_copy_constructible<typename Vector::value_type>::value, Class_> &cl) { |
127 | using T = typename Vector::value_type; |
128 | using SizeType = typename Vector::size_type; |
129 | using DiffType = typename Vector::difference_type; |
130 | |
131 | auto wrap_i = [](DiffType i, SizeType n) { |
132 | if (i < 0) { |
133 | i += n; |
134 | } |
135 | if (i < 0 || (SizeType) i >= n) { |
136 | throw index_error(); |
137 | } |
138 | return i; |
139 | }; |
140 | |
141 | cl.def( |
142 | "append" , |
143 | [](Vector &v, const T &value) { v.push_back(value); }, |
144 | arg("x" ), |
145 | "Add an item to the end of the list" ); |
146 | |
147 | cl.def(init([](const iterable &it) { |
148 | auto v = std::unique_ptr<Vector>(new Vector()); |
149 | v->reserve(len_hint(it)); |
150 | for (handle h : it) { |
151 | v->push_back(h.cast<T>()); |
152 | } |
153 | return v.release(); |
154 | })); |
155 | |
156 | cl.def( |
157 | "clear" , [](Vector &v) { v.clear(); }, "Clear the contents" ); |
158 | |
159 | cl.def( |
160 | "extend" , |
161 | [](Vector &v, const Vector &src) { v.insert(v.end(), src.begin(), src.end()); }, |
162 | arg("L" ), |
163 | "Extend the list by appending all the items in the given list" ); |
164 | |
165 | cl.def( |
166 | "extend" , |
167 | [](Vector &v, const iterable &it) { |
168 | const size_t old_size = v.size(); |
169 | v.reserve(old_size + len_hint(it)); |
170 | try { |
171 | for (handle h : it) { |
172 | v.push_back(h.cast<T>()); |
173 | } |
174 | } catch (const cast_error &) { |
175 | v.erase(v.begin() + static_cast<typename Vector::difference_type>(old_size), |
176 | v.end()); |
177 | try { |
178 | v.shrink_to_fit(); |
179 | } catch (const std::exception &) { |
180 | // Do nothing |
181 | } |
182 | throw; |
183 | } |
184 | }, |
185 | arg("L" ), |
186 | "Extend the list by appending all the items in the given list" ); |
187 | |
188 | cl.def( |
189 | "insert" , |
190 | [](Vector &v, DiffType i, const T &x) { |
191 | // Can't use wrap_i; i == v.size() is OK |
192 | if (i < 0) { |
193 | i += v.size(); |
194 | } |
195 | if (i < 0 || (SizeType) i > v.size()) { |
196 | throw index_error(); |
197 | } |
198 | v.insert(v.begin() + i, x); |
199 | }, |
200 | arg("i" ), |
201 | arg("x" ), |
202 | "Insert an item at a given position." ); |
203 | |
204 | cl.def( |
205 | "pop" , |
206 | [](Vector &v) { |
207 | if (v.empty()) { |
208 | throw index_error(); |
209 | } |
210 | T t = std::move(v.back()); |
211 | v.pop_back(); |
212 | return t; |
213 | }, |
214 | "Remove and return the last item" ); |
215 | |
216 | cl.def( |
217 | "pop" , |
218 | [wrap_i](Vector &v, DiffType i) { |
219 | i = wrap_i(i, v.size()); |
220 | T t = std::move(v[(SizeType) i]); |
221 | v.erase(std::next(v.begin(), i)); |
222 | return t; |
223 | }, |
224 | arg("i" ), |
225 | "Remove and return the item at index ``i``" ); |
226 | |
227 | cl.def("__setitem__" , [wrap_i](Vector &v, DiffType i, const T &t) { |
228 | i = wrap_i(i, v.size()); |
229 | v[(SizeType) i] = t; |
230 | }); |
231 | |
232 | /// Slicing protocol |
233 | cl.def( |
234 | "__getitem__" , |
235 | [](const Vector &v, const slice &slice) -> Vector * { |
236 | size_t start = 0, stop = 0, step = 0, slicelength = 0; |
237 | |
238 | if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) { |
239 | throw error_already_set(); |
240 | } |
241 | |
242 | auto *seq = new Vector(); |
243 | seq->reserve((size_t) slicelength); |
244 | |
245 | for (size_t i = 0; i < slicelength; ++i) { |
246 | seq->push_back(v[start]); |
247 | start += step; |
248 | } |
249 | return seq; |
250 | }, |
251 | arg("s" ), |
252 | "Retrieve list elements using a slice object" ); |
253 | |
254 | cl.def( |
255 | "__setitem__" , |
256 | [](Vector &v, const slice &slice, const Vector &value) { |
257 | size_t start = 0, stop = 0, step = 0, slicelength = 0; |
258 | if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) { |
259 | throw error_already_set(); |
260 | } |
261 | |
262 | if (slicelength != value.size()) { |
263 | throw std::runtime_error( |
264 | "Left and right hand size of slice assignment have different sizes!" ); |
265 | } |
266 | |
267 | for (size_t i = 0; i < slicelength; ++i) { |
268 | v[start] = value[i]; |
269 | start += step; |
270 | } |
271 | }, |
272 | "Assign list elements using a slice object" ); |
273 | |
274 | cl.def( |
275 | "__delitem__" , |
276 | [wrap_i](Vector &v, DiffType i) { |
277 | i = wrap_i(i, v.size()); |
278 | v.erase(v.begin() + i); |
279 | }, |
280 | "Delete the list elements at index ``i``" ); |
281 | |
282 | cl.def( |
283 | "__delitem__" , |
284 | [](Vector &v, const slice &slice) { |
285 | size_t start = 0, stop = 0, step = 0, slicelength = 0; |
286 | |
287 | if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) { |
288 | throw error_already_set(); |
289 | } |
290 | |
291 | if (step == 1 && false) { |
292 | v.erase(v.begin() + (DiffType) start, v.begin() + DiffType(start + slicelength)); |
293 | } else { |
294 | for (size_t i = 0; i < slicelength; ++i) { |
295 | v.erase(v.begin() + DiffType(start)); |
296 | start += step - 1; |
297 | } |
298 | } |
299 | }, |
300 | "Delete list elements using a slice object" ); |
301 | } |
302 | |
303 | // If the type has an operator[] that doesn't return a reference (most notably std::vector<bool>), |
304 | // we have to access by copying; otherwise we return by reference. |
305 | template <typename Vector> |
306 | using vector_needs_copy |
307 | = negation<std::is_same<decltype(std::declval<Vector>()[typename Vector::size_type()]), |
308 | typename Vector::value_type &>>; |
309 | |
310 | // The usual case: access and iterate by reference |
311 | template <typename Vector, typename Class_> |
312 | void vector_accessor(enable_if_t<!vector_needs_copy<Vector>::value, Class_> &cl) { |
313 | using T = typename Vector::value_type; |
314 | using SizeType = typename Vector::size_type; |
315 | using DiffType = typename Vector::difference_type; |
316 | using ItType = typename Vector::iterator; |
317 | |
318 | auto wrap_i = [](DiffType i, SizeType n) { |
319 | if (i < 0) { |
320 | i += n; |
321 | } |
322 | if (i < 0 || (SizeType) i >= n) { |
323 | throw index_error(); |
324 | } |
325 | return i; |
326 | }; |
327 | |
328 | cl.def( |
329 | "__getitem__" , |
330 | [wrap_i](Vector &v, DiffType i) -> T & { |
331 | i = wrap_i(i, v.size()); |
332 | return v[(SizeType) i]; |
333 | }, |
334 | return_value_policy::reference_internal // ref + keepalive |
335 | ); |
336 | |
337 | cl.def( |
338 | "__iter__" , |
339 | [](Vector &v) { |
340 | return make_iterator<return_value_policy::reference_internal, ItType, ItType, T &>( |
341 | v.begin(), v.end()); |
342 | }, |
343 | keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ |
344 | ); |
345 | } |
346 | |
347 | // The case for special objects, like std::vector<bool>, that have to be returned-by-copy: |
348 | template <typename Vector, typename Class_> |
349 | void vector_accessor(enable_if_t<vector_needs_copy<Vector>::value, Class_> &cl) { |
350 | using T = typename Vector::value_type; |
351 | using SizeType = typename Vector::size_type; |
352 | using DiffType = typename Vector::difference_type; |
353 | using ItType = typename Vector::iterator; |
354 | cl.def("__getitem__" , [](const Vector &v, DiffType i) -> T { |
355 | if (i < 0 && (i += v.size()) < 0) { |
356 | throw index_error(); |
357 | } |
358 | if ((SizeType) i >= v.size()) { |
359 | throw index_error(); |
360 | } |
361 | return v[(SizeType) i]; |
362 | }); |
363 | |
364 | cl.def( |
365 | "__iter__" , |
366 | [](Vector &v) { |
367 | return make_iterator<return_value_policy::copy, ItType, ItType, T>(v.begin(), v.end()); |
368 | }, |
369 | keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ |
370 | ); |
371 | } |
372 | |
373 | template <typename Vector, typename Class_> |
374 | auto vector_if_insertion_operator(Class_ &cl, std::string const &name) |
375 | -> decltype(std::declval<std::ostream &>() << std::declval<typename Vector::value_type>(), |
376 | void()) { |
377 | using size_type = typename Vector::size_type; |
378 | |
379 | cl.def( |
380 | "__repr__" , |
381 | [name](Vector &v) { |
382 | std::ostringstream s; |
383 | s << name << '['; |
384 | for (size_type i = 0; i < v.size(); ++i) { |
385 | s << v[i]; |
386 | if (i != v.size() - 1) { |
387 | s << ", " ; |
388 | } |
389 | } |
390 | s << ']'; |
391 | return s.str(); |
392 | }, |
393 | "Return the canonical string representation of this list." ); |
394 | } |
395 | |
396 | // Provide the buffer interface for vectors if we have data() and we have a format for it |
397 | // GCC seems to have "void std::vector<bool>::data()" - doing SFINAE on the existence of data() |
398 | // is insufficient, we need to check it returns an appropriate pointer |
399 | template <typename Vector, typename = void> |
400 | struct vector_has_data_and_format : std::false_type {}; |
401 | template <typename Vector> |
402 | struct vector_has_data_and_format< |
403 | Vector, |
404 | enable_if_t<std::is_same<decltype(format_descriptor<typename Vector::value_type>::format(), |
405 | std::declval<Vector>().data()), |
406 | typename Vector::value_type *>::value>> : std::true_type {}; |
407 | |
408 | // [workaround(intel)] Separate function required here |
409 | // Workaround as the Intel compiler does not compile the enable_if_t part below |
410 | // (tested with icc (ICC) 2021.1 Beta 20200827) |
411 | template <typename... Args> |
412 | constexpr bool args_any_are_buffer() { |
413 | return detail::any_of<std::is_same<Args, buffer_protocol>...>::value; |
414 | } |
415 | |
416 | // [workaround(intel)] Separate function required here |
417 | // [workaround(msvc)] Can't use constexpr bool in return type |
418 | |
419 | // Add the buffer interface to a vector |
420 | template <typename Vector, typename Class_, typename... Args> |
421 | void vector_buffer_impl(Class_ &cl, std::true_type) { |
422 | using T = typename Vector::value_type; |
423 | |
424 | static_assert(vector_has_data_and_format<Vector>::value, |
425 | "There is not an appropriate format descriptor for this vector" ); |
426 | |
427 | // numpy.h declares this for arbitrary types, but it may raise an exception and crash hard |
428 | // at runtime if PYBIND11_NUMPY_DTYPE hasn't been called, so check here |
429 | format_descriptor<T>::format(); |
430 | |
431 | cl.def_buffer([](Vector &v) -> buffer_info { |
432 | return buffer_info(v.data(), |
433 | static_cast<ssize_t>(sizeof(T)), |
434 | format_descriptor<T>::format(), |
435 | 1, |
436 | {v.size()}, |
437 | {sizeof(T)}); |
438 | }); |
439 | |
440 | cl.def(init([](const buffer &buf) { |
441 | auto info = buf.request(); |
442 | if (info.ndim != 1 || info.strides[0] % static_cast<ssize_t>(sizeof(T))) { |
443 | throw type_error("Only valid 1D buffers can be copied to a vector" ); |
444 | } |
445 | if (!detail::compare_buffer_info<T>::compare(info) |
446 | || (ssize_t) sizeof(T) != info.itemsize) { |
447 | throw type_error("Format mismatch (Python: " + info.format |
448 | + " C++: " + format_descriptor<T>::format() + ")" ); |
449 | } |
450 | |
451 | T *p = static_cast<T *>(info.ptr); |
452 | ssize_t step = info.strides[0] / static_cast<ssize_t>(sizeof(T)); |
453 | T *end = p + info.shape[0] * step; |
454 | if (step == 1) { |
455 | return Vector(p, end); |
456 | } |
457 | Vector vec; |
458 | vec.reserve((size_t) info.shape[0]); |
459 | for (; p != end; p += step) { |
460 | vec.push_back(*p); |
461 | } |
462 | return vec; |
463 | })); |
464 | |
465 | return; |
466 | } |
467 | |
468 | template <typename Vector, typename Class_, typename... Args> |
469 | void vector_buffer_impl(Class_ &, std::false_type) {} |
470 | |
471 | template <typename Vector, typename Class_, typename... Args> |
472 | void vector_buffer(Class_ &cl) { |
473 | vector_buffer_impl<Vector, Class_, Args...>( |
474 | cl, detail::any_of<std::is_same<Args, buffer_protocol>...>{}); |
475 | } |
476 | |
477 | PYBIND11_NAMESPACE_END(detail) |
478 | |
479 | // |
480 | // std::vector |
481 | // |
482 | template <typename Vector, typename holder_type = std::unique_ptr<Vector>, typename... Args> |
483 | class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, Args &&...args) { |
484 | using Class_ = class_<Vector, holder_type>; |
485 | |
486 | // If the value_type is unregistered (e.g. a converting type) or is itself registered |
487 | // module-local then make the vector binding module-local as well: |
488 | using vtype = typename Vector::value_type; |
489 | auto *vtype_info = detail::get_type_info(typeid(vtype)); |
490 | bool local = !vtype_info || vtype_info->module_local; |
491 | |
492 | Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...); |
493 | |
494 | // Declare the buffer interface if a buffer_protocol() is passed in |
495 | detail::vector_buffer<Vector, Class_, Args...>(cl); |
496 | |
497 | cl.def(init<>()); |
498 | |
499 | // Register copy constructor (if possible) |
500 | detail::vector_if_copy_constructible<Vector, Class_>(cl); |
501 | |
502 | // Register comparison-related operators and functions (if possible) |
503 | detail::vector_if_equal_operator<Vector, Class_>(cl); |
504 | |
505 | // Register stream insertion operator (if possible) |
506 | detail::vector_if_insertion_operator<Vector, Class_>(cl, name); |
507 | |
508 | // Modifiers require copyable vector value type |
509 | detail::vector_modifiers<Vector, Class_>(cl); |
510 | |
511 | // Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive |
512 | detail::vector_accessor<Vector, Class_>(cl); |
513 | |
514 | cl.def( |
515 | "__bool__" , |
516 | [](const Vector &v) -> bool { return !v.empty(); }, |
517 | "Check whether the list is nonempty" ); |
518 | |
519 | cl.def("__len__" , &Vector::size); |
520 | |
521 | #if 0 |
522 | // C++ style functions deprecated, leaving it here as an example |
523 | cl.def(init<size_type>()); |
524 | |
525 | cl.def("resize" , |
526 | (void (Vector::*) (size_type count)) & Vector::resize, |
527 | "changes the number of elements stored" ); |
528 | |
529 | cl.def("erase" , |
530 | [](Vector &v, SizeType i) { |
531 | if (i >= v.size()) |
532 | throw index_error(); |
533 | v.erase(v.begin() + i); |
534 | }, "erases element at index ``i``" ); |
535 | |
536 | cl.def("empty" , &Vector::empty, "checks whether the container is empty" ); |
537 | cl.def("size" , &Vector::size, "returns the number of elements" ); |
538 | cl.def("push_back" , (void (Vector::*)(const T&)) &Vector::push_back, "adds an element to the end" ); |
539 | cl.def("pop_back" , &Vector::pop_back, "removes the last element" ); |
540 | |
541 | cl.def("max_size" , &Vector::max_size, "returns the maximum possible number of elements" ); |
542 | cl.def("reserve" , &Vector::reserve, "reserves storage" ); |
543 | cl.def("capacity" , &Vector::capacity, "returns the number of elements that can be held in currently allocated storage" ); |
544 | cl.def("shrink_to_fit" , &Vector::shrink_to_fit, "reduces memory usage by freeing unused memory" ); |
545 | |
546 | cl.def("clear" , &Vector::clear, "clears the contents" ); |
547 | cl.def("swap" , &Vector::swap, "swaps the contents" ); |
548 | |
549 | cl.def("front" , [](Vector &v) { |
550 | if (v.size()) return v.front(); |
551 | else throw index_error(); |
552 | }, "access the first element" ); |
553 | |
554 | cl.def("back" , [](Vector &v) { |
555 | if (v.size()) return v.back(); |
556 | else throw index_error(); |
557 | }, "access the last element " ); |
558 | |
559 | #endif |
560 | |
561 | return cl; |
562 | } |
563 | |
564 | // |
565 | // std::map, std::unordered_map |
566 | // |
567 | |
568 | PYBIND11_NAMESPACE_BEGIN(detail) |
569 | |
570 | /* Fallback functions */ |
571 | template <typename, typename, typename... Args> |
572 | void map_if_insertion_operator(const Args &...) {} |
573 | template <typename, typename, typename... Args> |
574 | void map_assignment(const Args &...) {} |
575 | |
576 | // Map assignment when copy-assignable: just copy the value |
577 | template <typename Map, typename Class_> |
578 | void map_assignment( |
579 | enable_if_t<is_copy_assignable<typename Map::mapped_type>::value, Class_> &cl) { |
580 | using KeyType = typename Map::key_type; |
581 | using MappedType = typename Map::mapped_type; |
582 | |
583 | cl.def("__setitem__" , [](Map &m, const KeyType &k, const MappedType &v) { |
584 | auto it = m.find(k); |
585 | if (it != m.end()) { |
586 | it->second = v; |
587 | } else { |
588 | m.emplace(k, v); |
589 | } |
590 | }); |
591 | } |
592 | |
593 | // Not copy-assignable, but still copy-constructible: we can update the value by erasing and |
594 | // reinserting |
595 | template <typename Map, typename Class_> |
596 | void map_assignment(enable_if_t<!is_copy_assignable<typename Map::mapped_type>::value |
597 | && is_copy_constructible<typename Map::mapped_type>::value, |
598 | Class_> &cl) { |
599 | using KeyType = typename Map::key_type; |
600 | using MappedType = typename Map::mapped_type; |
601 | |
602 | cl.def("__setitem__" , [](Map &m, const KeyType &k, const MappedType &v) { |
603 | // We can't use m[k] = v; because value type might not be default constructable |
604 | auto r = m.emplace(k, v); |
605 | if (!r.second) { |
606 | // value type is not copy assignable so the only way to insert it is to erase it |
607 | // first... |
608 | m.erase(r.first); |
609 | m.emplace(k, v); |
610 | } |
611 | }); |
612 | } |
613 | |
614 | template <typename Map, typename Class_> |
615 | auto map_if_insertion_operator(Class_ &cl, std::string const &name) |
616 | -> decltype(std::declval<std::ostream &>() << std::declval<typename Map::key_type>() |
617 | << std::declval<typename Map::mapped_type>(), |
618 | void()) { |
619 | |
620 | cl.def( |
621 | "__repr__" , |
622 | [name](Map &m) { |
623 | std::ostringstream s; |
624 | s << name << '{'; |
625 | bool f = false; |
626 | for (auto const &kv : m) { |
627 | if (f) { |
628 | s << ", " ; |
629 | } |
630 | s << kv.first << ": " << kv.second; |
631 | f = true; |
632 | } |
633 | s << '}'; |
634 | return s.str(); |
635 | }, |
636 | "Return the canonical string representation of this map." ); |
637 | } |
638 | |
639 | template <typename Map> |
640 | struct keys_view { |
641 | Map ↦ |
642 | }; |
643 | |
644 | template <typename Map> |
645 | struct values_view { |
646 | Map ↦ |
647 | }; |
648 | |
649 | template <typename Map> |
650 | struct items_view { |
651 | Map ↦ |
652 | }; |
653 | |
654 | PYBIND11_NAMESPACE_END(detail) |
655 | |
656 | template <typename Map, typename holder_type = std::unique_ptr<Map>, typename... Args> |
657 | class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args &&...args) { |
658 | using KeyType = typename Map::key_type; |
659 | using MappedType = typename Map::mapped_type; |
660 | using KeysView = detail::keys_view<Map>; |
661 | using ValuesView = detail::values_view<Map>; |
662 | using ItemsView = detail::items_view<Map>; |
663 | using Class_ = class_<Map, holder_type>; |
664 | |
665 | // If either type is a non-module-local bound type then make the map binding non-local as well; |
666 | // otherwise (e.g. both types are either module-local or converting) the map will be |
667 | // module-local. |
668 | auto *tinfo = detail::get_type_info(typeid(MappedType)); |
669 | bool local = !tinfo || tinfo->module_local; |
670 | if (local) { |
671 | tinfo = detail::get_type_info(typeid(KeyType)); |
672 | local = !tinfo || tinfo->module_local; |
673 | } |
674 | |
675 | Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...); |
676 | class_<KeysView> keys_view( |
677 | scope, ("KeysView[" + name + "]" ).c_str(), pybind11::module_local(local)); |
678 | class_<ValuesView> values_view( |
679 | scope, ("ValuesView[" + name + "]" ).c_str(), pybind11::module_local(local)); |
680 | class_<ItemsView> items_view( |
681 | scope, ("ItemsView[" + name + "]" ).c_str(), pybind11::module_local(local)); |
682 | |
683 | cl.def(init<>()); |
684 | |
685 | // Register stream insertion operator (if possible) |
686 | detail::map_if_insertion_operator<Map, Class_>(cl, name); |
687 | |
688 | cl.def( |
689 | "__bool__" , |
690 | [](const Map &m) -> bool { return !m.empty(); }, |
691 | "Check whether the map is nonempty" ); |
692 | |
693 | cl.def( |
694 | "__iter__" , |
695 | [](Map &m) { return make_key_iterator(m.begin(), m.end()); }, |
696 | keep_alive<0, 1>() /* Essential: keep map alive while iterator exists */ |
697 | ); |
698 | |
699 | cl.def( |
700 | "keys" , |
701 | [](Map &m) { return KeysView{m}; }, |
702 | keep_alive<0, 1>() /* Essential: keep map alive while view exists */ |
703 | ); |
704 | |
705 | cl.def( |
706 | "values" , |
707 | [](Map &m) { return ValuesView{m}; }, |
708 | keep_alive<0, 1>() /* Essential: keep map alive while view exists */ |
709 | ); |
710 | |
711 | cl.def( |
712 | "items" , |
713 | [](Map &m) { return ItemsView{m}; }, |
714 | keep_alive<0, 1>() /* Essential: keep map alive while view exists */ |
715 | ); |
716 | |
717 | cl.def( |
718 | "__getitem__" , |
719 | [](Map &m, const KeyType &k) -> MappedType & { |
720 | auto it = m.find(k); |
721 | if (it == m.end()) { |
722 | throw key_error(); |
723 | } |
724 | return it->second; |
725 | }, |
726 | return_value_policy::reference_internal // ref + keepalive |
727 | ); |
728 | |
729 | cl.def("__contains__" , [](Map &m, const KeyType &k) -> bool { |
730 | auto it = m.find(k); |
731 | if (it == m.end()) { |
732 | return false; |
733 | } |
734 | return true; |
735 | }); |
736 | // Fallback for when the object is not of the key type |
737 | cl.def("__contains__" , [](Map &, const object &) -> bool { return false; }); |
738 | |
739 | // Assignment provided only if the type is copyable |
740 | detail::map_assignment<Map, Class_>(cl); |
741 | |
742 | cl.def("__delitem__" , [](Map &m, const KeyType &k) { |
743 | auto it = m.find(k); |
744 | if (it == m.end()) { |
745 | throw key_error(); |
746 | } |
747 | m.erase(it); |
748 | }); |
749 | |
750 | cl.def("__len__" , &Map::size); |
751 | |
752 | keys_view.def("__len__" , [](KeysView &view) { return view.map.size(); }); |
753 | keys_view.def( |
754 | "__iter__" , |
755 | [](KeysView &view) { return make_key_iterator(view.map.begin(), view.map.end()); }, |
756 | keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */ |
757 | ); |
758 | keys_view.def("__contains__" , [](KeysView &view, const KeyType &k) -> bool { |
759 | auto it = view.map.find(k); |
760 | if (it == view.map.end()) { |
761 | return false; |
762 | } |
763 | return true; |
764 | }); |
765 | // Fallback for when the object is not of the key type |
766 | keys_view.def("__contains__" , [](KeysView &, const object &) -> bool { return false; }); |
767 | |
768 | values_view.def("__len__" , [](ValuesView &view) { return view.map.size(); }); |
769 | values_view.def( |
770 | "__iter__" , |
771 | [](ValuesView &view) { return make_value_iterator(view.map.begin(), view.map.end()); }, |
772 | keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */ |
773 | ); |
774 | |
775 | items_view.def("__len__" , [](ItemsView &view) { return view.map.size(); }); |
776 | items_view.def( |
777 | "__iter__" , |
778 | [](ItemsView &view) { return make_iterator(view.map.begin(), view.map.end()); }, |
779 | keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */ |
780 | ); |
781 | |
782 | return cl; |
783 | } |
784 | |
785 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) |
786 | |