Ruby STL container ranges and slices fixes.

Access via ranges and slices now behave identically to Ruby arrays.
The fixes are mostly for out of range indices and lengths.
- Zero length slice requests return an empty container instead of nil.
- Slices which request a length greater than the size of the container
  no longer chop off the last element.
- Ranges which used to return nil now return an empty array when the
  the start element is a valid index.
This commit is contained in:
William S Fulton 2015-11-24 18:58:06 +00:00
commit cd33aba427
3 changed files with 114 additions and 42 deletions

View file

@ -39,12 +39,12 @@ iv.each_with_index { |e,i|
"iv.slice(1,2).to_s" => "12", "iv.slice(1,2).to_s" => "12",
"iv[0,-2]" => nil, "iv[0,-2]" => nil,
"iv[0,3].to_s" => "012", "iv[0,3].to_s" => "012",
"iv[0,10].to_s" => "012", "iv[0,10].to_s" => "0123",
"iv[1..2].to_s" => '12', "iv[1..2].to_s" => '12',
"iv[1..3].to_s" => '123', "iv[1..3].to_s" => '123',
"iv[1..4].to_s" => '123', "iv[1..4].to_s" => '123',
"iv[1..-2].to_s" => '12', "iv[1..-2].to_s" => '12',
"iv[2..-3]" => nil, "iv[2..-3].to_s" => '',
}.each do |k,v| }.each do |k,v|
swig_assert( "#{k} == #{v.inspect}", binding ) swig_assert( "#{k} == #{v.inspect}", binding )
end end
@ -100,6 +100,66 @@ end
iv[6] = 5 iv[6] = 5
swig_assert_equal(iv.to_s, '6239555', binding) swig_assert_equal(iv.to_s, '6239555', binding)
def failed(a, b, msg)
a = 'nil' if a == nil
b = 'nil' if b == nil
raise RuntimeError, "#{msg}: #{a} ... #{b}"
end
def compare_sequences(a, b)
if a != nil && b != nil
if a.size != b.size
failed(a, b, "different sizes")
end
for i in 0..a.size-1
failed(a, b, "elements are different") if a[i] != b[i]
end
else
unless a == nil && b == nil
failed(a, b, "only one of the sequences is nil")
end
end
end
def check_slice(i, length)
aa = [0,1,2,3]
iv = IntVector.new([0,1,2,3])
aa_slice = aa[i, length]
iv_slice = iv[i, length]
compare_sequences(aa_slice, iv_slice)
aa_slice = aa.slice(i, length)
iv_slice = iv.slice(i, length)
compare_sequences(aa_slice, iv_slice)
end
def check_range(i, j)
aa = [0,1,2,3]
iv = IntVector.new([0,1,2,3])
aa_range = aa[i..j]
iv_range = iv[i..j]
compare_sequences(aa_range, iv_range)
aa_range = aa[Range.new(i, j, true)]
iv_range = iv[Range.new(i, j, true)]
compare_sequences(aa_range, iv_range)
end
for i in -5..5
for length in -5..5
check_slice(i, length)
end
end
for i in -5..5
for j in -5..5
check_range(i, j)
end
end
dv = DoubleVector.new(10) dv = DoubleVector.new(10)
swig_assert( "dv.respond_to? :each_with_index", binding ) swig_assert( "dv.respond_to? :each_with_index", binding )

View file

@ -101,7 +101,7 @@ namespace swig {
inline Sequence* inline Sequence*
getslice(const Sequence* self, Difference i, Difference j) { getslice(const Sequence* self, Difference i, Difference j) {
typename Sequence::size_type size = self->size(); typename Sequence::size_type size = self->size();
typename Sequence::size_type ii = swig::check_index(i, size); typename Sequence::size_type ii = (i == size && j == size) ? i : swig::check_index(i, size);
typename Sequence::size_type jj = swig::slice_index(j, size); typename Sequence::size_type jj = swig::slice_index(j, size);
if (jj > ii) { if (jj > ii) {
@ -619,23 +619,28 @@ namespace swig
%extend { %extend {
VALUE slice( difference_type i, difference_type j ) throw (std::invalid_argument) VALUE slice( difference_type i, difference_type length ) throw (std::invalid_argument) {
{ if ( length < 0 )
if ( j <= 0 ) return Qnil; return Qnil;
std::size_t len = $self->size(); std::size_t len = $self->size();
if ( i < 0 ) i = len - i; if ( i < 0 ) {
j += i; if ( i + static_cast<Sequence::difference_type>(len) < 0 )
if ( static_cast<std::size_t>(j) >= len ) j = len-1; return Qnil;
else
VALUE r = Qnil; i = len + i;
try {
r = swig::from< const Sequence* >( swig::getslice(self, i, j) );
}
catch( std::out_of_range )
{
}
return r;
} }
Sequence::difference_type j = length + i;
if ( j > static_cast<Sequence::difference_type>(len) )
j = len;
VALUE r = Qnil;
try {
r = swig::from< const Sequence* >( swig::getslice(self, i, j) );
}
catch( std::out_of_range ) {
}
return r;
}
Sequence* each() Sequence* each()
@ -761,25 +766,31 @@ namespace swig
try { try {
r = swig::from< Sequence::value_type >( *(swig::cgetpos(self, i)) ); r = swig::from< Sequence::value_type >( *(swig::cgetpos(self, i)) );
} }
catch( std::out_of_range ) catch( std::out_of_range ) {
{ }
}
return r; return r;
} }
VALUE __getitem__(difference_type i, difference_type j) const throw (std::invalid_argument) { VALUE __getitem__(difference_type i, difference_type length) const throw (std::invalid_argument) {
if ( j <= 0 ) return Qnil; if ( length < 0 )
return Qnil;
std::size_t len = $self->size(); std::size_t len = $self->size();
if ( i < 0 ) i = len - i; if ( i < 0 ) {
j += i; if ( static_cast<std::size_t>(j) >= len ) j = len-1; if ( i + static_cast<Sequence::difference_type>(len) < 0 )
return Qnil;
else
i = len + i;
}
Sequence::difference_type j = length + i;
if ( j > static_cast<Sequence::difference_type>(len) )
j = len;
VALUE r = Qnil; VALUE r = Qnil;
try { try {
r = swig::from< const Sequence* >( swig::getslice(self, i, j) ); r = swig::from< const Sequence* >( swig::getslice(self, i, j) );
} }
catch( std::out_of_range ) catch( std::out_of_range ) {
{ }
}
return r; return r;
} }
@ -788,17 +799,15 @@ namespace swig
try { try {
r = swig::from< Sequence::value_type >( *(swig::cgetpos(self, i)) ); r = swig::from< Sequence::value_type >( *(swig::cgetpos(self, i)) );
} }
catch( std::out_of_range ) catch( std::out_of_range ) {
{ }
}
return r; return r;
} }
VALUE __getitem__(VALUE i) const throw (std::invalid_argument) { VALUE __getitem__(VALUE i) const throw (std::invalid_argument) {
if ( rb_obj_is_kind_of( i, rb_cRange ) == Qfalse ) if ( rb_obj_is_kind_of( i, rb_cRange ) == Qfalse ) {
{ rb_raise( rb_eTypeError, "not a valid index or range" );
rb_raise( rb_eTypeError, "not a valid index or range" ); }
}
static ID id_end = rb_intern("end"); static ID id_end = rb_intern("end");
static ID id_start = rb_intern("begin"); static ID id_start = rb_intern("begin");
@ -811,16 +820,19 @@ namespace swig
int len = $self->size(); int len = $self->size();
int s = NUM2INT( start ); int s = NUM2INT( start );
if ( s < 0 ) s = len + s; if ( s < 0 ) {
else if ( s >= len ) return Qnil; s = len + s;
if ( s < 0 )
return Qnil;
} else if ( s > len )
return Qnil;
int e = NUM2INT( end ); int e = NUM2INT( end );
if ( e < 0 ) e = len + e; if ( e < 0 ) e = len + e;
if ( e < s ) return Qnil; //std::swap( s, e );
if ( noend ) e -= 1; if ( noend ) e -= 1;
if ( e < 0 ) e = -1;
if ( e >= len ) e = len - 1; if ( e >= len ) e = len - 1;
if ( s == len ) e = len - 1;
return swig::from< Sequence* >( swig::getslice(self, s, e+1) ); return swig::from< Sequence* >( swig::getslice(self, s, e+1) );
} }

View file

@ -41,7 +41,7 @@
getslice(const std::array<T, N>* self, Difference i, Difference j) { getslice(const std::array<T, N>* self, Difference i, Difference j) {
using Sequence = std::array<T, N>; using Sequence = std::array<T, N>;
typename Sequence::size_type size = self->size(); typename Sequence::size_type size = self->size();
typename Sequence::size_type ii = swig::check_index(i, size); typename Sequence::size_type ii = (i == size && j == size) ? i : swig::check_index(i, size);
typename Sequence::size_type jj = swig::slice_index(j, size); typename Sequence::size_type jj = swig::slice_index(j, size);
if (ii == 0 && jj == size) { if (ii == 0 && jj == size) {