@@ -63,9 +63,11 @@ struct is_comparable<T, enable_if_t<container_traits<T>::is_pair>> {
6363template <typename , typename , typename ... Args> void vector_if_copy_constructible (const Args &...) { }
6464template <typename , typename , typename ... Args> void vector_if_equal_operator (const Args &...) { }
6565template <typename , typename , typename ... Args> void vector_if_insertion_operator (const Args &...) { }
66+ template <typename , typename , typename ... Args> void vector_modifiers (const Args &...) { }
6667
6768template <typename Vector, typename Class_>
6869void vector_if_copy_constructible (enable_if_t <
70+ std::is_copy_constructible<Vector>::value &&
6971 std::is_copy_constructible<typename Vector::value_type>::value, Class_> &cl) {
7072
7173 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_>
107109 );
108110}
109111
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) {
137117 using T = typename Vector::value_type;
138118 using SizeType = typename Vector::size_type;
139119 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);
152120
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" );
155125
156126 cl.def (" __init__" , [](Vector &v, iterable it) {
157127 new (&v) Vector ();
158128 try {
159129 v.reserve (len (it));
160130 for (handle h : it)
161- v.push_back (h.cast <typename Vector::value_type >());
131+ v.push_back (h.cast <T >());
162132 } catch (...) {
163133 v.~Vector ();
164134 throw ;
165135 }
166136 });
167137
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-
173138 cl.def (" extend" ,
174- [](Vector &v, Vector &src) {
139+ [](Vector &v, const Vector &src) {
175140 v.reserve (v.size () + src.size ());
176141 v.insert (v.end (), src.begin (), src.end ());
177142 },
@@ -210,21 +175,6 @@ pybind11::class_<Vector, holder_type> bind_vector(pybind11::module &m, std::stri
210175 " Remove and return the item at index ``i``"
211176 );
212177
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-
228178 cl.def (" __setitem__" ,
229179 [](Vector &v, SizeType i, const T &t) {
230180 if (i >= v.size ())
@@ -233,26 +183,6 @@ pybind11::class_<Vector, holder_type> bind_vector(pybind11::module &m, std::stri
233183 }
234184 );
235185
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-
256186 // / Slicing protocol
257187 cl.def (" __getitem__" ,
258188 [](const Vector &v, slice slice) -> Vector * {
@@ -291,6 +221,15 @@ pybind11::class_<Vector, holder_type> bind_vector(pybind11::module &m, std::stri
291221 " Assign list elements using a slice object"
292222 );
293223
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+
294233 cl.def (" __delitem__" ,
295234 [](Vector &v, slice slice) {
296235 size_t start, stop, step, slicelength;
@@ -310,6 +249,118 @@ pybind11::class_<Vector, holder_type> bind_vector(pybind11::module &m, std::stri
310249 " Delete list elements using a slice object"
311250 );
312251
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+
313364#if 0
314365 // C++ style functions deprecated, leaving it here as an example
315366 cl.def(pybind11::init<size_type>());
@@ -363,7 +414,9 @@ NAMESPACE_BEGIN(detail)
363414
364415/* Fallback functions */
365416template <typename, typename, typename... Args> void map_if_insertion_operator(const Args &...) { }
417+ template <typename , typename , typename ... Args> void map_assignment (const Args &...) { }
366418
419+ // Map assignment when copy-assignable: just copy the value
367420template <typename Map, typename Class_>
368421void map_assignment (enable_if_t <std::is_copy_assignable<typename Map::mapped_type>::value, Class_> &cl) {
369422 using KeyType = typename Map::key_type;
@@ -378,21 +431,23 @@ void map_assignment(enable_if_t<std::is_copy_assignable<typename Map::mapped_typ
378431 );
379432}
380433
434+ // Not copy-assignable, but still copy-constructible: we can update the value by erasing and reinserting
381435template <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,
384439 Class_> &cl) {
385440 using KeyType = typename Map::key_type;
386441 using MappedType = typename Map::mapped_type;
387442
388443 cl.def (" __setitem__" ,
389444 [](Map &m, const KeyType &k, const MappedType &v) {
390445 // 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);
392447 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...
394449 m.erase (r.first );
395- m.insert ( std::make_pair ( k, v) );
450+ m.emplace ( k, v);
396451 }
397452 }
398453 );
@@ -419,12 +474,48 @@ template <typename Map, typename Class_> auto map_if_insertion_operator(Class_ &
419474 " Return the canonical string representation of this map."
420475 );
421476}
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+
422514NAMESPACE_END (detail)
423515
424516template <typename Map, typename holder_type = std::unique_ptr<Map>, typename... Args>
425517pybind11::class_<Map, holder_type> bind_map(module &m, const std::string &name, Args&&... args) {
426518 using KeyType = typename Map::key_type;
427- using MappedType = typename Map::mapped_type;
428519 using Class_ = pybind11::class_<Map, holder_type>;
429520
430521 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,
449540 pybind11::keep_alive<0 , 1 >() /* Essential: keep list alive while iterator exists */
450541 );
451542
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);
460545
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);
462548
463549 cl.def (" __delitem__" ,
464550 [](Map &m, const KeyType &k) {
0 commit comments