// Copyright Daniel Wallin 2006. Use, modification and distribution is // subject to the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_MULTI_ARRAY_PYTHON_060213_HPP # define BOOST_MULTI_ARRAY_PYTHON_060213_HPP # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include namespace boost { namespace multi_array_python { namespace aux { template std::string tostring(T const& x) { return boost::lexical_cast(x); } template python::list as_list(Iter first, Iter last) { python::list result; for (; first != last; ++first) result.append(*first); return result; } // // This class template wraps a multi_array iterator, changing // it's value_type to be equal to it's reference type. // // The reason for this is that multi_array::iterator has a proxy // type as it's reference type, but the value_type is multi_array. // // The way BPL exposes iterators this means the contents of the // array will be copied every time the iterator is dereferenced. // // This makes sure the proxies are copied. // template struct iterator_wrapper; template struct iterator_wrapper_base { typedef boost::iterator_adaptor< iterator_wrapper , Base , typename boost::iterator_reference::type , boost::use_default , typename boost::iterator_reference::type > type; }; template struct iterator_wrapper : iterator_wrapper_base::type { typedef typename iterator_wrapper_base::type base_type; iterator_wrapper(Base iter) : base_type(iter) {} }; long adjust_index(long index, long size) { if (index < 0) index += size; if (index < 0 || index >= size) { PyErr_SetString(PyExc_IndexError, "Index out of range"); python::throw_error_already_set(); } return index; } template void get_slice( PySliceObject* slice , T size , T& start , T& stop , T& step ) { using python::extract; long start_ = slice->start == Py_None ? 0 : extract(slice->start)(); long stop_ = slice->stop == Py_None ? size : extract(slice->stop)(); long step_ = slice->step == Py_None ? 1 : extract(slice->step)(); if (step_ < 1) { PyErr_SetString(PyExc_IndexError, "Non-positive stride"); python::throw_error_already_set(); } start_ = adjust_index(start_, size); if (stop_ < 0) stop_ += (long)size; if (stop_ < 0 || stop_ > (long)size) { PyErr_SetString(PyExc_IndexError, "Index out of range"); python::throw_error_already_set(); } if (stop_ <= start_) { PyErr_SetString(PyExc_IndexError, "Zero size slice"); python::throw_error_already_set(); } start = start_; stop = stop_; step = step_; } // // A := model of MultiArray // Dims := number of dimensions of A // template struct multi_array_concept { typedef mpl::bool_ is_rank1; typedef typename A::index_range range; typedef typename A::size_type size_type; typedef typename A::element element; typedef typename A::reference reference; typedef typename A::value_type value_type; static python::list shape(A const& ar) { return as_list(ar.shape(), ar.shape() + ar.num_dimensions()); } static python::list strides(A const& ar) { return as_list(ar.strides(), ar.strides() + ar.num_dimensions()); } static python::list index_bases(A const& ar) { return as_list(ar.index_bases(), ar.index_bases() + ar.num_dimensions()); } struct get_item_visitor { template void operator()(T const& x) { result = python::object(x); } python::object result; }; static python::object get_item(A& ar, python::object const& index) { get_item_visitor visitor; visit_index(ar, index, visitor); return visitor.result; } // // Helper for set_view_aux() below. Recursively iterates through all // the elements of a MultiArray, and assigns from values. // template static void set_view_aux0(V const& view_, python::object const& values, long& index) { V& view = const_cast(view_); for (typename V::iterator i(view.begin()), e(view.end()); i != e; ++i) { set_view_aux0(*i, values, index); } } static void set_view_aux0( element& ref, python::object const& values, long& index ) { ref = python::extract(values[index++]); } template static void set_view_aux(V const& view, PyObject* values_) { python::object values(python::borrowed(values_)); if (values.attr("__len__")() != view.num_elements()) { PyErr_SetString(PyExc_TypeError, "Cannot change array size in assign"); python::throw_error_already_set(); } long index = 0; set_view_aux0(view, values, index); } struct set_item_visitor { set_item_visitor(PyObject* value) : value(value) {} void operator()(element& item) { item = python::extract(value); } template void operator()(V const& view) { set_view_aux(view, value); } PyObject* value; }; static void set_item(A& ar, python::object const& index, PyObject* value) { set_item_visitor visitor(value); visit_index(ar, index, visitor); } template static void visit_index_step( A& src , python::object const& index_tuple , Indices const& indices , boost::array& index_array , Visitor& visitor , mpl::long_ , mpl::long_ ) { python::object index = index_tuple[Dims - N]; size_type const extent = src.shape()[Dims - N]; if (PySlice_Check(index.ptr())) { PySliceObject* slice = (PySliceObject*)index.ptr(); size_type start; size_type stop; size_type step; get_slice(slice, extent, start, stop, step); visit_index_step( src , index_tuple , indices[range(start,stop,step)] , index_array , visitor , mpl::long_() , mpl::long_() ); } else { long idx = adjust_index(python::extract(index), extent); index_array[Dims - N] = (size_type)idx; visit_index_step( src , index_tuple , indices[idx] , index_array , visitor , mpl::long_() , mpl::long_() ); } } template static void visit_index_step( A& src , python::object const& , Indices const& indices , boost::array& , Visitor& visitor , mpl::long_<0> , mpl::long_ ) { visitor(src[indices]); } template static void visit_index_step( A& src , python::object const& , Indices const& , boost::array& index_array , Visitor& visitor , mpl::long_<0> , mpl::long_<0> ) { visitor(src(index_array)); } template static void visit_index(A& src, python::object index, Visitor& visitor) { python::extract idx(index); if (idx.check()) { visitor(src[idx()]); } else { if (PySlice_Check(index.ptr())) index = python::make_tuple(index); if (index.attr("__len__")() != Dims) { PyErr_SetString(PyExc_IndexError, "Index list size mismatch"); python::throw_error_already_set(); } boost::array index_array; visit_index_step( src , index , boost::indices , index_array , visitor , mpl::long_() , mpl::long_() ); } } typedef typename A::iterator base_iterator; typedef typename mpl::if_< is_rank1, base_iterator, iterator_wrapper >::type iterator; static iterator begin(A& ar) { return ar.begin(); } static iterator end(A& ar) { return ar.end(); } static python::class_& class_(char const* name) { using namespace python; static python::class_ cl(name, no_init); cl .def("__iter__", python::range(begin, end)) .def("__len__", &A::size) .def("__getitem__", get_item) .def("__setitem__", set_item) .add_property("shape", shape) .add_property("strides", strides) .add_property("index_bases", index_bases) .add_property("dimensions", &A::num_dimensions) .add_property("num_elements", &A::num_elements) .def(self == self) .def(self <= self) .def(self >= self) .def(self < self) .def(self > self) ; return cl; } }; template struct multi_array_pickle_suite : python::pickle_suite { static python::tuple getinitargs(A const& ar) { return python::make_tuple( as_list(ar.shape(), ar.shape() + ar.num_dimensions()) ); } static python::tuple getstate(A const& ar) { return python::make_tuple( as_list(ar.data(), ar.data() + ar.num_elements()) ); } static void setstate(A& ar, python::tuple const& state) { if (state.attr("__len__")() != 1) { PyErr_SetObject(PyExc_ValueError, ("expected 1-item tuple in call to __setstate__; got %s" % state).ptr() ); python::throw_error_already_set(); } python::object data = state[0]; if (data.attr("__len__")() != ar.num_elements()) { PyErr_SetString(PyExc_ValueError, "pickled data size mismatch"); python::throw_error_already_set(); } for (typename A::size_type i = 0; i < ar.num_elements(); ++i) { ar.data()[i] = python::extract(data[i]); } } }; template struct multi_array_class { typedef typename A::size_type size_type; typedef typename A::value_type value_type; static A* init(python::object const& extent_sequence) { if (extent_sequence.attr("__len__")() != N) { PyErr_SetString(PyExc_ValueError, "Extents list size mismatch"); python::throw_error_already_set(); } boost::array extents; for (long i = 0; i < N; ++i) { long extent = python::extract(extent_sequence[i]); if (extent < 1) { PyErr_SetString(PyExc_ValueError, "Non-positive extent"); python::throw_error_already_set(); } extents[i] = extent; } return new A(extents); } static void reshape(A& ar, python::object const& size_sequence) { if (size_sequence.attr("__len__")() != N) { PyErr_SetString(PyExc_ValueError, "Size list size mismatch"); python::throw_error_already_set(); } boost::array sizes; size_type num_elements(1); for (long i = 0; i < N; ++i) { long size = python::extract(size_sequence[i]); if (size < 1) { PyErr_SetString(PyExc_ValueError, "Non-positive size"); python::throw_error_already_set(); } sizes[i] = size; num_elements *= size; } if (num_elements != ar.num_elements()) { PyErr_SetString(PyExc_TypeError, "Cannot change size of array"); python::throw_error_already_set(); } ar.reshape(sizes); } struct wrap_subarray_view { wrap_subarray_view(char const* value_name) : value_name(value_name) {} template void operator()(mpl::integral_c) const { std::string const suffix = tostring(M) + "_" + value_name; multi_array_concept< typename A::template array_view::type, M >::class_(("view" + suffix).c_str()); multi_array_concept< typename A::template subarray::type, M >::class_(("subarray" + suffix).c_str()); } char const* value_name; }; multi_array_class(char const* value_name = python::type_id().name()) { std::string const suffix = tostring(N) + "_" + value_name; multi_array_concept::class_(("multi_array" + suffix).c_str()) .def("__init__", python::make_constructor(init)) .def("reshape", reshape) .def_pickle(multi_array_pickle_suite()) ; mpl::for_each >( wrap_subarray_view(value_name) ); } }; } // namespace aux template struct wrap_multi_array { typedef boost::multi_array array_type; wrap_multi_array( char const* value_name = python::type_id().name() ) { aux::multi_array_class class_(value_name); } }; }} // namespace boost::multi_array_python #endif // BOOST_MULTI_ARRAY_PYTHON_060213_HPP