diff --git a/Examples/test-suite/common.mk b/Examples/test-suite/common.mk index 1316876f8..114c568c5 100644 --- a/Examples/test-suite/common.mk +++ b/Examples/test-suite/common.mk @@ -531,6 +531,7 @@ CPP11_TEST_CASES = \ cpp11_initializer_list \ cpp11_initializer_list_extend \ cpp11_lambda_functions \ + cpp11_li_std_array \ cpp11_noexcept \ cpp11_null_pointer_constant \ cpp11_raw_string_literals \ diff --git a/Examples/test-suite/cpp11_li_std_array.i b/Examples/test-suite/cpp11_li_std_array.i new file mode 100644 index 000000000..cda2198ac --- /dev/null +++ b/Examples/test-suite/cpp11_li_std_array.i @@ -0,0 +1,53 @@ +%module cpp11_li_std_array + +#if defined(SWIGPYTHON) + +%include + +%template(ArrayInt6) std::array; + +%inline %{ +std::array arrayOutVal() { + return { -2, -1, 0, 0, 1, 2 }; +} + +std::array & arrayOutRef() { + static std::array a = { -2, -1, 0, 0, 1, 2 }; + return a; +} + +std::array * arrayOutPtr() { + static std::array a = { -2, -1, 0, 0, 1, 2 }; + return &a; +} + +std::array arrayInVal(std::array myarray) { + std::array a = myarray; + for (auto& val : a) { + val *= 10; + } + return a; +} + +const std::array & arrayInConstRef(const std::array & myarray) { + static std::array a = myarray; + for (auto& val : a) { + val *= 10; + } + return a; +} + +void arrayInRef(std::array & myarray) { + for (auto& val : myarray) { + val *= 10; + } +} + +void arrayInPtr(std::array * myarray) { + for (auto& val : *myarray) { + val *= 10; + } +} +%} + +#endif diff --git a/Examples/test-suite/python/cpp11_li_std_array_runme.py b/Examples/test-suite/python/cpp11_li_std_array_runme.py new file mode 100644 index 000000000..d6e1348e9 --- /dev/null +++ b/Examples/test-suite/python/cpp11_li_std_array_runme.py @@ -0,0 +1,160 @@ +from cpp11_li_std_array import * +import sys + + +def failed(a, b, msg): + raise RuntimeError, msg + " " + str(list(a)) + " " + str(list(b)) + + +def compare_sequences(a, b): + if len(a) != len(b): + failed(a, b, "different sizes") + for i in range(len(a)): + if a[i] != b[i]: + failed(a, b, "elements are different") + +def compare_containers(pythonlist, swigarray): + compare_sequences(pythonlist, swigarray) + +def steps_exception(swigarray, i, j, step): + try: + if i == None and j == None: + a = swigarray[::step] + elif i == None: + a = swigarray[:j:step] + elif j == None: + a = swigarray[i::step] + else: + a = swigarray[i:j:step] + raise RuntimeError, "swigarray[" + str(i) + ":" + str(j) + ":" + str(step) + "] missed steps exception for " + str(list(swigarray)) + except ValueError, e: +# print("exception: {}".format(e)) + pass + +def del_exception(swigarray, i, j, step): + try: + if i == None and j == None: + del swigarray[::step] + elif j == None and step == None: + del swigarray[i] + elif i == None: + del swigarray[:j:step] + elif j == None: + del swigarray[i::step] + else: + del swigarray[i:j:step] + raise RuntimeError, "swigarray[" + str(i) + ":" + str(j) + ":" + str(step) + "] missed del exception for " + str(list(swigarray)) + except ValueError, e: +# print("exception: {}".format(e)) + pass + +def setslice_exception(swigarray, newval): + try: + swigarray[::] = newval + raise RuntimeError, "swigarray[::] = " + str(newval) + " missed set exception for swigarray:" + str(list(swigarray)) + except TypeError, e: +# print("exception: {}".format(e)) + pass + + +# Check std::array has similar behaviour to a Python list +# except it is not resizable + +ps = [0, 1, 2, 3, 4, 5] + +ai = ArrayInt6(ps) + +# slices +compare_containers(ps[0:6], ai[0:6]) +compare_containers(ps[0:10], ai[0:10]) +compare_containers(ps[-10:6], ai[-10:6]) +compare_containers(ps[-10:10], ai[-10:10]) + +compare_containers(ps[0:6:1], ai[0:6:1]) +compare_containers(ps[::], ai[::]) +compare_containers(ps[::1], ai[::1]) + +compare_containers([x for x in ps], [x for x in ai]) + +# Reverse +compare_containers(ps[::-1], ai[::-1]) +compare_containers(ps[5::-1], ai[5::-1]) +compare_containers(ps[10::-1], ai[10::-1]) + +# Steps other than +1 and -1 not supported +steps_exception(ai, 0, 6, 3) +steps_exception(ai, None, None, 0) +steps_exception(ai, None, None, 2) +steps_exception(ai, None, None, -2) +steps_exception(ai, 1, 3, 1) +steps_exception(ai, 3, 1, -1) + +# Modify content +for i in range(len(ps)): + ps[i] = (ps[i] + 1) * 10 + ai[i] = (ai[i] + 1) * 10 +compare_containers(ps, ai) + +# Delete +del_exception(ai, 0, 6, 3) +del_exception(ai, None, None, 0) +del_exception(ai, None, None, 2) +del_exception(ai, None, None, -2) +del_exception(ai, 1, 3, 1) +del_exception(ai, 3, 1, -1) + +del_exception(ai, 0, None, None) +del_exception(ai, 5, None, None) + +# Empty +ai = ArrayInt6() +compare_containers([0, 0, 0, 0, 0, 0], ai) + +# Set slice +newvals = [10, 20, 30, 40, 50, 60] +ai[::] = newvals +compare_containers(ai, newvals) + +newvals = [100, 200, 300, 400, 500, 600] +ai[0:6:1] = newvals +compare_containers(ai, newvals) + +newvals = [1000, 2000, 3000, 4000, 5000, 6000] +ai[::-1] = newvals +compare_containers(ai, newvals[::-1]) + +newvals = [10000, 20000, 30000, 40000, 50000, 60000] +ai[-10:100:1] = newvals +compare_containers(ai, newvals[-10:100:1]) + +setslice_exception(ai, [1, 2, 3, 4, 5, 6, 7]) +setslice_exception(ai, [1, 2, 3, 4, 5]) +setslice_exception(ai, [1, 2, 3, 4]) +setslice_exception(ai, [1, 2, 3]) +setslice_exception(ai, [1, 2]) +setslice_exception(ai, [1]) +setslice_exception(ai, []) + +# Check return +compare_containers(arrayOutVal(), [-2, -1, 0, 0, 1, 2]) +compare_containers(arrayOutRef(), [-2, -1, 0, 0, 1, 2]) +compare_containers(arrayOutPtr(), [-2, -1, 0, 0, 1, 2]) + +# Check passing arguments +ai = arrayInVal([9, 8, 7, 6, 5, 4]) +compare_containers(ai, [90, 80, 70, 60, 50, 40]) + +ai = arrayInConstRef([9, 8, 7, 6, 5, 4]) +compare_containers(ai, [90, 80, 70, 60, 50, 40]) + +ai = ArrayInt6([9, 8, 7, 6, 5, 4]) +arrayInRef(ai) +compare_containers(ai, [90, 80, 70, 60, 50, 40]) + +ai = ArrayInt6([9, 8, 7, 6, 5, 4]) +arrayInPtr(ai) +compare_containers(ai, [90, 80, 70, 60, 50, 40]) + +# fill +ai.fill(111) +compare_containers(ai, [111, 111, 111, 111, 111, 111]) diff --git a/Lib/python/pycontainer.swg b/Lib/python/pycontainer.swg index 7168862f5..7c5cc37f9 100644 --- a/Lib/python/pycontainer.swg +++ b/Lib/python/pycontainer.swg @@ -252,6 +252,12 @@ namespace swig { return pos; } + template + inline void + erase(Sequence* seq, const typename Sequence::iterator& position) { + seq->erase(position); + } + template inline Sequence* getslice(const Sequence* self, Difference i, Difference j, Py_ssize_t step) { @@ -552,6 +558,7 @@ namespace swig difference_type _index; }; + // STL container wrapper around a Python sequence template struct SwigPySequence_Cont { @@ -748,7 +755,6 @@ namespace swig return self->size(); } } - %enddef @@ -756,7 +762,7 @@ namespace swig %define %swig_sequence_methods_common(Sequence...) %swig_sequence_iterator(%arg(Sequence)) %swig_container_methods(%arg(Sequence)) - + %fragment("SwigPySequence_Base"); #if defined(SWIGPYTHON_BUILTIN) @@ -769,14 +775,6 @@ namespace swig #endif // SWIGPYTHON_BUILTIN %extend { - value_type pop() throw (std::out_of_range) { - if (self->size() == 0) - throw std::out_of_range("pop from empty container"); - Sequence::value_type x = self->back(); - self->pop_back(); - return x; - } - /* typemap for slice object support */ %typemap(in) PySliceObject* { if (!PySlice_Check($input)) { @@ -794,7 +792,11 @@ namespace swig return swig::getslice(self, i, j, 1); } - void __setslice__(difference_type i, difference_type j, const Sequence& v = Sequence()) throw (std::out_of_range, std::invalid_argument) { + void __setslice__(difference_type i, difference_type j) throw (std::out_of_range, std::invalid_argument) { + swig::setslice(self, i, j, 1, Sequence()); + } + + void __setslice__(difference_type i, difference_type j, const Sequence& v) throw (std::out_of_range, std::invalid_argument) { swig::setslice(self, i, j, 1, v); } @@ -803,11 +805,10 @@ namespace swig } #endif - void __delitem__(difference_type i) throw (std::out_of_range) { - self->erase(swig::getpos(self,i)); + void __delitem__(difference_type i) throw (std::out_of_range, std::invalid_argument) { + swig::erase(self, swig::getpos(self, i)); } - /* Overloaded methods for Python 3 compatibility * (Also useful in Python 2.x) */ @@ -858,12 +859,11 @@ namespace swig Sequence::difference_type jd = j; swig::delslice(self, id, jd, step); } - - } + } %enddef -%define %swig_sequence_methods(Sequence...) +%define %swig_sequence_methods_non_resizable(Sequence...) %swig_sequence_methods_common(%arg(Sequence)) %extend { const value_type& __getitem__(difference_type i) const throw (std::out_of_range) { @@ -876,19 +876,32 @@ namespace swig #if defined(SWIGPYTHON_BUILTIN) // This will be called through the mp_ass_subscript slot to delete an entry. - void __setitem__(difference_type i) throw (std::out_of_range) { - self->erase(swig::getpos(self,i)); + void __setitem__(difference_type i) throw (std::out_of_range, std::invalid_argument) { + swig::erase(self, swig::getpos(self, i)); } #endif + } +%enddef + +%define %swig_sequence_methods(Sequence...) + %swig_sequence_methods_non_resizable(%arg(Sequence)) + %extend { + value_type pop() throw (std::out_of_range) { + if (self->size() == 0) + throw std::out_of_range("pop from empty container"); + Sequence::value_type x = self->back(); + self->pop_back(); + return x; + } + void append(const value_type& x) { self->push_back(x); } - } - + } %enddef -%define %swig_sequence_methods_val(Sequence...) +%define %swig_sequence_methods_non_resizable_val(Sequence...) %swig_sequence_methods_common(%arg(Sequence)) %extend { value_type __getitem__(difference_type i) throw (std::out_of_range) { @@ -901,16 +914,28 @@ namespace swig #if defined(SWIGPYTHON_BUILTIN) // This will be called through the mp_ass_subscript slot to delete an entry. - void __setitem__(difference_type i) throw (std::out_of_range) { - self->erase(swig::getpos(self,i)); + void __setitem__(difference_type i) throw (std::out_of_range, std::invalid_argument) { + swig::erase(self, swig::getpos(self, i)); } #endif + } +%enddef + +%define %swig_sequence_methods_val(Sequence...) + %swig_sequence_methods_non_resizable_val(%arg(Sequence)) + %extend { + value_type pop() throw (std::out_of_range) { + if (self->size() == 0) + throw std::out_of_range("pop from empty container"); + Sequence::value_type x = self->back(); + self->pop_back(); + return x; + } void append(value_type x) { self->push_back(x); } - } - + } %enddef diff --git a/Lib/python/std_array.i b/Lib/python/std_array.i new file mode 100644 index 000000000..b894868a0 --- /dev/null +++ b/Lib/python/std_array.i @@ -0,0 +1,91 @@ +/* + std::array +*/ + +%fragment("StdArrayTraits","header",fragment="StdSequenceTraits") +%{ + namespace swig { + template + struct traits_asptr > { + static int asptr(PyObject *obj, std::array **vec) { + return traits_asptr_stdseq >::asptr(obj, vec); + } + }; + + template + struct traits_from > { + static PyObject *from(const std::array& vec) { + return traits_from_stdseq >::from(vec); + } + }; + + template + inline void + assign(const SwigPySeq& swigpyseq, std::array* seq) { + if (swigpyseq.size() < seq->size()) + throw std::invalid_argument("std::array cannot be expanded in size"); + else if (swigpyseq.size() > seq->size()) + throw std::invalid_argument("std::array cannot be reduced in size"); + std::copy(swigpyseq.begin(), swigpyseq.end(), seq->begin()); + } + + template + inline void + erase(std::array* seq, const typename std::array::iterator& position) { + throw std::invalid_argument("std::array object does not support item deletion"); + } + + // Only limited slicing is supported as std::array is fixed in size + template + inline std::array* + getslice(const std::array* self, Difference i, Difference j, Py_ssize_t step) { + using Sequence = std::array; + typename Sequence::size_type size = self->size(); + Difference ii = 0; + Difference jj = 0; + swig::slice_adjust(i, j, step, size, ii, jj); + + if (step == 1 && ii == 0 && jj == size) { + Sequence *sequence = new Sequence(); + std::copy(self->begin(), self->end(), sequence->begin()); + return sequence; + } else if (step == -1 && ii == (size - 1) && jj == -1) { + Sequence *sequence = new Sequence(); + std::copy(self->rbegin(), self->rend(), sequence->begin()); + return sequence; + } else { + throw std::invalid_argument("std::array object only supports getting a slice that is the size of the array"); + } + } + + template + inline void + setslice(std::array* self, Difference i, Difference j, Py_ssize_t step, const InputSeq& is = InputSeq()) { + using Sequence = std::array; + typename Sequence::size_type size = self->size(); + Difference ii = 0; + Difference jj = 0; + swig::slice_adjust(i, j, step, size, ii, jj, true); + + if (step == 1 && ii == 0 && jj == size) { + std::copy(is.begin(), is.end(), self->begin()); + } else if (step == -1 && ii == (size - 1) && jj == -1) { + std::copy(is.rbegin(), is.rend(), self->begin()); + } else { + throw std::invalid_argument("std::array object only supports setting a slice that is the size of the array"); + } + } + + template + inline void + delslice(std::array* self, Difference i, Difference j, Py_ssize_t step) { + throw std::invalid_argument("std::array object does not support item deletion"); + } + } +%} + +#define %swig_array_methods(Type...) %swig_sequence_methods_non_resizable(Type) +#define %swig_array_methods_val(Type...) %swig_sequence_methods_non_resizable_val(Type); + +%include + diff --git a/Lib/std/std_array.i b/Lib/std/std_array.i new file mode 100644 index 000000000..0676f670e --- /dev/null +++ b/Lib/std/std_array.i @@ -0,0 +1,88 @@ +// +// std::array +// + +%include + +%define %std_array_methods(array...) + %std_sequence_methods_non_resizable(array) + void fill(const value_type& u); +%enddef + + +%define %std_array_methods_val(array...) + %std_sequence_methods_non_resizable_val(array) + void fill(const value_type& u); +%enddef + +// ------------------------------------------------------------------------ +// std::array +// +// The aim of all that follows would be to integrate std::array with +// as much as possible, namely, to allow the user to pass and +// be returned tuples or lists. +// const declarations are used to guess the intent of the function being +// exported; therefore, the following rationale is applied: +// +// -- f(std::array), f(const std::array&): +// the parameter being read-only, either a sequence or a +// previously wrapped std::array can be passed. +// -- f(std::array&), f(std::array*): +// the parameter may be modified; therefore, only a wrapped std::array +// can be passed. +// -- std::array f(), const std::array& f(): +// the array is returned by copy; therefore, a sequence of T:s +// is returned which is most easily used in other functions +// -- std::array& f(), std::array* f(): +// the array is returned by reference; therefore, a wrapped std::array +// is returned +// -- const std::array* f(), f(const std::array*): +// for consistency, they expect and return a plain array pointer. +// ------------------------------------------------------------------------ + +%{ +#include +%} + +// exported classes + +namespace std { + + template + class array { + public: + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef _Tp value_type; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef _Tp& reference; + typedef const _Tp& const_reference; + + %traits_swigtype(_Tp); + %traits_enum(_Tp); + + %fragment(SWIG_Traits_frag(std::array<_Tp, _Nm >), "header", + fragment=SWIG_Traits_frag(_Tp), + fragment="StdArrayTraits") { + namespace swig { + template <> struct traits > { + typedef pointer_category category; + static const char* type_name() { + return "std::array<" #_Tp "," #_Nm " >"; + } + }; + } + } + + %typemap_traits_ptr(SWIG_TYPECHECK_STDARRAY, std::array<_Tp, _Nm >); + +#ifdef %swig_array_methods + // Add swig/language extra methods + %swig_array_methods(std::array<_Tp, _Nm >); +#endif + + %std_array_methods(array); + }; +} + diff --git a/Lib/std/std_container.i b/Lib/std/std_container.i index 8ed327bbe..fc46aed0c 100644 --- a/Lib/std/std_container.i +++ b/Lib/std/std_container.i @@ -6,20 +6,17 @@ #include %} -// Common container methods +// Common non-resizable container methods + +%define %std_container_methods_non_resizable(container...) -%define %std_container_methods(container...) container(); container(const container&); bool empty() const; size_type size() const; - void clear(); - void swap(container& v); - allocator_type get_allocator() const; - #ifdef SWIG_EXPORT_ITERATOR_METHODS class iterator; class reverse_iterator; @@ -34,17 +31,27 @@ %enddef +// Common container methods + +%define %std_container_methods(container...) + %std_container_methods_non_resizable(%arg(container)) + + void clear(); + allocator_type get_allocator() const; + +%enddef + // Common sequence %define %std_sequence_methods_common(sequence) - + %std_container_methods(%arg(sequence)); - + sequence(size_type size); void pop_back(); - + void resize(size_type new_size); - + #ifdef SWIG_EXPORT_ITERATOR_METHODS %extend { // %extend wrapper used for differing definitions of these methods introduced in C++11 @@ -52,24 +59,31 @@ iterator erase(iterator first, iterator last) { return $self->erase(first, last); } } #endif - + %enddef +%define %std_sequence_methods_non_resizable(sequence) -%define %std_sequence_methods(sequence) - - %std_sequence_methods_common(%arg(sequence)); - - sequence(size_type size, const value_type& value); - void push_back(const value_type& x); + %std_container_methods_non_resizable(%arg(sequence)) const value_type& front() const; const value_type& back() const; - - void assign(size_type n, const value_type& x); +%enddef + +%define %std_sequence_methods(sequence) + + %std_sequence_methods_common(%arg(sequence)); + + sequence(size_type size, const value_type& value); + void push_back(const value_type& x); + + const value_type& front() const; + const value_type& back() const; + + void assign(size_type n, const value_type& x); void resize(size_type new_size, const value_type& x); - + #ifdef SWIG_EXPORT_ITERATOR_METHODS %extend { // %extend wrapper used for differing definitions of these methods introduced in C++11 @@ -77,23 +91,33 @@ void insert(iterator pos, size_type n, const value_type& x) { $self->insert(pos, n, x); } } #endif - + %enddef -%define %std_sequence_methods_val(sequence...) - - %std_sequence_methods_common(%arg(sequence)); - - sequence(size_type size, value_type value); - void push_back(value_type x); +%define %std_sequence_methods_non_resizable_val(sequence...) + + %std_container_methods_non_resizable(%arg(sequence)) value_type front() const; value_type back() const; - - void assign(size_type n, value_type x); +#endif + +%enddef + +%define %std_sequence_methods_val(sequence...) + + %std_sequence_methods_common(%arg(sequence)); + + sequence(size_type size, value_type value); + void push_back(value_type x); + + value_type front() const; + value_type back() const; + + void assign(size_type n, value_type x); void resize(size_type new_size, value_type x); - + #ifdef SWIG_EXPORT_ITERATOR_METHODS %extend { // %extend wrapper used for differing definitions of these methods introduced in C++11 @@ -101,7 +125,7 @@ void insert(iterator pos, size_type n, value_type x) { $self->insert(pos, n, x); } } #endif - + %enddef diff --git a/Lib/swig.swg b/Lib/swig.swg index c33ae3854..6f48f0d20 100644 --- a/Lib/swig.swg +++ b/Lib/swig.swg @@ -359,6 +359,7 @@ static int NAME(TYPE x) { %define SWIG_TYPECHECK_STDSTRING 135 %enddef %define SWIG_TYPECHECK_STRING 140 %enddef %define SWIG_TYPECHECK_PAIR 150 %enddef +%define SWIG_TYPECHECK_STDARRAY 155 %enddef %define SWIG_TYPECHECK_VECTOR 160 %enddef %define SWIG_TYPECHECK_DEQUE 170 %enddef %define SWIG_TYPECHECK_LIST 180 %enddef