@@ -63,9 +63,11 @@ struct is_comparable<T, enable_if_t<container_traits<T>::is_pair>> {
63
63
template <typename , typename , typename ... Args> void vector_if_copy_constructible (const Args &...) { }
64
64
template <typename , typename , typename ... Args> void vector_if_equal_operator (const Args &...) { }
65
65
template <typename , typename , typename ... Args> void vector_if_insertion_operator (const Args &...) { }
66
+ template <typename , typename , typename ... Args> void vector_modifiers (const Args &...) { }
66
67
67
68
template <typename Vector, typename Class_>
68
69
void vector_if_copy_constructible (enable_if_t <
70
+ std::is_copy_constructible<Vector>::value &&
69
71
std::is_copy_constructible<typename Vector::value_type>::value, Class_> &cl) {
70
72
71
73
cl.def (pybind11::init<const Vector &>(), " Copy constructor" );
@@ -107,71 +109,34 @@ void vector_if_equal_operator(enable_if_t<is_comparable<Vector>::value, Class_>
107
109
);
108
110
}
109
111
110
- template <typename Vector, typename Class_> auto vector_if_insertion_operator (Class_ &cl, std::string const &name)
111
- -> decltype(std::declval<std::ostream&>() << std::declval<typename Vector::value_type>(), void()) {
112
- using size_type = typename Vector::size_type;
113
-
114
- cl.def (" __repr__" ,
115
- [name](Vector &v) {
116
- std::ostringstream s;
117
- s << name << ' [' ;
118
- for (size_type i=0 ; i < v.size (); ++i) {
119
- s << v[i];
120
- if (i != v.size () - 1 )
121
- s << " , " ;
122
- }
123
- s << ' ]' ;
124
- return s.str ();
125
- },
126
- " Return the canonical string representation of this list."
127
- );
128
- }
129
-
130
- NAMESPACE_END (detail)
131
-
132
- //
133
- // std::vector
134
- //
135
- template <typename Vector, typename holder_type = std::unique_ptr<Vector>, typename... Args>
136
- pybind11::class_<Vector, holder_type> bind_vector(pybind11::module &m, std::string const &name, Args&&... args) {
112
+ // Vector modifiers -- requires a copyable vector_type:
113
+ // (Technically, some of these (pop and __delitem__) don't actually require copyability, but it seems
114
+ // silly to allow deletion but not insertion, so include them here too.)
115
+ template <typename Vector, typename Class_>
116
+ void vector_modifiers (enable_if_t <std::is_copy_constructible<typename Vector::value_type>::value, Class_> &cl) {
137
117
using T = typename Vector::value_type;
138
118
using SizeType = typename Vector::size_type;
139
119
using DiffType = typename Vector::difference_type;
140
- using ItType = typename Vector::iterator;
141
- using Class_ = pybind11::class_<Vector, holder_type>;
142
-
143
- Class_ cl (m, name.c_str (), std::forward<Args>(args)...);
144
-
145
- cl.def (pybind11::init<>());
146
-
147
- // Register copy constructor (if possible)
148
- detail::vector_if_copy_constructible<Vector, Class_>(cl);
149
-
150
- // Register comparison-related operators and functions (if possible)
151
- detail::vector_if_equal_operator<Vector, Class_>(cl);
152
120
153
- // Register stream insertion operator (if possible)
154
- detail::vector_if_insertion_operator<Vector, Class_>(cl, name);
121
+ cl.def (" append" ,
122
+ [](Vector &v, const T &value) { v.push_back (value); },
123
+ arg (" x" ),
124
+ " Add an item to the end of the list" );
155
125
156
126
cl.def (" __init__" , [](Vector &v, iterable it) {
157
127
new (&v) Vector ();
158
128
try {
159
129
v.reserve (len (it));
160
130
for (handle h : it)
161
- v.push_back (h.cast <typename Vector::value_type >());
131
+ v.push_back (h.cast <T >());
162
132
} catch (...) {
163
133
v.~Vector ();
164
134
throw ;
165
135
}
166
136
});
167
137
168
- cl.def (" append" ,
169
- [](Vector &v, const T &value) { v.push_back (value); },
170
- arg (" x" ),
171
- " Add an item to the end of the list" );
172
-
173
138
cl.def (" extend" ,
174
- [](Vector &v, Vector &src) {
139
+ [](Vector &v, const Vector &src) {
175
140
v.reserve (v.size () + src.size ());
176
141
v.insert (v.end (), src.begin (), src.end ());
177
142
},
@@ -210,21 +175,6 @@ pybind11::class_<Vector, holder_type> bind_vector(pybind11::module &m, std::stri
210
175
" Remove and return the item at index ``i``"
211
176
);
212
177
213
- cl.def (" __bool__" ,
214
- [](const Vector &v) -> bool {
215
- return !v.empty ();
216
- },
217
- " Check whether the list is nonempty"
218
- );
219
-
220
- cl.def (" __getitem__" ,
221
- [](const Vector &v, SizeType i) -> T {
222
- if (i >= v.size ())
223
- throw pybind11::index_error ();
224
- return v[i];
225
- }
226
- );
227
-
228
178
cl.def (" __setitem__" ,
229
179
[](Vector &v, SizeType i, const T &t) {
230
180
if (i >= v.size ())
@@ -233,26 +183,6 @@ pybind11::class_<Vector, holder_type> bind_vector(pybind11::module &m, std::stri
233
183
}
234
184
);
235
185
236
- cl.def (" __delitem__" ,
237
- [](Vector &v, SizeType i) {
238
- if (i >= v.size ())
239
- throw pybind11::index_error ();
240
- v.erase (v.begin () + typename Vector::difference_type (i));
241
- },
242
- " Delete list elements using a slice object"
243
- );
244
-
245
- cl.def (" __len__" , &Vector::size);
246
-
247
- cl.def (" __iter__" ,
248
- [](Vector &v) {
249
- return pybind11::make_iterator<
250
- return_value_policy::reference_internal, ItType, ItType, T>(
251
- v.begin (), v.end ());
252
- },
253
- pybind11::keep_alive<0 , 1 >() /* Essential: keep list alive while iterator exists */
254
- );
255
-
256
186
// / Slicing protocol
257
187
cl.def (" __getitem__" ,
258
188
[](const Vector &v, slice slice) -> Vector * {
@@ -291,6 +221,15 @@ pybind11::class_<Vector, holder_type> bind_vector(pybind11::module &m, std::stri
291
221
" Assign list elements using a slice object"
292
222
);
293
223
224
+ cl.def (" __delitem__" ,
225
+ [](Vector &v, SizeType i) {
226
+ if (i >= v.size ())
227
+ throw pybind11::index_error ();
228
+ v.erase (v.begin () + DiffType (i));
229
+ },
230
+ " Delete the list elements at index ``i``"
231
+ );
232
+
294
233
cl.def (" __delitem__" ,
295
234
[](Vector &v, slice slice) {
296
235
size_t start, stop, step, slicelength;
@@ -310,6 +249,118 @@ pybind11::class_<Vector, holder_type> bind_vector(pybind11::module &m, std::stri
310
249
" Delete list elements using a slice object"
311
250
);
312
251
252
+ }
253
+
254
+ // Default __getitem__, when we can copy the value:
255
+ template <typename Vector, typename Class_>
256
+ void vector_accessor (enable_if_t <std::is_copy_constructible<typename Vector::value_type>::value, Class_> &cl) {
257
+ using T = typename Vector::value_type;
258
+ using SizeType = typename Vector::size_type;
259
+ using ItType = typename Vector::iterator;
260
+ cl.def (" __getitem__" ,
261
+ [](const Vector &v, SizeType i) -> T {
262
+ if (i >= v.size ())
263
+ throw pybind11::index_error ();
264
+ return v[i];
265
+ }
266
+ );
267
+
268
+ cl.def (" __iter__" ,
269
+ [](Vector &v) {
270
+ return pybind11::make_iterator<
271
+ return_value_policy::reference_internal, ItType, ItType, T>(
272
+ v.begin (), v.end ());
273
+ },
274
+ keep_alive<0 , 1 >() /* Essential: keep list alive while iterator exists */
275
+ );
276
+ }
277
+
278
+ // When we can't copy, we have to return a reference and use a keepalive:
279
+ template <typename Vector, typename Class_>
280
+ void vector_accessor (enable_if_t <!std::is_copy_constructible<typename Vector::value_type>::value, Class_> &cl) {
281
+ using T = typename Vector::value_type;
282
+ using SizeType = typename Vector::size_type;
283
+ using ItType = typename Vector::iterator;
284
+
285
+ cl.def (" __getitem__" ,
286
+ [](Vector &v, SizeType i) -> T & {
287
+ if (i >= v.size ())
288
+ throw pybind11::index_error ();
289
+ return v[i];
290
+ },
291
+ return_value_policy::reference_internal // ref + keepalive
292
+ );
293
+
294
+ cl.def (" __iter__" ,
295
+ [](Vector &v) {
296
+ return pybind11::make_iterator<
297
+ return_value_policy::reference_internal, ItType, ItType, T&>(
298
+ v.begin (), v.end ());
299
+ },
300
+ keep_alive<0 , 1 >() /* Essential: keep list alive while iterator exists */
301
+ );
302
+ }
303
+
304
+ template <typename Vector, typename Class_> auto vector_if_insertion_operator (Class_ &cl, std::string const &name)
305
+ -> decltype(std::declval<std::ostream&>() << std::declval<typename Vector::value_type>(), void()) {
306
+ using size_type = typename Vector::size_type;
307
+
308
+ cl.def (" __repr__" ,
309
+ [name](Vector &v) {
310
+ std::ostringstream s;
311
+ s << name << ' [' ;
312
+ for (size_type i=0 ; i < v.size (); ++i) {
313
+ s << v[i];
314
+ if (i != v.size () - 1 )
315
+ s << " , " ;
316
+ }
317
+ s << ' ]' ;
318
+ return s.str ();
319
+ },
320
+ " Return the canonical string representation of this list."
321
+ );
322
+ }
323
+
324
+ NAMESPACE_END (detail)
325
+
326
+ //
327
+ // std::vector
328
+ //
329
+ template <typename Vector, typename holder_type = std::unique_ptr<Vector>, typename... Args>
330
+ pybind11::class_<Vector, holder_type> bind_vector(pybind11::module &m, std::string const &name, Args&&... args) {
331
+ using Class_ = pybind11::class_<Vector, holder_type>;
332
+
333
+ Class_ cl (m, name.c_str (), std::forward<Args>(args)...);
334
+
335
+ cl.def (pybind11::init<>());
336
+
337
+ // Register copy constructor (if possible)
338
+ detail::vector_if_copy_constructible<Vector, Class_>(cl);
339
+
340
+ // Register comparison-related operators and functions (if possible)
341
+ detail::vector_if_equal_operator<Vector, Class_>(cl);
342
+
343
+ // Register stream insertion operator (if possible)
344
+ detail::vector_if_insertion_operator<Vector, Class_>(cl, name);
345
+
346
+ // Modifiers require copyable vector value type
347
+ detail::vector_modifiers<Vector, Class_>(cl);
348
+
349
+ // Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive
350
+ detail::vector_accessor<Vector, Class_>(cl);
351
+
352
+ cl.def (" __bool__" ,
353
+ [](const Vector &v) -> bool {
354
+ return !v.empty ();
355
+ },
356
+ " Check whether the list is nonempty"
357
+ );
358
+
359
+ cl.def (" __len__" , &Vector::size);
360
+
361
+
362
+
363
+
313
364
#if 0
314
365
// C++ style functions deprecated, leaving it here as an example
315
366
cl.def(pybind11::init<size_type>());
@@ -363,7 +414,9 @@ NAMESPACE_BEGIN(detail)
363
414
364
415
/* Fallback functions */
365
416
template <typename, typename, typename... Args> void map_if_insertion_operator(const Args &...) { }
417
+ template <typename , typename , typename ... Args> void map_assignment (const Args &...) { }
366
418
419
+ // Map assignment when copy-assignable: just copy the value
367
420
template <typename Map, typename Class_>
368
421
void map_assignment (enable_if_t <std::is_copy_assignable<typename Map::mapped_type>::value, Class_> &cl) {
369
422
using KeyType = typename Map::key_type;
@@ -378,21 +431,23 @@ void map_assignment(enable_if_t<std::is_copy_assignable<typename Map::mapped_typ
378
431
);
379
432
}
380
433
434
+ // Not copy-assignable, but still copy-constructible: we can update the value by erasing and reinserting
381
435
template <typename Map, typename Class_>
382
- void map_if_copy_assignable (enable_if_t <
383
- !std::is_copy_assignable<typename Map::mapped_type>::value,
436
+ void map_assignment (enable_if_t <
437
+ !std::is_copy_assignable<typename Map::mapped_type>::value &&
438
+ std::is_copy_constructible<typename Map::mapped_type>::value,
384
439
Class_> &cl) {
385
440
using KeyType = typename Map::key_type;
386
441
using MappedType = typename Map::mapped_type;
387
442
388
443
cl.def (" __setitem__" ,
389
444
[](Map &m, const KeyType &k, const MappedType &v) {
390
445
// We can't use m[k] = v; because value type might not be default constructable
391
- auto r = m.insert ( std::make_pair ( k, v) );
446
+ auto r = m.emplace ( k, v);
392
447
if (!r.second ) {
393
- // value type might be const so the only way to insert it is to erase it first...
448
+ // value type is not copy assignable so the only way to insert it is to erase it first...
394
449
m.erase (r.first );
395
- m.insert ( std::make_pair ( k, v) );
450
+ m.emplace ( k, v);
396
451
}
397
452
}
398
453
);
@@ -419,12 +474,48 @@ template <typename Map, typename Class_> auto map_if_insertion_operator(Class_ &
419
474
" Return the canonical string representation of this map."
420
475
);
421
476
}
477
+
478
+ // Default __getitem__, when we can copy the value:
479
+ template <typename Map, typename Class_>
480
+ void map_accessor (enable_if_t <std::is_copy_constructible<typename Map::mapped_type>::value, Class_> &cl) {
481
+ using KeyType = typename Map::key_type;
482
+ using MappedType = typename Map::mapped_type;
483
+
484
+ cl.def (" __getitem__" ,
485
+ [](Map &m, const KeyType &k) -> MappedType {
486
+ auto it = m.find (k);
487
+ if (it == m.end ())
488
+ throw pybind11::key_error ();
489
+ return it->second ;
490
+ }
491
+ );
492
+
493
+ }
494
+
495
+ // When we can't copy, we have to return a reference and use a keepalive:
496
+ template <typename Map, typename Class_>
497
+ void map_accessor (enable_if_t <!std::is_copy_constructible<typename Map::mapped_type>::value, Class_> &cl) {
498
+ using KeyType = typename Map::key_type;
499
+ using MappedType = typename Map::mapped_type;
500
+
501
+ cl.def (" __getitem__" ,
502
+ [](Map &m, const KeyType &k) -> MappedType & {
503
+ auto it = m.find (k);
504
+ if (it == m.end ())
505
+ throw pybind11::key_error ();
506
+ return it->second ;
507
+ },
508
+ return_value_policy::reference_internal // ref + keepalive
509
+ );
510
+
511
+ }
512
+
513
+
422
514
NAMESPACE_END (detail)
423
515
424
516
template <typename Map, typename holder_type = std::unique_ptr<Map>, typename... Args>
425
517
pybind11::class_<Map, holder_type> bind_map(module &m, const std::string &name, Args&&... args) {
426
518
using KeyType = typename Map::key_type;
427
- using MappedType = typename Map::mapped_type;
428
519
using Class_ = pybind11::class_<Map, holder_type>;
429
520
430
521
Class_ cl (m, name.c_str (), std::forward<Args>(args)...);
@@ -449,16 +540,11 @@ pybind11::class_<Map, holder_type> bind_map(module &m, const std::string &name,
449
540
pybind11::keep_alive<0 , 1 >() /* Essential: keep list alive while iterator exists */
450
541
);
451
542
452
- cl.def (" __getitem__" ,
453
- [](Map &m, const KeyType &k) -> MappedType {
454
- auto it = m.find (k);
455
- if (it == m.end ())
456
- throw pybind11::key_error ();
457
- return it->second ;
458
- }
459
- );
543
+ // Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive
544
+ detail::map_accessor<Map, Class_>(cl);
460
545
461
- detail::map_if_copy_assignable<Map, Class_>(cl);
546
+ // Assignment provided only if the type is copyable
547
+ detail::map_assignment<Map, Class_>(cl);
462
548
463
549
cl.def (" __delitem__" ,
464
550
[](Map &m, const KeyType &k) {
0 commit comments