Add std::array container wrappers for Python

These work much like any of the other STL containers except Python slicing
is somewhat limited because the array is a fixed size. Only slices of
the full size are supported.
This commit is contained in:
William S Fulton 2015-11-17 07:27:48 +00:00
commit 55bbf68512
8 changed files with 500 additions and 57 deletions

View file

@ -531,6 +531,7 @@ CPP11_TEST_CASES = \
cpp11_initializer_list \ cpp11_initializer_list \
cpp11_initializer_list_extend \ cpp11_initializer_list_extend \
cpp11_lambda_functions \ cpp11_lambda_functions \
cpp11_li_std_array \
cpp11_noexcept \ cpp11_noexcept \
cpp11_null_pointer_constant \ cpp11_null_pointer_constant \
cpp11_raw_string_literals \ cpp11_raw_string_literals \

View file

@ -0,0 +1,53 @@
%module cpp11_li_std_array
#if defined(SWIGPYTHON)
%include <std_array.i>
%template(ArrayInt6) std::array<int, 6>;
%inline %{
std::array<int, 6> arrayOutVal() {
return { -2, -1, 0, 0, 1, 2 };
}
std::array<int, 6> & arrayOutRef() {
static std::array<int, 6> a = { -2, -1, 0, 0, 1, 2 };
return a;
}
std::array<int, 6> * arrayOutPtr() {
static std::array<int, 6> a = { -2, -1, 0, 0, 1, 2 };
return &a;
}
std::array<int, 6> arrayInVal(std::array<int, 6> myarray) {
std::array<int, 6> a = myarray;
for (auto& val : a) {
val *= 10;
}
return a;
}
const std::array<int, 6> & arrayInConstRef(const std::array<int, 6> & myarray) {
static std::array<int, 6> a = myarray;
for (auto& val : a) {
val *= 10;
}
return a;
}
void arrayInRef(std::array<int, 6> & myarray) {
for (auto& val : myarray) {
val *= 10;
}
}
void arrayInPtr(std::array<int, 6> * myarray) {
for (auto& val : *myarray) {
val *= 10;
}
}
%}
#endif

View file

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

View file

@ -252,6 +252,12 @@ namespace swig {
return pos; return pos;
} }
template <class Sequence>
inline void
erase(Sequence* seq, const typename Sequence::iterator& position) {
seq->erase(position);
}
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) {
@ -552,6 +558,7 @@ namespace swig
difference_type _index; difference_type _index;
}; };
// STL container wrapper around a Python sequence
template <class T> template <class T>
struct SwigPySequence_Cont struct SwigPySequence_Cont
{ {
@ -748,7 +755,6 @@ namespace swig
return self->size(); return self->size();
} }
} }
%enddef %enddef
@ -756,7 +762,7 @@ namespace swig
%define %swig_sequence_methods_common(Sequence...) %define %swig_sequence_methods_common(Sequence...)
%swig_sequence_iterator(%arg(Sequence)) %swig_sequence_iterator(%arg(Sequence))
%swig_container_methods(%arg(Sequence)) %swig_container_methods(%arg(Sequence))
%fragment("SwigPySequence_Base"); %fragment("SwigPySequence_Base");
#if defined(SWIGPYTHON_BUILTIN) #if defined(SWIGPYTHON_BUILTIN)
@ -769,14 +775,6 @@ namespace swig
#endif // SWIGPYTHON_BUILTIN #endif // SWIGPYTHON_BUILTIN
%extend { %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 for slice object support */
%typemap(in) PySliceObject* { %typemap(in) PySliceObject* {
if (!PySlice_Check($input)) { if (!PySlice_Check($input)) {
@ -794,7 +792,11 @@ namespace swig
return swig::getslice(self, i, j, 1); 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); swig::setslice(self, i, j, 1, v);
} }
@ -803,11 +805,10 @@ namespace swig
} }
#endif #endif
void __delitem__(difference_type i) throw (std::out_of_range) { void __delitem__(difference_type i) throw (std::out_of_range, std::invalid_argument) {
self->erase(swig::getpos(self,i)); swig::erase(self, swig::getpos(self, i));
} }
/* Overloaded methods for Python 3 compatibility /* Overloaded methods for Python 3 compatibility
* (Also useful in Python 2.x) * (Also useful in Python 2.x)
*/ */
@ -858,12 +859,11 @@ namespace swig
Sequence::difference_type jd = j; Sequence::difference_type jd = j;
swig::delslice(self, id, jd, step); swig::delslice(self, id, jd, step);
} }
}
}
%enddef %enddef
%define %swig_sequence_methods(Sequence...) %define %swig_sequence_methods_non_resizable(Sequence...)
%swig_sequence_methods_common(%arg(Sequence)) %swig_sequence_methods_common(%arg(Sequence))
%extend { %extend {
const value_type& __getitem__(difference_type i) const throw (std::out_of_range) { const value_type& __getitem__(difference_type i) const throw (std::out_of_range) {
@ -876,19 +876,32 @@ namespace swig
#if defined(SWIGPYTHON_BUILTIN) #if defined(SWIGPYTHON_BUILTIN)
// This will be called through the mp_ass_subscript slot to delete an entry. // This will be called through the mp_ass_subscript slot to delete an entry.
void __setitem__(difference_type i) throw (std::out_of_range) { void __setitem__(difference_type i) throw (std::out_of_range, std::invalid_argument) {
self->erase(swig::getpos(self,i)); swig::erase(self, swig::getpos(self, i));
} }
#endif #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) { void append(const value_type& x) {
self->push_back(x); self->push_back(x);
} }
} }
%enddef %enddef
%define %swig_sequence_methods_val(Sequence...) %define %swig_sequence_methods_non_resizable_val(Sequence...)
%swig_sequence_methods_common(%arg(Sequence)) %swig_sequence_methods_common(%arg(Sequence))
%extend { %extend {
value_type __getitem__(difference_type i) throw (std::out_of_range) { value_type __getitem__(difference_type i) throw (std::out_of_range) {
@ -901,16 +914,28 @@ namespace swig
#if defined(SWIGPYTHON_BUILTIN) #if defined(SWIGPYTHON_BUILTIN)
// This will be called through the mp_ass_subscript slot to delete an entry. // This will be called through the mp_ass_subscript slot to delete an entry.
void __setitem__(difference_type i) throw (std::out_of_range) { void __setitem__(difference_type i) throw (std::out_of_range, std::invalid_argument) {
self->erase(swig::getpos(self,i)); swig::erase(self, swig::getpos(self, i));
} }
#endif #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) { void append(value_type x) {
self->push_back(x); self->push_back(x);
} }
} }
%enddef %enddef

91
Lib/python/std_array.i Normal file
View file

@ -0,0 +1,91 @@
/*
std::array
*/
%fragment("StdArrayTraits","header",fragment="StdSequenceTraits")
%{
namespace swig {
template <class T, size_t N>
struct traits_asptr<std::array<T, N> > {
static int asptr(PyObject *obj, std::array<T, N> **vec) {
return traits_asptr_stdseq<std::array<T, N> >::asptr(obj, vec);
}
};
template <class T, size_t N>
struct traits_from<std::array<T, N> > {
static PyObject *from(const std::array<T, N>& vec) {
return traits_from_stdseq<std::array<T, N> >::from(vec);
}
};
template <class SwigPySeq, class T, size_t N>
inline void
assign(const SwigPySeq& swigpyseq, std::array<T, N>* 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 <class T, size_t N>
inline void
erase(std::array<T, N>* seq, const typename std::array<T, N>::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 <class T, size_t N, class Difference>
inline std::array<T, N>*
getslice(const std::array<T, N>* self, Difference i, Difference j, Py_ssize_t step) {
using Sequence = std::array<T, N>;
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 <class T, size_t N, class Difference, class InputSeq>
inline void
setslice(std::array<T, N>* self, Difference i, Difference j, Py_ssize_t step, const InputSeq& is = InputSeq()) {
using Sequence = std::array<T, N>;
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 <class T, size_t N, class Difference>
inline void
delslice(std::array<T, N>* 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 <std/std_array.i>

88
Lib/std/std_array.i Normal file
View file

@ -0,0 +1,88 @@
//
// std::array
//
%include <std_container.i>
%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<T, N>), f(const std::array<T, N>&):
// the parameter being read-only, either a sequence or a
// previously wrapped std::array<T, N> can be passed.
// -- f(std::array<T, N>&), f(std::array<T, N>*):
// the parameter may be modified; therefore, only a wrapped std::array
// can be passed.
// -- std::array<T, N> f(), const std::array<T, N>& 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<T, N>& f(), std::array<T, N>* f():
// the array is returned by reference; therefore, a wrapped std::array
// is returned
// -- const std::array<T, N>* f(), f(const std::array<T, N>*):
// for consistency, they expect and return a plain array pointer.
// ------------------------------------------------------------------------
%{
#include <array>
%}
// exported classes
namespace std {
template<class _Tp, size_t _Nm >
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<std::array<_Tp, _Nm > > {
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);
};
}

View file

@ -6,20 +6,17 @@
#include <algorithm> #include <algorithm>
%} %}
// Common container methods // Common non-resizable container methods
%define %std_container_methods_non_resizable(container...)
%define %std_container_methods(container...)
container(); container();
container(const container&); container(const container&);
bool empty() const; bool empty() const;
size_type size() const; size_type size() const;
void clear();
void swap(container& v); void swap(container& v);
allocator_type get_allocator() const;
#ifdef SWIG_EXPORT_ITERATOR_METHODS #ifdef SWIG_EXPORT_ITERATOR_METHODS
class iterator; class iterator;
class reverse_iterator; class reverse_iterator;
@ -34,17 +31,27 @@
%enddef %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 // Common sequence
%define %std_sequence_methods_common(sequence) %define %std_sequence_methods_common(sequence)
%std_container_methods(%arg(sequence)); %std_container_methods(%arg(sequence));
sequence(size_type size); sequence(size_type size);
void pop_back(); void pop_back();
void resize(size_type new_size); void resize(size_type new_size);
#ifdef SWIG_EXPORT_ITERATOR_METHODS #ifdef SWIG_EXPORT_ITERATOR_METHODS
%extend { %extend {
// %extend wrapper used for differing definitions of these methods introduced in C++11 // %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); } iterator erase(iterator first, iterator last) { return $self->erase(first, last); }
} }
#endif #endif
%enddef %enddef
%define %std_sequence_methods_non_resizable(sequence)
%define %std_sequence_methods(sequence) %std_container_methods_non_resizable(%arg(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& front() const;
const value_type& back() 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); void resize(size_type new_size, const value_type& x);
#ifdef SWIG_EXPORT_ITERATOR_METHODS #ifdef SWIG_EXPORT_ITERATOR_METHODS
%extend { %extend {
// %extend wrapper used for differing definitions of these methods introduced in C++11 // %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); } void insert(iterator pos, size_type n, const value_type& x) { $self->insert(pos, n, x); }
} }
#endif #endif
%enddef %enddef
%define %std_sequence_methods_val(sequence...) %define %std_sequence_methods_non_resizable_val(sequence...)
%std_sequence_methods_common(%arg(sequence)); %std_container_methods_non_resizable(%arg(sequence))
sequence(size_type size, value_type value);
void push_back(value_type x);
value_type front() const; value_type front() const;
value_type back() 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); void resize(size_type new_size, value_type x);
#ifdef SWIG_EXPORT_ITERATOR_METHODS #ifdef SWIG_EXPORT_ITERATOR_METHODS
%extend { %extend {
// %extend wrapper used for differing definitions of these methods introduced in C++11 // %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); } void insert(iterator pos, size_type n, value_type x) { $self->insert(pos, n, x); }
} }
#endif #endif
%enddef %enddef

View file

@ -359,6 +359,7 @@ static int NAME(TYPE x) {
%define SWIG_TYPECHECK_STDSTRING 135 %enddef %define SWIG_TYPECHECK_STDSTRING 135 %enddef
%define SWIG_TYPECHECK_STRING 140 %enddef %define SWIG_TYPECHECK_STRING 140 %enddef
%define SWIG_TYPECHECK_PAIR 150 %enddef %define SWIG_TYPECHECK_PAIR 150 %enddef
%define SWIG_TYPECHECK_STDARRAY 155 %enddef
%define SWIG_TYPECHECK_VECTOR 160 %enddef %define SWIG_TYPECHECK_VECTOR 160 %enddef
%define SWIG_TYPECHECK_DEQUE 170 %enddef %define SWIG_TYPECHECK_DEQUE 170 %enddef
%define SWIG_TYPECHECK_LIST 180 %enddef %define SWIG_TYPECHECK_LIST 180 %enddef