//# Array.h: A templated N-D Array class with zero origin //# Copyright (C) 1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003,2015 //# Associated Universities, Inc. Washington DC, USA, //# National Astronomical Observatory of Japan //# 2-21-1, Osawa, Mitaka, Tokyo, 181-8588, Japan. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library is distributed in the hope that it will be useful, but WITHOUT //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: Array.h 21545 2015-01-22 19:36:35Z gervandiepen $ #ifndef CASA_ARRAY_2_H #define CASA_ARRAY_2_H //# Includes #include "ArrayBase.h" #include "ArrayError.h" #include "IPosition.h" #include "MaskLogiArrFwd.h" #include "Storage.h" #include #include #include #include namespace casacore { //#Begin casa namespace // A templated N-D %Array class with zero origin. // Array is a templated, N-dimensional, %Array class. The origin is zero, // but by default indices are zero-based. This Array class is the // base class for the Vector, Matrix, and Cube subclasses. // // Indexing into the array, and positions in general, are given with IPosition // (essentially a vector of integers) objects. That is, an N-dimensional // array requires a length-N IPosition to define a position within the array. // Unlike C, indexing is done with (), not []. Also, the storage order // is the same as in FORTRAN, i.e. memory varies most rapidly with the first // index. // // // axisLengths = [1,2,3,4,5] // IPosition axisLengths(5, 1, 2, 3, 4, 5); // Array ai(axisLengths); // ai is a 5 dimensional array of // // integers; indices are 0-based // // => ai.nelements() == 120 // Array ai2(axisLengths); // The first element is at index 0 // IPosition zero(5); zero = 0; // [0,0,0,0,0] // //... // // Indexing into an N-dimensional array is relatively expensive. Normally // you will index into a Vector, Matrix, or Cube. These may be obtained from // an N-dimensional array by creating a reference, or by using an // ArrayIterator. The "shape" of the array is an IPosition which gives the // length of each axis. // // An Array may be standalone, or it may refer to another array, or to // part of another array (by refer we mean that if you change a pixel in // the current array, a pixel in the referred to array also changes, i.e. // they share underlying storage). // // One way one array can reference another is through the copy // constructor. While this might be what you want, you should // probably use the reference() member function to make it explicit. // The copy constructor is used when arguments are passed by value; // normally functions should not pass Arrays by value, rather they // should pass a reference or a const reference. On the positive // side, returning an array from a function is efficient since no // copying need be done. // // // Aside from the explicit reference() member function, a user will // most commonly encounter an array which references another array // when he takes an array slice (or section). A slice is a sub-region of // an array (which might also have a stride: every nth row, every mth column, // ...). // // IPosition lengths(3,10,20,30); // Array ai(lengths); // A 10x20x30 cube // Cube ci; // //... // ci.reference(ai1); // ci and ai now reference the same // // storage // ci(0,0,0) = 123; // Can use Cube indexing // ci.xyPlane(2) = 0; // and other member functions // IPosition zero(3,0,0,0); // assert(ai(zero) == 123); // true because ai, ci are references // //... // Array subArray; // IPosition blc(3,0,0,0), trc(3,5,5,5); // subArray.reference(ai(blc, trc)); // subArray = 10; // All of subArray, which is the // // subcube from 0,0,0 to 5,5,5 in // // ai, has the value 10. // // While the last example has an array slice referenced explicitly by another // array variable, normally the user will often only use the slice as // a temporary in an expresion, for example: // // Array array; // IPosition blc, trc, offset; // //... // // Copy from one region of the array into another // array(blc, trc) = array(blc+offset, trc+offset); // // // The Array classes are intended to operate on relatively large // amounts of data. While they haven't been extensively tuned yet, // they are relatively efficient in terms of speed. Presently they // are not space efficient -- the overhead is about 15 words. While // this will be improved (probably to about 1/2 that), these array // classes are not appropriate for very large numbers of very small // arrays. The Block class may be what you want in this circumstance. // // Element by element mathematical and logical operations are available // for arrays (defined in aips/ArrayMath.h and aips/ArrayLogical.h). // Because arithmetic and logical functions are split out, it is possible // to create an Array (and hence Vector etc) for any type T that has // a default constructor, assignment operator, and copy constructor. In // particular, Array works. // // If compiled with the preprocessor symbol AIPS_DEBUG symbol, array // consistency ("invariants") will be checked in most member // functions, and indexing will be range-checked. This should not be // defined for production runs. // // // Most of the data members and functions which are "protected" should // likely become "private". // // // //
  • Integrate into the Lattice hierarchy //
  • Factor out the common functions (shape etc) into a type-independent // base class. // template class Array : public ArrayBase { public: // Result has dimensionality of zero, and nelements is zero. Array(const Alloc& allocator = Alloc()); // Create an array of the given shape, i.e. after construction // array.ndim() == shape.nelements() and array.shape() == shape. // The origin of the Array is zero. // Storage is allocated by DefaultAllocator. // Without initPolicy parameter, the initialization of elements depends on type T. // When T is a fundamental type like int, elements are NOT initialized. // When T is a class type like casacore::Complex or std::string, elements are initialized. // This inconsistent behavior confuses programmers and make it hard to write efficient and generic code using template. // Especially when T is of type Complex or DComplex and it is unnecessary to initialize, // provide initPolicy with value NO_INIT to skip the initialization. // Therefore, it is strongly recommended to explicitly provide initPolicy parameter, explicit Array(const IPosition &shape, const Alloc& allocator = Alloc()); // Create an array of the given shape and initialize it with the // initial value. // Storage is allocated by DefaultAllocator. Array(const IPosition &shape, const T &initialValue, const Alloc& allocator = Alloc()); // This is a tag for the constructor that may be used to construct an uninitialized Array. static struct uninitializedType{} uninitialized; // Constructor to create an uninitialized array. This constructor can for example // be called with: // // Array a(shape, Array::uninitialized); // Array(const IPosition& shape, uninitializedType, const Alloc& allocator = Alloc()); // Construct a one-dimensional array from an initializer list. // Example: // // Array a({5, 6, 7, 8}); // Array(std::initializer_list list, const Alloc& allocator = Alloc()); // After construction, this and other reference the same storage. Array(const Array &other); // Source will be empty after this call. Array(Array&& source) noexcept; // Create an Array of a given shape from a pointer. // If policy is COPY, storage of a new copy is allocated by DefaultAllocator. // If policy is TAKE_OVER, storage will be destructed and released by the specified allocator. // // FILE *fp = ...; // typedef DefaultAllocator Alloc; // Alloc::type alloc; // IPosition shape(1, 10); // int *ptr = alloc.allocate(shape.product()); // size_t nread = fread(ptr, sizeof(int), shape.product(), fp); // Array ai(shape, ptr, TAKE_OVER, Alloc::value); // Array(const IPosition &shape, T *storage, StorageInitPolicy policy = COPY, const Alloc& allocator = Alloc()); // Create an Array of a given shape from a pointer. Because the pointer // is const, a copy is always made. // The copy is allocated by DefaultAllocator. Array(const IPosition &shape, const T *storage); // Construct an array from an iterator and a shape. template Array(const IPosition &shape, InputIterator startIter, const Alloc& allocator = Alloc()); // Frees up storage only if this array was the last reference to it. virtual ~Array() noexcept; // Make an empty array of the same template type. virtual std::unique_ptr makeArray() const override; // Retrieve the allocator associated with this array. // @{ Alloc& allocator() { return *data_p; } const Alloc& allocator() const { return *data_p; } // @} // Assign the other array to this array. // If the shapes mismatch, this array is resized. // void assign (const Array& other); void assignBase (const ArrayBase& other, bool checkType=true) override; // // Set every element of the array to "value." Also could use the // assignment operator which assigns an array from a scalar. void set(const T &value); // Apply the function to every element of the array. This modifies // the array in place. // (TODO this version made the other versions of apply() redundant) template void apply(Callable function); // After invocation, this array and other reference the same storage. That // is, modifying an element through one will show up in the other. The // arrays appear to be identical; they have the same shape. //
    Please note that this function makes it possible to reference a // const Array, thus effectively it makes a const Array non-const. // Although this may seem undesirable at first sight, it is necessary to // be able to make references to temporary Array objects, in particular to // Array slices. Otherwise one first needs to use the copy constructor. //# The const has been introduced on 2005-Mar-31 because of the hassle //# involved in calling the copy ctor before reference. virtual void reference(const Array &other); // Copy the values in other to this. If the array on the left hand // side has no elements, then it is resized to be the same size as // as the array on the right hand side. Otherwise, the arrays must // conform (same shapes). // // IPosition shape(2,10,10); // some shape // Array ad(shape); // //... // Array ad2; // N.B. ad2.nelements() == 0 // ad2 = ad; // ad2 resizes, then elements // // are copied. // shape = 20; // Array ad3(shape); // ad3 = ad; // Error: arrays do not conform // // Note that the assign function can be used to assign a // non-conforming array. Array& assign_conforming(const Array& other) { return assign_conforming_implementation(other, std::is_copy_assignable()); } // Copy to this those values in marray whose corresponding elements // in marray's mask are true. // //
  • ArrayConformanceError // // template Array& assign_conforming (const MaskedArray& marray); // TODO we should change the semantics Array& operator=(const Array& other) { return assign_conforming(other); } // Calls assign_conforming(). template Array& operator=(const MaskedArray& marray) { return assign_conforming(marray); } // The move operator takes the storage from the given array. After moving an // Array, the source Array will be left empty. Array& operator=(Array&& other); // Set every element of this array to "value". In other words, a scalar // behaves as if it were a constant conformant array. Array& operator=(const T& value); // This makes a copy of the array and returns it. This can be // useful for, e.g. making working copies of function arguments // that you can write into. // // void someFunction(const Array &arg) // { // Array tmp(arg.copy()); // // ... // } // // Note that since the copy constructor makes a reference, if we just // created used to copy constructor, modifying "tmp" would also // modify "arg". Clearly another alternative would simply be: // // void someFunction(const Array &arg) // { // Array tmp; // tmp = arg; // // ... // } // // which likely would be simpler to understand. (Should copy() // be deprecated and removed?) // // TODO deprecate Array copy() const { // Make a copy of this return copy(Alloc()); } // This function copies the matching part of from array to this array. // The matching part is the part with the minimum size for each axis. // E.g. if this array has shape [4,5,6] and from array has shape [7,3], // the matching part has shape [4,3]. //
    Note it is used by the resize function if // copyValues==true. void copyMatchingPart (const Array& from); // This ensures that this array does not reference any other storage. // // When a section is taken of an array with non-unity strides, // storage can be wasted if the array, which originally contained // all the data, goes away. unique() also reclaims storage. This // is an optimization users don't normally need to understand. // // // IPosition shape(...), blc(...), trc(...), inc(...); // Array af(shape); // inc = 2; // or anything > 1 // Array aSection.reference(af(blc, trc, inc)); // af.reference(anotherArray); // // aSection now references storage that has a stride // // in it, but nothing else is. Storage is wasted. // aSection.unique(); // // void unique(); // Create an STL vector from an Array. The created vector is a linear // representation of the Array memory. See // Vector for // details of the operation and its reverse (i.e. creating a // Vector from a vector), and for details of // definition and instantiation. // template void tovector(std::vector& out) const; std::vector tovector() const; // // It is occasionally useful to have an array which access the same // storage appear to have a different shape. For example, // turning an N-dimensional array into a Vector. //
    When the array data are contiguous, the array can be reshaped // to any form as long as the number of elements stays the same. // When not contiguous, it is only possible to remove or add axes // with length 1. // // IPosition squareShape(2,5,5); // Array square(squareShape); // IPosition lineShape(1,25); // Vector line(square.reform(lineShape)); // // "square"'s storage may now be accessed through Vector "line" // Array reform(const IPosition& shape) const; // Having an array that can be reused without requiring reallocation can // be useful for large arrays. The method reformOrResize permits this // usage. // // The reformOrResize method first attempts to reform the matrix so that // it reuses the existing storage for an array with a new shape. If the // existing storage will not hold the new shape, then the method will // resize the array when resizeIfNeeded is true; if a resize is needed and // resizeIfNeeded is false, then an ArrayConformanceError is thrown. The // copyDataIfNeeded parameter is passed to resize if resizing is performed. // resizePercentage is the percent of additional storage to be addeed when // a resize is performed; this allows the allocations to be amortized when // the caller expects to be calling this method again in the future. The // parameter is used to define an allocation shape which is larger than // the newShape by increasing the last dimension by resizePercentage percent // (i.e., lastDim = (lastDim * (100 + resizePercentage)) / 100). If // resizePercentage <= 0 then resizing uses newShape as-is. Returns true // if resizing (allocation) was performed. // // To truncate the array so that it no longer holds additional storage, // use the resize method. // // Array may not be shared with another Array object during this call. // Exception thrown if it is shared. bool reformOrResize (const IPosition & newShape, size_t resizePercentage = 0, bool resizeIfNeeded = true); // Use this method to extend or reduce the last dimension of an array. If // sufficient excess capacity exists then the bookkeeping is adjusted to // support the new shape. If insufficient storage exists then a new array // is allocated (unless resizeIfNeeded is false; then an exception is thrown). // If resizing is not required then the data remains untouched; if resizing // is required then the data is copied into the new storage. The resizePercentage // works the same as for reformOrResize (see above). This method never releases // extra storage; use "resize" to do this. Array may not be sharing storage // with another array at call time; an exception will be thrown if the array is shared. // Returns true if the array was extension required a Array::resize operation. bool adjustLastAxis (const IPosition & newShape, size_t resizePercentage = 0, bool resizeIfNeeded = true); // Returns the number of elements allocated. This value is >= to the value returned // by size(). size_t capacity () const; // These member functions remove degenerate (ie. length==1) axes from // Arrays. Only axes greater than startingAxis are considered (normally // one wants to remove trailing axes). The first two of these functions // return an Array reference with axes removed. The latter two functions // let this Array object reference the 'other' array with degenerated axes // removed. //
    // Unless throwIfError is false, an exception will be thrown if // startingAxis exceeds the array's dimensionality. //
    // The functions with argument ignoreAxes do // not consider the axes given in that argument. In this way it can be // achieved that degenerate axes are kept. // When the two functions returning void // are invoked on a derived object (e.g. Matrix), an exception is // thrown if removing the degenerate axes from other does not result // in a correct number of axes. // // Array nonDegenerate(size_t startingAxis=0, bool throwIfError=true) const; Array nonDegenerate(const IPosition& ignoreAxes) const; void nonDegenerate(const Array &other, size_t startingAxis=0, bool throwIfError=true); void nonDegenerate(const Array &other, const IPosition &ignoreAxes) { doNonDegenerate (other, ignoreAxes); } // // Remove degenerate axes from this Array object. // Note it does not make sense to use these functions on a derived object // like Matrix, because it is not possible to remove axes from them. // void removeDegenerate(size_t startingAxis=0, bool throwIfError=true); void removeDegenerate(const IPosition &ignoreAxes); // // This member function returns an Array reference with the specified // number of extra axes, all of length one, appended to the end of the // Array. Note that the reform function can also be // used to add extra axes. // const Array addDegenerate(size_t numAxes) const; Array addDegenerate(size_t numAxes); // // Make this array a different shape. If copyValues==true // the old values are copied over to the new array. // Copying is done on a per axis basis, thus a subsection with the // minimum of the old and new shape is copied. //
    Resize without argument is equal to resize(IPosition()). //
    It is important to note that if multiple Array objects // reference the same data storage, this Array object still references // the same data storage as the other Array objects if the shape does // not change. Otherwise this Array object references newly allocated // storage, while the other Array objects still reference the existing // data storage. //
    If you want to be sure that the data storage of this Array object // is not referenced by other Array objects, the function unique should // be called first. // void resize(); void resize(const IPosition &newShape, bool copyValues=false) override; // // Access a single element of the array. This is relatively // expensive. Extensive indexing should be done through one // of the Array specializations (Vector, Matrix, Cube). // T &operator()(const IPosition &); const T &operator()(const IPosition &) const; // // Get a reference to an array section extending // from start to end (inclusive). // Array operator()(const IPosition &start, const IPosition &end); const Array operator()(const IPosition &start, const IPosition &end) const; // Along the ith axis, every inc[i]'th element is chosen. Array operator()(const IPosition &start, const IPosition &end, const IPosition &inc); const Array operator()(const IPosition &start, const IPosition &end, const IPosition &inc) const; // // Get a reference to an array section using a Slicer. // Array operator()(const Slicer&); const Array operator()(const Slicer&) const; // // Get a reference to a section of an array. // This is the same as operator(), but can be used in a type-agnostic way. std::unique_ptr getSection (const Slicer&) const override; // Get the subset given by the i-th value of the last axis. So for a cube // it returns the i-th xy plane. For a Matrix it returns the i-th row. // The returned array references the original array data; its dimensionality // is one less. For a 1-dim array it still returns a 1-dim array. // This function should not be used in tight loops as it is (much) // slower than iterating using begin() and end(), ArrayIter, or // ArrayAccessor. Array operator[] (size_t i) const; // Get the diagonal of each matrix part in the full array. // The matrices are taken using axes firstAxes and firstAxis+1. // diag==0 is main diagonal; diag>0 above the main diagonal; diag<0 below. Array diagonals (size_t firstAxis=0, long long diag=0) const; // The array is masked by the input LogicalArray. // This mask must conform to the array. // const MaskedArray operator() (const LogicalArray &mask) const; MaskedArray operator() (const LogicalArray &mask); // // The array is masked by the input MaskedLogicalArray. // The mask is effectively the AND of the internal LogicalArray // and the internal mask of the MaskedLogicalArray. // The MaskedLogicalArray must conform to the array. // const MaskedArray operator() (const MaskedLogicalArray &mask) const; MaskedArray operator() (const MaskedLogicalArray &mask); // // The number of references the underlying storage has assigned to it. // It is 1 unless there are outstanding references to the storage (e.g., // through a slice). Normally you have no need to do this since the // arrays handle all of the references for you. // NB: Even when nrefs()==1, the array might be shared, because the // the storage itself might be shared. Therefore, this function should // not be used outside debugging. // TODO make protected. size_t nrefs() const; // Check to see if the Array is consistent. This is about the same thing // as checking for invariants. If AIPS_DEBUG is defined, this is invoked // after construction and on entry to most member functions. virtual bool ok() const override; // Are the shapes identical? // bool conform (const Array &other) const { return conform2(other); } bool conform (const MaskedArray &other) const; // // Get a pointer to the beginning of the array. // Note that the array may not be contiguous. // T* data() { return begin_p; } const T* data() const { return begin_p; } // // Generally use of this should be shunned, except to use a FORTRAN routine // or something similar. Because you can't know the state of the underlying // data layout (in particular, if there are increments) sometimes the // pointer returned will be to a copy, but often this won't be necessary. // A boolean is returned which tells you if this is a copy (and hence the // storage must be deleted). Note that if you don't do anything unusual, // getStorage followed by freeStorage or putStorage will do the deletion // for you (if required). e.g.: // // Array a(shape); ... // bool deleteIt; int *storage = a.getStorage(deleteIt); // foo(storage, a.nelements()); a.puStorage(storage, deleteIt); // // or a.freeStorage(storage, deleteIt) if a is const. // // NB: However, if you only use getStorage, you will have to delete the // pointer yourself using freeStorage(). // // It would probably be useful to have corresponding "copyin" "copyout" // functions that used a user supplied buffer. // Note that deleteIt is set in this function. // T *getStorage(bool& deleteIt); const T *getStorage(bool& deleteIt) const { // The cast is OK because the return pointer will be cast to const return const_cast*>(this)->getStorage(deleteIt); } void *getVStorage(bool &deleteIt) override; const void *getVStorage(bool &deleteIt) const override; // // putStorage() is normally called after a call to getStorage() (cf). // The "storage" pointer is set to zero. void putStorage(T *&storage, bool deleteAndCopy); void putVStorage(void *&storage, bool deleteAndCopy) override; // If deleteIt is set, delete "storage". Normally freeStorage calls // will follow calls to getStorage. The reason the pointer is "const" // is because only const pointers are released from const arrays. // The "storage" pointer is set to zero. // TODO this function can not be const for stateful allocators void freeStorage(const T *&storage, bool deleteIt) const; void freeVStorage(const void *&storage, bool deleteIt) const override; // Replace the data values with those in the pointer storage. // The results are undefined if storage does not point at nelements() or // more data elements. After takeStorage() is called, nrefs() // is 1. // // If policy is COPY, storage of a new copy is allocated by allocator. // If policy is TAKE_OVER, storage will be destructed and released by allocator. virtual void takeStorage(const IPosition &shape, T *storage, StorageInitPolicy policy = COPY, const Alloc& allocator = Alloc()); // Since the pointer is const, a copy is always taken. // Storage of a new copy is allocated by the specified allocator. virtual void takeStorage(const IPosition &shape, const T *storage, const Alloc& allocator = Alloc()); // // Used to iterate through Arrays. Derived classes VectorIterator and // MatrixIterator are probably more useful. friend class ArrayIterator; // Create an ArrayIterator object of the correct type. std::unique_ptr makeIterator(size_t byDim) const override; // // See the function begin() and end() for a detailed description // of the STL iterator capability. class BaseIteratorSTL { public: // Create the begin const_iterator object for an Array. explicit BaseIteratorSTL (const Array&); // Create the end const_iterator object for an Array. // It also acts as the default constructor. explicit BaseIteratorSTL (const T* end = 0) : itsPos(end), itsLineEnd(0), itsLineIncr(0), itsLineAxis(0), itsArray(0), itsContig(false) {} void nextElem() { itsPos++; if (!itsContig) { itsPos += itsLineIncr; if (itsPos > itsLineEnd) increment(); } } void nextLine() { itsPos = itsLineEnd; increment(); } bool operator== (const BaseIteratorSTL& other) const { return itsPos == other.itsPos; } bool operator!= (const BaseIteratorSTL& other) const { return itsPos != other.itsPos; } T* getPos() { return const_cast(itsPos); } friend std::ostream& operator<< (std::ostream& os, const BaseIteratorSTL& iter) { os << iter.itsPos; return os; } protected: // Increment iterator for a non-contiguous array. void increment(); const T* itsPos; const T* itsLineEnd; size_t itsLineIncr; size_t itsLineAxis; IPosition itsCurPos; IPosition itsLastPos; const Array* itsArray; bool itsContig; }; class IteratorSTL: public BaseIteratorSTL { public: // typedef T value_type; typedef value_type* pointer; typedef value_type& reference; typedef std::size_t size_type; typedef ptrdiff_t difference_type; typedef std::forward_iterator_tag iterator_category; // // Create the begin iterator object for an Array. explicit IteratorSTL (Array& arr) : BaseIteratorSTL (arr) {} // Create the end iterator object for an Array. // It also acts as the default constructor. explicit IteratorSTL (const T* end = 0) : BaseIteratorSTL (end) {} const IteratorSTL& operator++() { this->nextElem(); return *this; } IteratorSTL operator++(int) { IteratorSTL old(*this); this->nextElem(); return old; } T& operator*() { return *this->getPos(); } T* operator->() { return this->getPos(); } }; class ConstIteratorSTL: public BaseIteratorSTL { public: // typedef T value_type; typedef const value_type* pointer; typedef const value_type& reference; typedef std::size_t size_type; typedef ptrdiff_t difference_type; typedef std::forward_iterator_tag iterator_category; // // Create the begin const_iterator object for an Array. explicit ConstIteratorSTL (const Array& arr) : BaseIteratorSTL (arr) {} // Create the end const_iterator object for an Array. // It also acts as the default constructor. explicit ConstIteratorSTL (const T* end = 0) : BaseIteratorSTL (end) {} // Create from a non-const iterator. ConstIteratorSTL (const IteratorSTL& iter) : BaseIteratorSTL (iter) {} const ConstIteratorSTL& operator++() { this->nextElem(); return *this; } ConstIteratorSTL operator++(int) { ConstIteratorSTL old(*this); this->nextElem(); return old; } const T& operator*() const { return *this->itsPos; } const T* operator->() { return this->itsPos; } const T* pos() const { return this->itsPos; } }; // // Define the STL-style iterator functions (only forward iterator). // It makes it possible to iterate through all data elements of an array // and to use it common STL functions. // The end() function is relatively expensive, so it should not be // used inside a for statement. It is much better to call it beforehand // as shown in the example below. Furthermore it is very important to // use ++iter, because iter++ is 4 times slower. // // Array arr(shape); // Array::iterator iterend(arr.end()); // for (Array::iterator iter=arr.begin(); iter!=iterend; ++iter) { // *iter += 1; // } // // The Array class supports random access, so in principle a random // iterator could be implemented, but its performance would not be great, // especially for non-contiguous arrays. //
    Some other STL like functions exist for performance reasons. // If the array is contiguous, it is possible to use the // cbegin and cend functions which are // about 10% faster. // // STL-style typedefs. // // Type of allocator used to allocate and deallocate space typedef Alloc allocator_type; // Element type typedef T value_type; // TODO This is how std containers define a reference type, but // the name 'reference' is already taken by a method. // typedef T& reference; typedef const T& const_reference; // Pointer to an element type typedef T* pointer; // Constant pointer to the element type typedef const T* const_pointer; typedef IteratorSTL iterator; typedef ConstIteratorSTL const_iterator; typedef T* contiter; typedef const T* const_contiter; // // Get the begin iterator object for any array. // iterator begin() { return iterator (*this); } const_iterator begin() const { return const_iterator (*this); } iterator end() { return iterator(end_p); } const_iterator end() const { return const_iterator(end_p); } // // Get the begin iterator object for a contiguous array. // contiter cbegin() { return begin_p; } const_contiter cbegin() const { return begin_p; } contiter cend() { return end_p; } const_contiter cend() const { return end_p; } // // private: // Implementation of constructor taking a Shape, a Templated parameter and an allocator. // This method implements it for when T is integral, in which case the templated parameter // is the initial value. template Array(const IPosition &shape, Integral startIter, const Alloc& allocator, std::true_type /*is_integral*/ ); // Implementation of constructor taking a Shape, a Templated parameter and an allocator. // This method implements it for when T is NOT integral, in which case the templated parameter // is an iterator. template Array(const IPosition &shape, InputIterator startIter, const Alloc& allocator, std::false_type /*is_integral*/ ); // Makes a copy using the allocator. Array copy(const Alloc& allocator) const; // Implementation for assign for copyable types Array& assign_conforming_implementation (const Array& other, std::true_type); // Implementation for assign for non-copyable types: can not be assigned Array& assign_conforming_implementation (const Array&, std::false_type) { throw ArrayError("Can not assign from non-copyable object"); } static void copyToContiguousStorage(T *dst, Array const& src, std::true_type); static void copyToContiguousStorage(T*, Array const&, std::false_type) { throw ArrayError("Can not coy from non-copyable object"); } // An Array is unique when the container is shared and when nrefs==1. bool isUnique() const { return !data_p->is_shared() && nrefs()==1; } protected: // Source will be empty with given shape after this call. Array(Array&& source, const IPosition& shapeForSource) noexcept; template friend void swap(Array& left, Array& right); // Swap this array with another array. // Normally, casacore::swap() should be used instead. void swap(Array& other); // pre/post processing hook of takeStorage() for subclasses. virtual void preTakeStorage(const IPosition &) {} virtual void postTakeStorage() {} // This function is called when this array is about to be resized, before // any work is done. Subclasses can throw an exception if the size doesn't // match, e.g. if a Matrix is resized to have 3 dimensions. // Before this function existed, assign-like functions had to be virtual. // However, for non-copyable types, assign can't exist. This is fine for // non-virtual methods (they just can't be called), but virtual methods // cause the who class to fail to be instantiatable. void checkBeforeResize(const IPosition &newShape) { if(fixedDimensionality() !=0 && newShape.size() != fixedDimensionality()) throw(ArrayNDimError(fixedDimensionality(), newShape.size(), std::string("Invalid size given to ") + typeid(*this).name() + ": should have dimensionality of " + std::to_string(fixedDimensionality()))); } // Subclasses can return their dimensionality. The Array class will make sure // that whenever it is resized, the dimensionality is checked. // Array's constructors do not check the dimensionality, because the subclass // hasn't been created yet at that point. Subclasses should therefore make // sure to call the constructors appropriately. // For classes that return 0, any resize will be allowed. virtual size_t fixedDimensionality() const { return 0; } virtual void checkAssignableType(ArrayBase& arrayBase) const { const Array* pa = dynamic_cast*>(&arrayBase); if (pa == nullptr) { throw ArrayError("ArrayBase& has incorrect template type"); } } static void copyToContiguousStorage(T *dst, Array const& src) { copyToContiguousStorage(dst, src, std::is_copy_assignable()); } // Remove the degenerate axes from the Array object. // This is the implementation of the nonDegenerate functions. // It has a different name to be able to make it virtual without having // the "hide virtual function" message when compiling derived classes. virtual void doNonDegenerate(const Array &other, const IPosition &ignoreAxes); // Shared pointer to a Storage that contains the data. std::shared_ptr> data_p; // This pointer is adjusted to point to the first element of the array. // It is not necessarily the same thing as data->storage() since // this array might be a section, e.g. have a blc which shifts us forward // into the block. T *begin_p; // The end for an STL-style iteration. T* end_p; // Fill the steps and the end for a derived class. void makeSteps() { baseMakeSteps(); this->setEndIter(); } // Set the end iterator. void setEndIter() { end_p = (nels_p==0 ? 0 : (contiguous_p ? begin_p + nels_p : begin_p + size_t(length_p(ndim()-1)) * steps_p(ndim()-1))); } }; // Swap the first array with the second. // This is more efficient than std::swap() template void swap(Array& first, Array& second) { first.swap(second); } //# Declare extern templates for often used types. extern template class Array>; extern template class Array>; extern template class Array>; extern template class Array>; extern template class Array>; extern template class Array>; extern template class Array>; extern template class Array>; extern template class Array>; }//#End casa namespace #include "ArrayStr.h" #include "Array.tcc" #endif