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
This commit is contained in:
William S Fulton 2012-01-24 23:41:40 +00:00
commit 7c58a044d2
3 changed files with 160 additions and 94 deletions

View file

@ -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<int>
@ -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:

View file

@ -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])

View file

@ -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 <class Sequence, class Difference>
inline typename Sequence::iterator
getpos(Sequence* self, Difference i) {
@ -239,18 +255,12 @@ namespace swig {
template <class Sequence, class Difference>
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<step && it!=se; ++c)
it++;
}
return sequence;
}
}
} else {
return new Sequence();
Sequence *sequence = new Sequence();
if (ii > 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 <class Sequence, class Difference, class InputSeq>
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<replacecount; ++rc) {
*it++ = *isit++;
for (typename Sequence::size_type c=0; c<(step-1); ++c)
it++;
}
}
} else {
size_t replacecount = (jj - ii + step - 1) / step;
if (jj > 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<replacecount; ++rc) {
*it++ = *isit++;
for (typename Sequence::size_type c=0; c<(step-1); ++c)
for (typename Sequence::size_type c=0; c<(-step-1); ++c)
it++;
}
}
@ -328,32 +364,42 @@ namespace swig {
template <class Sequence, class Difference>
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.");