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) Version 2.0.5 (in progress)
=========================== ===========================
2012-01-23: klickverbot 2012-01-24: wsfulton
[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] Add Python stepped slicing support to the STL wrappers (std::vector, std::list). [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. 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> %template(vector_i) std::vector<int>
@ -22,13 +18,18 @@ Version 2.0.5 (in progress)
print list(vi) print list(vi)
del vi[3:10:3] del vi[3:10:3]
print list(vi) print list(vi)
print list(vi[::-1])
gives (same behaviour as native Python sequences such as list): gives (same behaviour as native Python sequences such as list):
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 111, 2, 333, 4, 5, 6, 7, 8, 9] [0, 111, 2, 333, 4, 5, 6, 7, 8, 9]
[0, 111, 2, 4, 5, 7, 8] [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 2012-01-20: wsfulton
[Python] Fix some indexing bugs in Python STL wrappers when the index is negative, eg: [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[-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[-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[-100:7:2], iv[-100:7:2], il[-100:7:2])
compare_containers(ps[::1], iv[::1], il[::1]) compare_containers(ps[::1], iv[::1], il[::1])
compare_containers(ps[::2], iv[::2], il[::2]) 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) # 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) 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]: 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) container_delete_step(start, end, None)
for step in range(1,7): for step in range(-7,7):
container_delete_step(start, end, step) container_delete_step(start, end, step)
ps = range(6) ps = range(6)
@ -227,7 +246,7 @@ del ps[:]; del iv[:]; del il[:]
compare_containers(ps, iv, il) compare_containers(ps, iv, il)
for end in range(7): for end in range(7):
for step in range(1,7): for step in range(-7,7):
for start in range(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, 777])
container_insert_step(start, end, step, [111, 222, 333, 444, 555, 666]) container_insert_step(start, end, step, [111, 222, 333, 444, 555, 666])

View file

@ -202,24 +202,40 @@ namespace swig {
} }
void void
slice_adjust(ptrdiff_t i, ptrdiff_t j, ptrdiff_t step, size_t size, size_t &ii, size_t &jj, bool insert = false) { slice_adjust(ptrdiff_t i, ptrdiff_t j, ptrdiff_t step, size_t size, ptrdiff_t &ii, ptrdiff_t &jj, bool insert = false) {
if ( i < 0 ) { if (step == 0) {
ii = 0; throw std::invalid_argument("slice step cannot be zero");
} else if ( (size_t) i < size ) { } else if (step > 0) {
ii = (size_t) i; // Required range: 0 <= i < size, 0 <= j < size
} else if (insert && ((size_t) i >= size)) { if (i < 0) {
ii = size; 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 { } else {
throw std::out_of_range("index out of range"); // Required range: -1 <= i < size-1, -1 <= j < size-1
} if (i < -1) {
if ( j < 0 ) { ii = -1;
jj = 0; } else if (i < (ptrdiff_t) size) {
} else { ii = i;
jj = ( (size_t) j < size ) ? ((size_t) j) : size; } 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> template <class Sequence, class Difference>
inline typename Sequence::iterator inline typename Sequence::iterator
getpos(Sequence* self, Difference i) { getpos(Sequence* self, Difference i) {
@ -239,18 +255,12 @@ namespace swig {
template <class Sequence, class Difference> template <class Sequence, class Difference>
inline Sequence* inline Sequence*
getslice(const Sequence* self, Difference i, Difference j, Py_ssize_t step) { 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 size = self->size();
typename Sequence::size_type ii = 0; Difference ii = 0;
typename Sequence::size_type jj = 0; Difference jj = 0;
swig::slice_adjust(i, j, step, size, ii, jj); 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 sb = self->begin();
typename Sequence::const_iterator se = self->begin(); typename Sequence::const_iterator se = self->begin();
std::advance(sb,ii); std::advance(sb,ii);
@ -258,8 +268,8 @@ namespace swig {
if (step == 1) { if (step == 1) {
return new Sequence(sb, se); return new Sequence(sb, se);
} else { } else {
typename Sequence::const_iterator it = sb;
Sequence *sequence = new Sequence(); Sequence *sequence = new Sequence();
typename Sequence::const_iterator it = sb;
while (it!=se) { while (it!=se) {
sequence->push_back(*it); sequence->push_back(*it);
for (typename Sequence::size_type c=0; c<step && it!=se; ++c) for (typename Sequence::size_type c=0; c<step && it!=se; ++c)
@ -268,58 +278,84 @@ namespace swig {
return sequence; return sequence;
} }
} else { } 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> template <class Sequence, class Difference, class InputSeq>
inline void inline void
setslice(Sequence* self, Difference i, Difference j, Py_ssize_t step, const InputSeq& is = InputSeq()) { 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 size = self->size();
typename Sequence::size_type ii = 0; Difference ii = 0;
typename Sequence::size_type jj = 0; Difference jj = 0;
swig::slice_adjust(i, j, step, size, ii, jj, true); swig::slice_adjust(i, j, step, size, ii, jj, true);
if (jj < ii) if (step > 0) {
jj = ii; if (jj < ii)
if (step == 1) { jj = ii;
size_t ssize = jj - ii; if (step == 1) {
if (ssize <= is.size()) { size_t ssize = jj - ii;
// expanding/staying the same size if (ssize <= is.size()) {
typename Sequence::iterator sb = self->begin(); // expanding/staying the same size
typename InputSeq::const_iterator isit = is.begin(); typename Sequence::iterator sb = self->begin();
std::advance(sb,ii); typename InputSeq::const_iterator isit = is.begin();
std::advance(isit, jj - ii); std::advance(sb,ii);
self->insert(std::copy(is.begin(), isit, sb), isit, is.end()); 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 { } else {
// shrinking size_t replacecount = (jj - ii + step - 1) / step;
typename Sequence::iterator sb = self->begin(); if (is.size() != replacecount) {
typename Sequence::iterator se = self->begin(); char msg[1024];
std::advance(sb,ii); sprintf(msg, "attempt to assign sequence of size %d to extended slice of size %d", is.size(), replacecount);
std::advance(se,jj); throw std::invalid_argument(msg);
self->erase(sb,se); }
sb = self->begin(); typename Sequence::const_iterator isit = is.begin();
std::advance(sb,ii); typename Sequence::iterator it = self->begin();
self->insert(sb, is.begin(), is.end()); 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 { } 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) { if (is.size() != replacecount) {
char msg[1024]; char msg[1024];
sprintf(msg, "attempt to assign sequence of size %d to extended slice of size %d", is.size(), replacecount); sprintf(msg, "attempt to assign sequence of size %d to extended slice of size %d", is.size(), replacecount);
throw std::invalid_argument(msg); throw std::invalid_argument(msg);
} }
typename Sequence::const_iterator isit = is.begin(); typename Sequence::const_iterator isit = is.begin();
typename Sequence::iterator it = self->begin(); typename Sequence::reverse_iterator it = self->rbegin();
std::advance(it,ii); std::advance(it,size-ii-1);
for (size_t rc=0; rc<replacecount; ++rc) { for (size_t rc=0; rc<replacecount; ++rc) {
*it++ = *isit++; *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++; it++;
} }
} }
@ -328,32 +364,42 @@ namespace swig {
template <class Sequence, class Difference> template <class Sequence, class Difference>
inline void inline void
delslice(Sequence* self, Difference i, Difference j, Py_ssize_t step) { 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 size = self->size();
typename Sequence::size_type ii = 0; Difference ii = 0;
typename Sequence::size_type jj = 0; Difference jj = 0;
swig::slice_adjust(i, j, step, size, ii, jj, true); swig::slice_adjust(i, j, step, size, ii, jj, true);
if (jj > ii) { if (step > 0) {
typename Sequence::iterator sb = self->begin(); if (jj > ii) {
typename Sequence::iterator se = self->begin(); typename Sequence::iterator sb = self->begin();
std::advance(sb,ii); std::advance(sb,ii);
if (step == 1) { if (step == 1) {
std::advance(se,jj); typename Sequence::iterator se = self->begin();
self->erase(sb,se); std::advance(se,jj);
} else { self->erase(sb,se);
typename Sequence::iterator it = sb; } else {
std::advance(se,jj-1); typename Sequence::iterator it = sb;
size_t delcount = (jj - ii + step - 1) / step; 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) { while (delcount) {
it = self->erase(it); self->erase((++it).base());
if (it==self->end()) if (it==self->rend())
break; break;
for (typename Sequence::size_type c=0; c<(step-1); ++c) for (typename Sequence::size_type c=0; c<(-step-1); ++c)
it++; it++;
delcount--; delcount--;
} }
@ -747,7 +793,7 @@ namespace swig
/* deprecated in Python 2 */ /* deprecated in Python 2 */
#if 1 #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); return swig::getslice(self, i, j, 1);
} }
@ -756,7 +802,7 @@ namespace swig
swig::setslice(self, i, j, 1, v); 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); swig::delslice(self, i, j, 1);
} }
#endif #endif
@ -791,7 +837,7 @@ namespace swig
} }
void __setitem__(PySliceObject *slice) void __setitem__(PySliceObject *slice)
throw (std::out_of_range) { throw (std::out_of_range, std::invalid_argument) {
Py_ssize_t i, j, step; Py_ssize_t i, j, step;
if( !PySlice_Check(slice) ) { if( !PySlice_Check(slice) ) {
SWIG_Error(SWIG_TypeError, "Slice object expected."); SWIG_Error(SWIG_TypeError, "Slice object expected.");
@ -802,7 +848,7 @@ namespace swig
} }
void __delitem__(PySliceObject *slice) void __delitem__(PySliceObject *slice)
throw (std::out_of_range) { throw (std::out_of_range, std::invalid_argument) {
Py_ssize_t i, j, step; Py_ssize_t i, j, step;
if( !PySlice_Check(slice) ) { if( !PySlice_Check(slice) ) {
SWIG_Error(SWIG_TypeError, "Slice object expected."); SWIG_Error(SWIG_TypeError, "Slice object expected.");