From 7c58a044d2f4b33067765c1c2f02ff8e3b5e33fa Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Tue, 24 Jan 2012 23:41:40 +0000 Subject: [PATCH] Add support for negative steps in Python slices on the STL containers git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/trunk@12903 626c5289-ae23-0410-ae9c-e8d60b6d4f22 --- CHANGES.current | 13 +- .../python/li_std_containers_int_runme.py | 27 ++- Lib/python/pycontainer.swg | 214 +++++++++++------- 3 files changed, 160 insertions(+), 94 deletions(-) diff --git a/CHANGES.current b/CHANGES.current index e47e934ca..79410bd38 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -5,14 +5,10 @@ See the RELEASENOTES file for a summary of changes in each release. Version 2.0.5 (in progress) =========================== -2012-01-23: klickverbot - [D] Correctly annotate function pointers with C linkage. - [D] Exception and Error have become blessed names; removed d_exception_name test case. - -2012-01-20: wsfulton +2012-01-24: wsfulton [Python] Add Python stepped slicing support to the STL wrappers (std::vector, std::list). Assigning to a slice, reading a slice and deleting a slice with steps now work. - Positive steps only at the moment. For example: + For example: %template(vector_i) std::vector @@ -22,13 +18,18 @@ Version 2.0.5 (in progress) print list(vi) del vi[3:10:3] print list(vi) + print list(vi[::-1]) gives (same behaviour as native Python sequences such as list): [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 111, 2, 333, 4, 5, 6, 7, 8, 9] [0, 111, 2, 4, 5, 7, 8] + [8, 7, 5, 4, 2, 111, 0] +2012-01-23: klickverbot + [D] Correctly annotate function pointers with C linkage. + [D] Exception and Error have become blessed names; removed d_exception_name test case. 2012-01-20: wsfulton [Python] Fix some indexing bugs in Python STL wrappers when the index is negative, eg: diff --git a/Examples/test-suite/python/li_std_containers_int_runme.py b/Examples/test-suite/python/li_std_containers_int_runme.py index 6cb9e2e4f..7611ee63e 100644 --- a/Examples/test-suite/python/li_std_containers_int_runme.py +++ b/Examples/test-suite/python/li_std_containers_int_runme.py @@ -191,10 +191,29 @@ compare_containers(ps[-1:7:1], iv[-1:7:1], il[-1:7:1]) compare_containers(ps[-1:7:2], iv[-1:7:2], il[-1:7:2]) compare_containers(ps[-6:7:2], iv[-6:7:2], il[-6:7:2]) compare_containers(ps[-100:7:2], iv[-100:7:2], il[-100:7:2]) - compare_containers(ps[::1], iv[::1], il[::1]) compare_containers(ps[::2], iv[::2], il[::2]) -#compare_containers(ps[::-1], iv[::-1], il[]) + +compare_containers(ps[::-1], iv[::-1], il[::-1]) +compare_containers(ps[6::-1], iv[6::-1], il[6::-1]) +compare_containers(ps[:-3:-1], iv[:-3:-1], il[:-3:-1]) +compare_containers(ps[:-6:-1], iv[:-6:-1], il[:-6:-1]) +compare_containers(ps[:-7:-1], iv[:-7:-1], il[:-7:-1]) +compare_containers(ps[:-8:-1], iv[:-8:-1], il[:-8:-1]) +compare_containers(ps[:-100:-1], iv[:-100:-1], il[:-100:-1]) +compare_containers(ps[4:6:-1], iv[4:6:-1], il[4:6:-1]) +compare_containers(ps[4:5:-1], iv[4:5:-1], il[4:5:-1]) +compare_containers(ps[4:4:-1], iv[4:4:-1], il[4:4:-1]) +compare_containers(ps[4:3:-1], iv[4:3:-1], il[4:3:-1]) +compare_containers(ps[4:2:-1], iv[4:2:-1], il[4:2:-1]) +compare_containers(ps[100:104:-1], iv[100:104:-1], il[100:104:-1]) +compare_containers(ps[104:100:-1], iv[104:100:-1], il[104:100:-1]) +compare_containers(ps[-100:-104:-1], iv[-100:-104:-1], il[-100:-104:-1]) +compare_containers(ps[-104:-100:-1], iv[-104:-100:-1], il[-104:-100:-1]) +compare_containers(ps[::-2], iv[::-2], il[::-2]) +compare_containers(ps[::-3], iv[::-3], il[::-3]) +compare_containers(ps[::-4], iv[::-4], il[::-4]) +compare_containers(ps[::-5], iv[::-5], il[::-5]) # insert sequences (growing, shrinking and staying same size) @@ -217,7 +236,7 @@ for start in [-102, -100, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 10 container_delete_step(start, None, None) for end in [-102, -100, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 100, 102]: container_delete_step(start, end, None) - for step in range(1,7): + for step in range(-7,7): container_delete_step(start, end, step) ps = range(6) @@ -227,7 +246,7 @@ del ps[:]; del iv[:]; del il[:] compare_containers(ps, iv, il) for end in range(7): - for step in range(1,7): + for step in range(-7,7): for start in range(7): container_insert_step(start, end, step, [111, 222, 333, 444, 555, 666, 777]) container_insert_step(start, end, step, [111, 222, 333, 444, 555, 666]) diff --git a/Lib/python/pycontainer.swg b/Lib/python/pycontainer.swg index d0a4bf6df..7b69b9fd6 100644 --- a/Lib/python/pycontainer.swg +++ b/Lib/python/pycontainer.swg @@ -202,24 +202,40 @@ namespace swig { } void - slice_adjust(ptrdiff_t i, ptrdiff_t j, ptrdiff_t step, size_t size, size_t &ii, size_t &jj, bool insert = false) { - if ( i < 0 ) { - ii = 0; - } else if ( (size_t) i < size ) { - ii = (size_t) i; - } else if (insert && ((size_t) i >= size)) { - ii = size; + slice_adjust(ptrdiff_t i, ptrdiff_t j, ptrdiff_t step, size_t size, ptrdiff_t &ii, ptrdiff_t &jj, bool insert = false) { + if (step == 0) { + throw std::invalid_argument("slice step cannot be zero"); + } else if (step > 0) { + // Required range: 0 <= i < size, 0 <= j < size + if (i < 0) { + ii = 0; + } else if (i < (ptrdiff_t)size) { + ii = i; + } else if (insert && (i >= (ptrdiff_t)size)) { + ii = (ptrdiff_t)size; + } + if ( j < 0 ) { + jj = 0; + } else { + jj = (j < (ptrdiff_t)size) ? j : (ptrdiff_t)size; + } } else { - throw std::out_of_range("index out of range"); - } - if ( j < 0 ) { - jj = 0; - } else { - jj = ( (size_t) j < size ) ? ((size_t) j) : size; + // Required range: -1 <= i < size-1, -1 <= j < size-1 + if (i < -1) { + ii = -1; + } else if (i < (ptrdiff_t) size) { + ii = i; + } else if (i >= (ptrdiff_t)(size-1)) { + ii = (ptrdiff_t)(size-1); + } + if (j < -1) { + jj = -1; + } else { + jj = (j < (ptrdiff_t)size ) ? j : (ptrdiff_t)(size-1); + } } } - template inline typename Sequence::iterator getpos(Sequence* self, Difference i) { @@ -239,18 +255,12 @@ namespace swig { template inline Sequence* getslice(const Sequence* self, Difference i, Difference j, Py_ssize_t step) { - if (step == 0) - throw std::invalid_argument("slice step cannot be zero"); - - if (step < 0) - throw std::invalid_argument("negative steps not implemented"); - typename Sequence::size_type size = self->size(); - typename Sequence::size_type ii = 0; - typename Sequence::size_type jj = 0; + Difference ii = 0; + Difference jj = 0; swig::slice_adjust(i, j, step, size, ii, jj); - if (jj > ii) { + if (step > 0) { typename Sequence::const_iterator sb = self->begin(); typename Sequence::const_iterator se = self->begin(); std::advance(sb,ii); @@ -258,68 +268,94 @@ namespace swig { if (step == 1) { return new Sequence(sb, se); } else { - typename Sequence::const_iterator it = sb; Sequence *sequence = new Sequence(); + typename Sequence::const_iterator it = sb; while (it!=se) { sequence->push_back(*it); for (typename Sequence::size_type c=0; c jj) { + typename Sequence::const_reverse_iterator sb = self->rbegin(); + typename Sequence::const_reverse_iterator se = self->rbegin(); + std::advance(sb,size-ii-1); + std::advance(se,size-jj-1); + typename Sequence::const_reverse_iterator it = sb; + while (it!=se) { + sequence->push_back(*it); + for (typename Sequence::size_type c=0; c<-step && it!=se; ++c) + it++; + } + } + return sequence; } } template inline void setslice(Sequence* self, Difference i, Difference j, Py_ssize_t step, const InputSeq& is = InputSeq()) { - if (step == 0) - throw std::invalid_argument("slice step cannot be zero"); - - if (step < 0) - throw std::invalid_argument("negative steps not implemented"); - typename Sequence::size_type size = self->size(); - typename Sequence::size_type ii = 0; - typename Sequence::size_type jj = 0; + Difference ii = 0; + Difference jj = 0; swig::slice_adjust(i, j, step, size, ii, jj, true); - if (jj < ii) - jj = ii; - if (step == 1) { - size_t ssize = jj - ii; - if (ssize <= is.size()) { - // expanding/staying the same size - typename Sequence::iterator sb = self->begin(); - typename InputSeq::const_iterator isit = is.begin(); - std::advance(sb,ii); - std::advance(isit, jj - ii); - self->insert(std::copy(is.begin(), isit, sb), isit, is.end()); + if (step > 0) { + if (jj < ii) + jj = ii; + if (step == 1) { + size_t ssize = jj - ii; + if (ssize <= is.size()) { + // expanding/staying the same size + typename Sequence::iterator sb = self->begin(); + typename InputSeq::const_iterator isit = is.begin(); + std::advance(sb,ii); + std::advance(isit, jj - ii); + self->insert(std::copy(is.begin(), isit, sb), isit, is.end()); + } else { + // shrinking + typename Sequence::iterator sb = self->begin(); + typename Sequence::iterator se = self->begin(); + std::advance(sb,ii); + std::advance(se,jj); + self->erase(sb,se); + sb = self->begin(); + std::advance(sb,ii); + self->insert(sb, is.begin(), is.end()); + } } else { - // shrinking - typename Sequence::iterator sb = self->begin(); - typename Sequence::iterator se = self->begin(); - std::advance(sb,ii); - std::advance(se,jj); - self->erase(sb,se); - sb = self->begin(); - std::advance(sb,ii); - self->insert(sb, is.begin(), is.end()); + size_t replacecount = (jj - ii + step - 1) / step; + if (is.size() != replacecount) { + char msg[1024]; + sprintf(msg, "attempt to assign sequence of size %d to extended slice of size %d", is.size(), replacecount); + throw std::invalid_argument(msg); + } + typename Sequence::const_iterator isit = is.begin(); + typename Sequence::iterator it = self->begin(); + std::advance(it,ii); + for (size_t rc=0; rc ii) + jj = ii; + size_t replacecount = (ii - jj - step - 1) / -step; if (is.size() != replacecount) { char msg[1024]; sprintf(msg, "attempt to assign sequence of size %d to extended slice of size %d", is.size(), replacecount); throw std::invalid_argument(msg); } typename Sequence::const_iterator isit = is.begin(); - typename Sequence::iterator it = self->begin(); - std::advance(it,ii); + typename Sequence::reverse_iterator it = self->rbegin(); + std::advance(it,size-ii-1); for (size_t rc=0; rc inline void delslice(Sequence* self, Difference i, Difference j, Py_ssize_t step) { - if (step == 0) - throw std::invalid_argument("slice step cannot be zero"); - - if (step < 0) - throw std::invalid_argument("negative steps not implemented"); - typename Sequence::size_type size = self->size(); - typename Sequence::size_type ii = 0; - typename Sequence::size_type jj = 0; + Difference ii = 0; + Difference jj = 0; swig::slice_adjust(i, j, step, size, ii, jj, true); - if (jj > ii) { - typename Sequence::iterator sb = self->begin(); - typename Sequence::iterator se = self->begin(); - std::advance(sb,ii); - if (step == 1) { - std::advance(se,jj); - self->erase(sb,se); - } else { - typename Sequence::iterator it = sb; - std::advance(se,jj-1); - size_t delcount = (jj - ii + step - 1) / step; + if (step > 0) { + if (jj > ii) { + typename Sequence::iterator sb = self->begin(); + std::advance(sb,ii); + if (step == 1) { + typename Sequence::iterator se = self->begin(); + std::advance(se,jj); + self->erase(sb,se); + } else { + typename Sequence::iterator it = sb; + size_t delcount = (jj - ii + step - 1) / step; + while (delcount) { + it = self->erase(it); + if (it==self->end()) + break; + for (typename Sequence::size_type c=0; c<(step-1); ++c) + it++; + delcount--; + } + } + } + } else { + if (ii > jj) { + typename Sequence::reverse_iterator sb = self->rbegin(); + std::advance(sb,size-ii-1); + typename Sequence::reverse_iterator it = sb; + size_t delcount = (ii - jj - step - 1) / -step; while (delcount) { - it = self->erase(it); - if (it==self->end()) + self->erase((++it).base()); + if (it==self->rend()) break; - for (typename Sequence::size_type c=0; c<(step-1); ++c) + for (typename Sequence::size_type c=0; c<(-step-1); ++c) it++; delcount--; } @@ -747,7 +793,7 @@ namespace swig /* deprecated in Python 2 */ #if 1 - Sequence* __getslice__(difference_type i, difference_type j) throw (std::out_of_range) { + Sequence* __getslice__(difference_type i, difference_type j) throw (std::out_of_range, std::invalid_argument) { return swig::getslice(self, i, j, 1); } @@ -756,7 +802,7 @@ namespace swig swig::setslice(self, i, j, 1, v); } - void __delslice__(difference_type i, difference_type j) throw (std::out_of_range) { + void __delslice__(difference_type i, difference_type j) throw (std::out_of_range, std::invalid_argument) { swig::delslice(self, i, j, 1); } #endif @@ -791,7 +837,7 @@ namespace swig } void __setitem__(PySliceObject *slice) - throw (std::out_of_range) { + throw (std::out_of_range, std::invalid_argument) { Py_ssize_t i, j, step; if( !PySlice_Check(slice) ) { SWIG_Error(SWIG_TypeError, "Slice object expected."); @@ -802,7 +848,7 @@ namespace swig } void __delitem__(PySliceObject *slice) - throw (std::out_of_range) { + throw (std::out_of_range, std::invalid_argument) { Py_ssize_t i, j, step; if( !PySlice_Check(slice) ) { SWIG_Error(SWIG_TypeError, "Slice object expected.");