Chris@16: // Copyright 2002 The Trustees of Indiana University. Chris@16: Chris@16: // Use, modification and distribution is subject to the Boost Software Chris@16: // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at Chris@16: // http://www.boost.org/LICENSE_1_0.txt) Chris@16: Chris@16: // Boost.MultiArray Library Chris@16: // Authors: Ronald Garcia Chris@16: // Jeremy Siek Chris@16: // Andrew Lumsdaine Chris@16: // See http://www.boost.org/libs/multi_array for documentation. Chris@16: Chris@16: #ifndef BASE_RG071801_HPP Chris@16: #define BASE_RG071801_HPP Chris@16: Chris@16: // Chris@16: // base.hpp - some implementation base classes for from which Chris@16: // functionality is acquired Chris@16: // Chris@16: Chris@16: #include "boost/multi_array/extent_range.hpp" Chris@16: #include "boost/multi_array/extent_gen.hpp" Chris@16: #include "boost/multi_array/index_range.hpp" Chris@16: #include "boost/multi_array/index_gen.hpp" Chris@16: #include "boost/multi_array/storage_order.hpp" Chris@16: #include "boost/multi_array/types.hpp" Chris@16: #include "boost/config.hpp" Chris@16: #include "boost/multi_array/concept_checks.hpp" //for ignore_unused_... Chris@16: #include "boost/mpl/eval_if.hpp" Chris@16: #include "boost/mpl/if.hpp" Chris@16: #include "boost/mpl/size_t.hpp" Chris@16: #include "boost/iterator/reverse_iterator.hpp" Chris@16: #include "boost/static_assert.hpp" Chris@16: #include "boost/type.hpp" Chris@16: #include "boost/assert.hpp" Chris@16: #include Chris@16: #include Chris@16: Chris@16: namespace boost { Chris@16: Chris@16: ///////////////////////////////////////////////////////////////////////// Chris@16: // class declarations Chris@16: ///////////////////////////////////////////////////////////////////////// Chris@16: Chris@16: template > Chris@16: class multi_array; Chris@16: Chris@16: // This is a public interface for use by end users! Chris@16: namespace multi_array_types { Chris@16: typedef boost::detail::multi_array::size_type size_type; Chris@16: typedef std::ptrdiff_t difference_type; Chris@16: typedef boost::detail::multi_array::index index; Chris@16: typedef detail::multi_array::index_range index_range; Chris@16: typedef detail::multi_array::extent_range extent_range; Chris@16: typedef detail::multi_array::index_gen<0,0> index_gen; Chris@16: typedef detail::multi_array::extent_gen<0> extent_gen; Chris@16: } Chris@16: Chris@16: Chris@16: // boost::extents and boost::indices are now a part of the public Chris@16: // interface. That way users don't necessarily have to create their Chris@16: // own objects. On the other hand, one may not want the overhead of Chris@16: // object creation in small-memory environments. Thus, the objects Chris@16: // can be left undefined by defining BOOST_MULTI_ARRAY_NO_GENERATORS Chris@16: // before loading multi_array.hpp. Chris@101: #ifndef BOOST_MULTI_ARRAY_NO_GENERATORS Chris@16: namespace { Chris@16: multi_array_types::extent_gen extents; Chris@16: multi_array_types::index_gen indices; Chris@16: } Chris@16: #endif // BOOST_MULTI_ARRAY_NO_GENERATORS Chris@16: Chris@16: namespace detail { Chris@16: namespace multi_array { Chris@16: Chris@16: template Chris@16: class sub_array; Chris@16: Chris@16: template Chris@16: class const_sub_array; Chris@16: Chris@16: template Chris@16: class array_iterator; Chris@16: Chris@16: template Chris@16: class const_multi_array_view; Chris@16: Chris@16: template Chris@16: class multi_array_view; Chris@16: Chris@16: ///////////////////////////////////////////////////////////////////////// Chris@16: // class interfaces Chris@16: ///////////////////////////////////////////////////////////////////////// Chris@16: Chris@16: class multi_array_base { Chris@16: public: Chris@16: typedef multi_array_types::size_type size_type; Chris@16: typedef multi_array_types::difference_type difference_type; Chris@16: typedef multi_array_types::index index; Chris@16: typedef multi_array_types::index_range index_range; Chris@16: typedef multi_array_types::extent_range extent_range; Chris@16: typedef multi_array_types::index_gen index_gen; Chris@16: typedef multi_array_types::extent_gen extent_gen; Chris@16: }; Chris@16: Chris@16: // Chris@16: // value_accessor_n Chris@16: // contains the routines for accessing elements from Chris@16: // N-dimensional views. Chris@16: // Chris@16: template Chris@16: class value_accessor_n : public multi_array_base { Chris@16: typedef multi_array_base super_type; Chris@16: public: Chris@16: typedef typename super_type::index index; Chris@16: Chris@16: // Chris@16: // public typedefs used by classes that inherit from this base Chris@16: // Chris@16: typedef T element; Chris@16: typedef boost::multi_array value_type; Chris@16: typedef sub_array reference; Chris@16: typedef const_sub_array const_reference; Chris@16: Chris@16: protected: Chris@16: // used by array operator[] and iterators to get reference types. Chris@16: template Chris@16: Reference access(boost::type,index idx,TPtr base, Chris@16: const size_type* extents, Chris@16: const index* strides, Chris@16: const index* index_bases) const { Chris@16: Chris@16: BOOST_ASSERT(idx - index_bases[0] >= 0); Chris@16: BOOST_ASSERT(size_type(idx - index_bases[0]) < extents[0]); Chris@16: // return a sub_array proxy object Chris@16: TPtr newbase = base + idx * strides[0]; Chris@16: return Reference(newbase,extents+1,strides+1,index_bases+1); Chris@16: Chris@16: } Chris@16: Chris@16: value_accessor_n() { } Chris@16: ~value_accessor_n() { } Chris@16: }; Chris@16: Chris@16: Chris@16: Chris@16: // Chris@16: // value_accessor_one Chris@16: // contains the routines for accessing reference elements from Chris@16: // 1-dimensional views. Chris@16: // Chris@16: template Chris@16: class value_accessor_one : public multi_array_base { Chris@16: typedef multi_array_base super_type; Chris@16: public: Chris@16: typedef typename super_type::index index; Chris@16: // Chris@16: // public typedefs for use by classes that inherit it. Chris@16: // Chris@16: typedef T element; Chris@16: typedef T value_type; Chris@16: typedef T& reference; Chris@16: typedef T const& const_reference; Chris@16: Chris@16: protected: Chris@16: // used by array operator[] and iterators to get reference types. Chris@16: template Chris@16: Reference access(boost::type,index idx,TPtr base, Chris@16: const size_type* extents, Chris@16: const index* strides, Chris@16: const index* index_bases) const { Chris@16: Chris@16: ignore_unused_variable_warning(index_bases); Chris@16: ignore_unused_variable_warning(extents); Chris@16: BOOST_ASSERT(idx - index_bases[0] >= 0); Chris@16: BOOST_ASSERT(size_type(idx - index_bases[0]) < extents[0]); Chris@16: return *(base + idx * strides[0]); Chris@16: } Chris@16: Chris@16: value_accessor_one() { } Chris@16: ~value_accessor_one() { } Chris@16: }; Chris@16: Chris@16: Chris@16: ///////////////////////////////////////////////////////////////////////// Chris@16: // choose value accessor begins Chris@16: // Chris@16: Chris@16: template Chris@16: struct choose_value_accessor_n { Chris@16: typedef value_accessor_n type; Chris@16: }; Chris@16: Chris@16: template Chris@16: struct choose_value_accessor_one { Chris@16: typedef value_accessor_one type; Chris@16: }; Chris@16: Chris@16: template Chris@16: struct value_accessor_generator { Chris@16: BOOST_STATIC_CONSTANT(std::size_t, dimensionality = NumDims::value); Chris@16: Chris@16: typedef typename Chris@16: mpl::eval_if_c<(dimensionality == 1), Chris@16: choose_value_accessor_one, Chris@16: choose_value_accessor_n Chris@16: >::type type; Chris@16: }; Chris@16: Chris@16: template Chris@16: struct associated_types Chris@16: : value_accessor_generator::type Chris@16: {}; Chris@16: Chris@16: // Chris@16: // choose value accessor ends Chris@16: ///////////////////////////////////////////////////////////////////////// Chris@16: Chris@16: // Due to some imprecision in the C++ Standard, Chris@16: // MSVC 2010 is broken in debug mode: it requires Chris@16: // that an Output Iterator have output_iterator_tag in its iterator_category if Chris@16: // that iterator is not bidirectional_iterator or random_access_iterator. Chris@16: #if BOOST_WORKAROUND(BOOST_MSVC, >= 1600) Chris@16: struct mutable_iterator_tag Chris@16: : boost::random_access_traversal_tag, std::input_iterator_tag Chris@16: { Chris@16: operator std::output_iterator_tag() const { Chris@16: return std::output_iterator_tag(); Chris@16: } Chris@16: }; Chris@16: #endif Chris@16: Chris@16: //////////////////////////////////////////////////////////////////////// Chris@16: // multi_array_base Chris@16: //////////////////////////////////////////////////////////////////////// Chris@16: template Chris@16: class multi_array_impl_base Chris@16: : Chris@16: public value_accessor_generator >::type Chris@16: { Chris@16: typedef associated_types > types; Chris@16: public: Chris@16: typedef typename types::index index; Chris@16: typedef typename types::size_type size_type; Chris@16: typedef typename types::element element; Chris@16: typedef typename types::index_range index_range; Chris@16: typedef typename types::value_type value_type; Chris@16: typedef typename types::reference reference; Chris@16: typedef typename types::const_reference const_reference; Chris@16: Chris@16: template Chris@16: struct subarray { Chris@16: typedef boost::detail::multi_array::sub_array type; Chris@16: }; Chris@16: Chris@16: template Chris@16: struct const_subarray { Chris@16: typedef boost::detail::multi_array::const_sub_array type; Chris@16: }; Chris@16: Chris@16: template Chris@16: struct array_view { Chris@16: typedef boost::detail::multi_array::multi_array_view type; Chris@16: }; Chris@16: Chris@16: template Chris@16: struct const_array_view { Chris@16: public: Chris@16: typedef boost::detail::multi_array::const_multi_array_view type; Chris@16: }; Chris@16: Chris@16: // Chris@16: // iterator support Chris@16: // Chris@16: #if BOOST_WORKAROUND(BOOST_MSVC, >= 1600) Chris@16: // Deal with VC 2010 output_iterator_tag requirement Chris@16: typedef array_iterator,reference, Chris@16: mutable_iterator_tag> iterator; Chris@16: #else Chris@16: typedef array_iterator,reference, Chris@16: boost::random_access_traversal_tag> iterator; Chris@16: #endif Chris@16: typedef array_iterator,const_reference, Chris@16: boost::random_access_traversal_tag> const_iterator; Chris@16: Chris@16: typedef ::boost::reverse_iterator reverse_iterator; Chris@16: typedef ::boost::reverse_iterator const_reverse_iterator; Chris@16: Chris@16: BOOST_STATIC_CONSTANT(std::size_t, dimensionality = NumDims); Chris@16: protected: Chris@16: Chris@16: multi_array_impl_base() { } Chris@16: ~multi_array_impl_base() { } Chris@16: Chris@16: // Used by operator() in our array classes Chris@16: template Chris@16: Reference access_element(boost::type, Chris@16: const IndexList& indices, Chris@16: TPtr base, Chris@16: const size_type* extents, Chris@16: const index* strides, Chris@16: const index* index_bases) const { Chris@16: boost::function_requires< Chris@16: CollectionConcept >(); Chris@16: ignore_unused_variable_warning(index_bases); Chris@16: ignore_unused_variable_warning(extents); Chris@16: #if !defined(NDEBUG) && !defined(BOOST_DISABLE_ASSERTS) Chris@16: for (size_type i = 0; i != NumDims; ++i) { Chris@16: BOOST_ASSERT(indices[i] - index_bases[i] >= 0); Chris@16: BOOST_ASSERT(size_type(indices[i] - index_bases[i]) < extents[i]); Chris@16: } Chris@16: #endif Chris@16: Chris@16: index offset = 0; Chris@16: { Chris@16: typename IndexList::const_iterator i = indices.begin(); Chris@16: size_type n = 0; Chris@16: while (n != NumDims) { Chris@16: offset += (*i) * strides[n]; Chris@16: ++n; Chris@16: ++i; Chris@16: } Chris@16: } Chris@16: return base[offset]; Chris@16: } Chris@16: Chris@16: template Chris@16: void compute_strides(StrideList& stride_list, ExtentList& extent_list, Chris@16: const general_storage_order& storage) Chris@16: { Chris@16: // invariant: stride = the stride for dimension n Chris@16: index stride = 1; Chris@16: for (size_type n = 0; n != NumDims; ++n) { Chris@16: index stride_sign = +1; Chris@16: Chris@16: if (!storage.ascending(storage.ordering(n))) Chris@16: stride_sign = -1; Chris@16: Chris@16: // The stride for this dimension is the product of the Chris@16: // lengths of the ranks minor to it. Chris@16: stride_list[storage.ordering(n)] = stride * stride_sign; Chris@16: Chris@16: stride *= extent_list[storage.ordering(n)]; Chris@16: } Chris@16: } Chris@16: Chris@16: // This calculates the offset to the array base pointer due to: Chris@16: // 1. dimensions stored in descending order Chris@16: // 2. non-zero dimension index bases Chris@16: template Chris@16: index Chris@16: calculate_origin_offset(const StrideList& stride_list, Chris@16: const ExtentList& extent_list, Chris@16: const general_storage_order& storage, Chris@16: const BaseList& index_base_list) Chris@16: { Chris@16: return Chris@16: calculate_descending_dimension_offset(stride_list,extent_list, Chris@16: storage) + Chris@16: calculate_indexing_offset(stride_list,index_base_list); Chris@16: } Chris@16: Chris@16: // This calculates the offset added to the base pointer that are Chris@16: // caused by descending dimensions Chris@16: template Chris@16: index Chris@16: calculate_descending_dimension_offset(const StrideList& stride_list, Chris@16: const ExtentList& extent_list, Chris@16: const general_storage_order& storage) Chris@16: { Chris@16: index offset = 0; Chris@16: if (!storage.all_dims_ascending()) Chris@16: for (size_type n = 0; n != NumDims; ++n) Chris@16: if (!storage.ascending(n)) Chris@16: offset -= (extent_list[n] - 1) * stride_list[n]; Chris@16: Chris@16: return offset; Chris@16: } Chris@16: Chris@16: // This is used to reindex array_views, which are no longer Chris@16: // concerned about storage order (specifically, whether dimensions Chris@16: // are ascending or descending) since the viewed array handled it. Chris@16: Chris@16: template Chris@16: index Chris@16: calculate_indexing_offset(const StrideList& stride_list, Chris@16: const BaseList& index_base_list) Chris@16: { Chris@16: index offset = 0; Chris@16: for (size_type n = 0; n != NumDims; ++n) Chris@16: offset -= stride_list[n] * index_base_list[n]; Chris@16: return offset; Chris@16: } Chris@16: Chris@16: // Slicing using an index_gen. Chris@16: // Note that populating an index_gen creates a type that encodes Chris@16: // both the number of dimensions in the current Array (NumDims), and Chris@16: // the Number of dimensions for the resulting view. This allows the Chris@16: // compiler to fail if the dimensions aren't completely accounted Chris@16: // for. For reasons unbeknownst to me, a BOOST_STATIC_ASSERT Chris@16: // within the member function template does not work. I should add a Chris@16: // note to the documentation specifying that you get a damn ugly Chris@16: // error message if you screw up in your slicing code. Chris@16: template Chris@16: ArrayRef Chris@16: generate_array_view(boost::type, Chris@16: const boost::detail::multi_array:: Chris@16: index_gen& indices, Chris@16: const size_type* extents, Chris@16: const index* strides, Chris@16: const index* index_bases, Chris@16: TPtr base) const { Chris@16: Chris@16: boost::array new_strides; Chris@16: boost::array new_extents; Chris@16: Chris@16: index offset = 0; Chris@16: size_type dim = 0; Chris@16: for (size_type n = 0; n != NumDims; ++n) { Chris@16: Chris@16: // Use array specs and input specs to produce real specs. Chris@16: const index default_start = index_bases[n]; Chris@16: const index default_finish = default_start+extents[n]; Chris@16: const index_range& current_range = indices.ranges_[n]; Chris@16: index start = current_range.get_start(default_start); Chris@16: index finish = current_range.get_finish(default_finish); Chris@16: index stride = current_range.stride(); Chris@16: BOOST_ASSERT(stride != 0); Chris@16: Chris@16: // An index range indicates a half-open strided interval Chris@16: // [start,finish) (with stride) which faces upward when stride Chris@16: // is positive and downward when stride is negative, Chris@16: Chris@16: // RG: The following code for calculating length suffers from Chris@16: // some representation issues: if finish-start cannot be represented as Chris@16: // by type index, then overflow may result. Chris@16: Chris@16: index len; Chris@16: if ((finish - start) / stride < 0) { Chris@16: // [start,finish) is empty according to the direction imposed by Chris@16: // the stride. Chris@16: len = 0; Chris@16: } else { Chris@16: // integral trick for ceiling((finish-start) / stride) Chris@16: // taking into account signs. Chris@16: index shrinkage = stride > 0 ? 1 : -1; Chris@16: len = (finish - start + (stride - shrinkage)) / stride; Chris@16: } Chris@16: Chris@16: // start marks the closed side of the range, so it must lie Chris@16: // exactly in the set of legal indices Chris@16: // with a special case for empty arrays Chris@16: BOOST_ASSERT(index_bases[n] <= start && Chris@16: ((start <= index_bases[n]+index(extents[n])) || Chris@16: (start == index_bases[n] && extents[n] == 0))); Chris@16: Chris@16: #ifndef BOOST_DISABLE_ASSERTS Chris@16: // finish marks the open side of the range, so it can go one past Chris@16: // the "far side" of the range (the top if stride is positive, the bottom Chris@16: // if stride is negative). Chris@16: index bound_adjustment = stride < 0 ? 1 : 0; Chris@16: BOOST_ASSERT(((index_bases[n] - bound_adjustment) <= finish) && Chris@16: (finish <= (index_bases[n] + index(extents[n]) - bound_adjustment))); Chris@16: #endif // BOOST_DISABLE_ASSERTS Chris@16: Chris@16: Chris@16: // the array data pointer is modified to account for non-zero Chris@16: // bases during slicing (see [Garcia] for the math involved) Chris@16: offset += start * strides[n]; Chris@16: Chris@16: if (!current_range.is_degenerate()) { Chris@16: Chris@16: // The stride for each dimension is included into the Chris@16: // strides for the array_view (see [Garcia] for the math involved). Chris@16: new_strides[dim] = stride * strides[n]; Chris@16: Chris@16: // calculate new extents Chris@16: new_extents[dim] = len; Chris@16: ++dim; Chris@16: } Chris@16: } Chris@16: BOOST_ASSERT(dim == NDims); Chris@16: Chris@16: return Chris@16: ArrayRef(base+offset, Chris@16: new_extents, Chris@16: new_strides); Chris@16: } Chris@16: Chris@16: Chris@16: }; Chris@16: Chris@16: } // namespace multi_array Chris@16: } // namespace detail Chris@16: Chris@16: } // namespace boost Chris@16: Chris@16: #endif // BASE_RG071801_HPP