Ticket #4874: base.hpp

File base.hpp, 17.4 KB (added by Bill Buklis, 11 years ago)

MSVC version check for output_iterator_tag (merge with trunk version)

Line 
1// Copyright 2002 The Trustees of Indiana University.
2
3// Use, modification and distribution is subject to the Boost Software
4// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
5// http://www.boost.org/LICENSE_1_0.txt)
6
7// Boost.MultiArray Library
8// Authors: Ronald Garcia
9// Jeremy Siek
10// Andrew Lumsdaine
11// See http://www.boost.org/libs/multi_array for documentation.
12
13#ifndef BASE_RG071801_HPP
14#define BASE_RG071801_HPP
15
16//
17// base.hpp - some implementation base classes for from which
18// functionality is acquired
19//
20
21#include "boost/multi_array/extent_range.hpp"
22#include "boost/multi_array/extent_gen.hpp"
23#include "boost/multi_array/index_range.hpp"
24#include "boost/multi_array/index_gen.hpp"
25#include "boost/multi_array/storage_order.hpp"
26#include "boost/multi_array/types.hpp"
27#include "boost/config.hpp"
28#include "boost/multi_array/concept_checks.hpp" //for ignore_unused_...
29#include "boost/mpl/eval_if.hpp"
30#include "boost/mpl/if.hpp"
31#include "boost/mpl/size_t.hpp"
32#include "boost/mpl/aux_/msvc_eti_base.hpp"
33#include "boost/iterator/reverse_iterator.hpp"
34#include "boost/static_assert.hpp"
35#include "boost/type.hpp"
36#include "boost/assert.hpp"
37#include <cstddef>
38#include <memory>
39
40namespace boost {
41
42/////////////////////////////////////////////////////////////////////////
43// class declarations
44/////////////////////////////////////////////////////////////////////////
45
46template<typename T, std::size_t NumDims,
47 typename Allocator = std::allocator<T> >
48class multi_array;
49
50// This is a public interface for use by end users!
51namespace multi_array_types {
52 typedef boost::detail::multi_array::size_type size_type;
53 typedef std::ptrdiff_t difference_type;
54 typedef boost::detail::multi_array::index index;
55 typedef detail::multi_array::index_range<index,size_type> index_range;
56 typedef detail::multi_array::extent_range<index,size_type> extent_range;
57 typedef detail::multi_array::index_gen<0,0> index_gen;
58 typedef detail::multi_array::extent_gen<0> extent_gen;
59}
60
61
62// boost::extents and boost::indices are now a part of the public
63// interface. That way users don't necessarily have to create their
64// own objects. On the other hand, one may not want the overhead of
65// object creation in small-memory environments. Thus, the objects
66// can be left undefined by defining BOOST_MULTI_ARRAY_NO_GENERATORS
67// before loading multi_array.hpp.
68#if !BOOST_MULTI_ARRAY_NO_GENERATORS
69namespace {
70 multi_array_types::extent_gen extents;
71 multi_array_types::index_gen indices;
72}
73#endif // BOOST_MULTI_ARRAY_NO_GENERATORS
74
75namespace detail {
76namespace multi_array {
77
78template <typename T, std::size_t NumDims>
79class sub_array;
80
81template <typename T, std::size_t NumDims, typename TPtr = const T*>
82class const_sub_array;
83
84 template <typename T, typename TPtr, typename NumDims, typename Reference,
85 typename IteratorCategory>
86class array_iterator;
87
88template <typename T, std::size_t NumDims, typename TPtr = const T*>
89class const_multi_array_view;
90
91template <typename T, std::size_t NumDims>
92class multi_array_view;
93
94/////////////////////////////////////////////////////////////////////////
95// class interfaces
96/////////////////////////////////////////////////////////////////////////
97
98class multi_array_base {
99public:
100 typedef multi_array_types::size_type size_type;
101 typedef multi_array_types::difference_type difference_type;
102 typedef multi_array_types::index index;
103 typedef multi_array_types::index_range index_range;
104 typedef multi_array_types::extent_range extent_range;
105 typedef multi_array_types::index_gen index_gen;
106 typedef multi_array_types::extent_gen extent_gen;
107};
108
109//
110// value_accessor_n
111// contains the routines for accessing elements from
112// N-dimensional views.
113//
114template<typename T, std::size_t NumDims>
115class value_accessor_n : public multi_array_base {
116 typedef multi_array_base super_type;
117public:
118 typedef typename super_type::index index;
119
120 //
121 // public typedefs used by classes that inherit from this base
122 //
123 typedef T element;
124 typedef boost::multi_array<T,NumDims-1> value_type;
125 typedef sub_array<T,NumDims-1> reference;
126 typedef const_sub_array<T,NumDims-1> const_reference;
127
128protected:
129 // used by array operator[] and iterators to get reference types.
130 template <typename Reference, typename TPtr>
131 Reference access(boost::type<Reference>,index idx,TPtr base,
132 const size_type* extents,
133 const index* strides,
134 const index* index_bases) const {
135
136 BOOST_ASSERT(idx - index_bases[0] >= 0);
137 BOOST_ASSERT(size_type(idx - index_bases[0]) < extents[0]);
138 // return a sub_array<T,NDims-1> proxy object
139 TPtr newbase = base + idx * strides[0];
140 return Reference(newbase,extents+1,strides+1,index_bases+1);
141
142 }
143
144 value_accessor_n() { }
145 ~value_accessor_n() { }
146};
147
148
149
150//
151// value_accessor_one
152// contains the routines for accessing reference elements from
153// 1-dimensional views.
154//
155template<typename T>
156class value_accessor_one : public multi_array_base {
157 typedef multi_array_base super_type;
158public:
159 typedef typename super_type::index index;
160 //
161 // public typedefs for use by classes that inherit it.
162 //
163 typedef T element;
164 typedef T value_type;
165 typedef T& reference;
166 typedef T const& const_reference;
167
168protected:
169 // used by array operator[] and iterators to get reference types.
170 template <typename Reference, typename TPtr>
171 Reference access(boost::type<Reference>,index idx,TPtr base,
172 const size_type* extents,
173 const index* strides,
174 const index* index_bases) const {
175
176 ignore_unused_variable_warning(index_bases);
177 ignore_unused_variable_warning(extents);
178 BOOST_ASSERT(idx - index_bases[0] >= 0);
179 BOOST_ASSERT(size_type(idx - index_bases[0]) < extents[0]);
180 return *(base + idx * strides[0]);
181 }
182
183 value_accessor_one() { }
184 ~value_accessor_one() { }
185};
186
187
188/////////////////////////////////////////////////////////////////////////
189// choose value accessor begins
190//
191
192template <typename T, std::size_t NumDims>
193struct choose_value_accessor_n {
194 typedef value_accessor_n<T,NumDims> type;
195};
196
197template <typename T>
198struct choose_value_accessor_one {
199 typedef value_accessor_one<T> type;
200};
201
202template <typename T, typename NumDims>
203struct value_accessor_generator {
204 BOOST_STATIC_CONSTANT(std::size_t, dimensionality = NumDims::value);
205
206 typedef typename
207 mpl::eval_if_c<(dimensionality == 1),
208 choose_value_accessor_one<T>,
209 choose_value_accessor_n<T,dimensionality>
210 >::type type;
211};
212
213#if BOOST_WORKAROUND(BOOST_MSVC, < 1300)
214
215struct eti_value_accessor
216{
217 typedef int index;
218 typedef int size_type;
219 typedef int element;
220 typedef int index_range;
221 typedef int value_type;
222 typedef int reference;
223 typedef int const_reference;
224};
225
226template <>
227struct value_accessor_generator<int,int>
228{
229 typedef eti_value_accessor type;
230};
231
232template <class T, class NumDims>
233struct associated_types
234 : mpl::aux::msvc_eti_base<
235 typename value_accessor_generator<T,NumDims>::type
236 >::type
237{};
238
239template <>
240struct associated_types<int,int> : eti_value_accessor {};
241
242#else
243
244template <class T, class NumDims>
245struct associated_types
246 : value_accessor_generator<T,NumDims>::type
247{};
248
249#endif
250
251//
252// choose value accessor ends
253/////////////////////////////////////////////////////////////////////////
254
255
256struct mutable_iterator_tag
257 : boost::random_access_traversal_tag, std::input_iterator_tag
258{
259#if BOOST_WORKAROUND(BOOST_MSVC, <= 1700)
260 operator std::output_iterator_tag() const
261 {
262 return std::output_iterator_tag();
263 }
264#endif
265};
266
267
268////////////////////////////////////////////////////////////////////////
269// multi_array_base
270////////////////////////////////////////////////////////////////////////
271template <typename T, std::size_t NumDims>
272class multi_array_impl_base
273 :
274#if BOOST_WORKAROUND(BOOST_MSVC, < 1300)
275 public mpl::aux::msvc_eti_base<
276 typename value_accessor_generator<T,mpl::size_t<NumDims> >::type
277 >::type
278#else
279 public value_accessor_generator<T,mpl::size_t<NumDims> >::type
280#endif
281{
282 typedef associated_types<T,mpl::size_t<NumDims> > types;
283public:
284 typedef typename types::index index;
285 typedef typename types::size_type size_type;
286 typedef typename types::element element;
287 typedef typename types::index_range index_range;
288 typedef typename types::value_type value_type;
289 typedef typename types::reference reference;
290 typedef typename types::const_reference const_reference;
291
292 template <std::size_t NDims>
293 struct subarray {
294 typedef boost::detail::multi_array::sub_array<T,NDims> type;
295 };
296
297 template <std::size_t NDims>
298 struct const_subarray {
299 typedef boost::detail::multi_array::const_sub_array<T,NDims> type;
300 };
301
302 template <std::size_t NDims>
303 struct array_view {
304 typedef boost::detail::multi_array::multi_array_view<T,NDims> type;
305 };
306
307 template <std::size_t NDims>
308 struct const_array_view {
309 public:
310 typedef boost::detail::multi_array::const_multi_array_view<T,NDims> type;
311 };
312
313 //
314 // iterator support
315 //
316 typedef array_iterator<T,T*,mpl::size_t<NumDims>,reference,
317 mutable_iterator_tag> iterator;
318 typedef array_iterator<T,T const*,mpl::size_t<NumDims>,const_reference,
319 boost::random_access_traversal_tag> const_iterator;
320
321 typedef ::boost::reverse_iterator<iterator> reverse_iterator;
322 typedef ::boost::reverse_iterator<const_iterator> const_reverse_iterator;
323
324 BOOST_STATIC_CONSTANT(std::size_t, dimensionality = NumDims);
325protected:
326
327 multi_array_impl_base() { }
328 ~multi_array_impl_base() { }
329
330 // Used by operator() in our array classes
331 template <typename Reference, typename IndexList, typename TPtr>
332 Reference access_element(boost::type<Reference>,
333 const IndexList& indices,
334 TPtr base,
335 const size_type* extents,
336 const index* strides,
337 const index* index_bases) const {
338 boost::function_requires<
339 CollectionConcept<IndexList> >();
340 ignore_unused_variable_warning(index_bases);
341 ignore_unused_variable_warning(extents);
342#if !defined(NDEBUG) && !defined(BOOST_DISABLE_ASSERTS)
343 for (size_type i = 0; i != NumDims; ++i) {
344 BOOST_ASSERT(indices[i] - index_bases[i] >= 0);
345 BOOST_ASSERT(size_type(indices[i] - index_bases[i]) < extents[i]);
346 }
347#endif
348
349 index offset = 0;
350 {
351 typename IndexList::const_iterator i = indices.begin();
352 size_type n = 0;
353 while (n != NumDims) {
354 offset += (*i) * strides[n];
355 ++n;
356 ++i;
357 }
358 }
359 return base[offset];
360 }
361
362 template <typename StrideList, typename ExtentList>
363 void compute_strides(StrideList& stride_list, ExtentList& extent_list,
364 const general_storage_order<NumDims>& storage)
365 {
366 // invariant: stride = the stride for dimension n
367 index stride = 1;
368 for (size_type n = 0; n != NumDims; ++n) {
369 index stride_sign = +1;
370
371 if (!storage.ascending(storage.ordering(n)))
372 stride_sign = -1;
373
374 // The stride for this dimension is the product of the
375 // lengths of the ranks minor to it.
376 stride_list[storage.ordering(n)] = stride * stride_sign;
377
378 stride *= extent_list[storage.ordering(n)];
379 }
380 }
381
382 // This calculates the offset to the array base pointer due to:
383 // 1. dimensions stored in descending order
384 // 2. non-zero dimension index bases
385 template <typename StrideList, typename ExtentList, typename BaseList>
386 index
387 calculate_origin_offset(const StrideList& stride_list,
388 const ExtentList& extent_list,
389 const general_storage_order<NumDims>& storage,
390 const BaseList& index_base_list)
391 {
392 return
393 calculate_descending_dimension_offset(stride_list,extent_list,
394 storage) +
395 calculate_indexing_offset(stride_list,index_base_list);
396 }
397
398 // This calculates the offset added to the base pointer that are
399 // caused by descending dimensions
400 template <typename StrideList, typename ExtentList>
401 index
402 calculate_descending_dimension_offset(const StrideList& stride_list,
403 const ExtentList& extent_list,
404 const general_storage_order<NumDims>& storage)
405 {
406 index offset = 0;
407 if (!storage.all_dims_ascending())
408 for (size_type n = 0; n != NumDims; ++n)
409 if (!storage.ascending(n))
410 offset -= (extent_list[n] - 1) * stride_list[n];
411
412 return offset;
413 }
414
415 // This is used to reindex array_views, which are no longer
416 // concerned about storage order (specifically, whether dimensions
417 // are ascending or descending) since the viewed array handled it.
418
419 template <typename StrideList, typename BaseList>
420 index
421 calculate_indexing_offset(const StrideList& stride_list,
422 const BaseList& index_base_list)
423 {
424 index offset = 0;
425 for (size_type n = 0; n != NumDims; ++n)
426 offset -= stride_list[n] * index_base_list[n];
427 return offset;
428 }
429
430 // Slicing using an index_gen.
431 // Note that populating an index_gen creates a type that encodes
432 // both the number of dimensions in the current Array (NumDims), and
433 // the Number of dimensions for the resulting view. This allows the
434 // compiler to fail if the dimensions aren't completely accounted
435 // for. For reasons unbeknownst to me, a BOOST_STATIC_ASSERT
436 // within the member function template does not work. I should add a
437 // note to the documentation specifying that you get a damn ugly
438 // error message if you screw up in your slicing code.
439 template <typename ArrayRef, int NDims, typename TPtr>
440 ArrayRef
441 generate_array_view(boost::type<ArrayRef>,
442 const boost::detail::multi_array::
443 index_gen<NumDims,NDims>& indices,
444 const size_type* extents,
445 const index* strides,
446 const index* index_bases,
447 TPtr base) const {
448
449 boost::array<index,NDims> new_strides;
450 boost::array<index,NDims> new_extents;
451
452 index offset = 0;
453 size_type dim = 0;
454 for (size_type n = 0; n != NumDims; ++n) {
455
456 // Use array specs and input specs to produce real specs.
457 const index default_start = index_bases[n];
458 const index default_finish = default_start+extents[n];
459 const index_range& current_range = indices.ranges_[n];
460 index start = current_range.get_start(default_start);
461 index finish = current_range.get_finish(default_finish);
462 index stride = current_range.stride();
463 BOOST_ASSERT(stride != 0);
464
465 // An index range indicates a half-open strided interval
466 // [start,finish) (with stride) which faces upward when stride
467 // is positive and downward when stride is negative,
468
469 // RG: The following code for calculating length suffers from
470 // some representation issues: if finish-start cannot be represented as
471 // by type index, then overflow may result.
472
473 index len;
474 if ((finish - start) / stride < 0) {
475 // [start,finish) is empty according to the direction imposed by
476 // the stride.
477 len = 0;
478 } else {
479 // integral trick for ceiling((finish-start) / stride)
480 // taking into account signs.
481 index shrinkage = stride > 0 ? 1 : -1;
482 len = (finish - start + (stride - shrinkage)) / stride;
483 }
484
485 // start marks the closed side of the range, so it must lie
486 // exactly in the set of legal indices
487 // with a special case for empty arrays
488 BOOST_ASSERT(index_bases[n] <= start &&
489 ((start <= index_bases[n]+index(extents[n])) ||
490 (start == index_bases[n] && extents[n] == 0)));
491
492#ifndef BOOST_DISABLE_ASSERTS
493 // finish marks the open side of the range, so it can go one past
494 // the "far side" of the range (the top if stride is positive, the bottom
495 // if stride is negative).
496 index bound_adjustment = stride < 0 ? 1 : 0;
497 BOOST_ASSERT(((index_bases[n] - bound_adjustment) <= finish) &&
498 (finish <= (index_bases[n] + index(extents[n]) - bound_adjustment)));
499#endif // BOOST_DISABLE_ASSERTS
500
501
502 // the array data pointer is modified to account for non-zero
503 // bases during slicing (see [Garcia] for the math involved)
504 offset += start * strides[n];
505
506 if (!current_range.is_degenerate()) {
507
508 // The stride for each dimension is included into the
509 // strides for the array_view (see [Garcia] for the math involved).
510 new_strides[dim] = stride * strides[n];
511
512 // calculate new extents
513 new_extents[dim] = len;
514 ++dim;
515 }
516 }
517 BOOST_ASSERT(dim == NDims);
518
519 return
520 ArrayRef(base+offset,
521 new_extents,
522 new_strides);
523 }
524
525
526};
527
528} // namespace multi_array
529} // namespace detail
530
531} // namespace boost
532
533#endif // BASE_RG071801_HPP