casa  $Rev:20696$
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
Block.h
Go to the documentation of this file.
00001 //# Block.h: Simple templated array classes
00002 //# Copyright (C) 1993-1997,2000,2002,2005
00003 //# Associated Universities, Inc. Washington DC, USA.
00004 //#
00005 //# This library is free software; you can redistribute it and/or modify it
00006 //# under the terms of the GNU Library General Public License as published by
00007 //# the Free Software Foundation; either version 2 of the License, or (at your
00008 //# option) any later version.
00009 //#
00010 //# This library is distributed in the hope that it will be useful, but WITHOUT
00011 //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00012 //# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
00013 //# License for more details.
00014 //#
00015 //# You should have received a copy of the GNU Library General Public License
00016 //# along with this library; if not, write to the Free Software Foundation,
00017 //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
00018 //#
00019 //# Correspondence concerning AIPS++ should be addressed as follows:
00020 //#        Internet email: aips2-request@nrao.edu.
00021 //#        Postal address: AIPS++ Project Office
00022 //#                        National Radio Astronomy Observatory
00023 //#                        520 Edgemont Road
00024 //#                        Charlottesville, VA 22903-2475 USA
00025 //#
00026 //# $Id: Block.h 21090 2011-06-01 10:01:28Z gervandiepen $
00027 
00028 #ifndef CASA_BLOCK_H
00029 #define CASA_BLOCK_H
00030 
00031 #include <casa/aips.h>
00033 #include <casa/Utilities/Copy.h>
00034 
00035 //# For index checking
00036 #if defined(AIPS_ARRAY_INDEX_CHECK)
00037 #include <casa/Exceptions/Error.h>
00038 #endif
00039 
00040 #include <stddef.h>   // for ptrdiff_t
00041 
00042 namespace casa { //# NAMESPACE CASA - BEGIN
00043 
00044 // <summary>simple 1-D array</summary>
00045 // <use visibility=export>
00046 //
00047 // <reviewed reviewer="UNKNOWN" date="before2004/08/25" tests="" demos="">
00048 // </reviewed>
00049 //
00050 // <etymology>
00051 //     This should be viewed as a <em>block</em> of memory without sophisticated
00052 //     manipulation functions. Thus it is called <src>Block</src>.
00053 // </etymology>
00054 //
00055 // <synopsis>
00056 // <src>Block<T></src> is a simple templated 1-D array class. Indices are always
00057 // 0-based. For efficiency reasons, no index checking is done unless the
00058 // preprocessor symbol <src>AIPS_ARRAY_INDEX_CHECK</src> is defined. 
00059 // <src>Block<T></src>'s may be assigned to and constructed from other
00060 // <src>Block<T></src>'s.
00061 // As no reference counting is done this can be an expensive operation, however.
00062 //
00063 // The net effect of this class is meant to be unsurprising to users who think
00064 // of arrays as first class objects. The name "Block" is  intended to convey
00065 // the concept of a solid "chunk" of things without any intervening "fancy"
00066 // memory management, etc. This class was written to be
00067 // used in the implementations of more functional Vector, Matrix, etc. classes,
00068 // although it is expected <src>Block<T></src> will be useful on its own.
00069 //
00070 // The Block class should be efficient. You should normally use <src>Block</src>.
00071 //
00072 // <note role=warning> If you use the assignment operator on an element of this
00073 // class, you may leave dangling references to pointers released from
00074 // <src>storage()</src>.
00075 // Resizing the array will also have this effect if the underlying storage
00076 // is actually affected.
00077 // </note>
00078 //
00079 // If index checking is turned on, an out-of-bounds index will
00080 // generate an <src>indexError<uInt></src> exception.
00081 // </synopsis>
00082 //
00083 // <example> 
00084 // <srcblock>
00085 // Block<Int> a(100,0);  // 100 ints initialized to 0
00086 // Block<Int> b;         // 0-length Block
00087 // // ...
00088 // b = a;                // resize b and copy a into it
00089 // for (size_t i=0; i < a.nelements(); i++) {
00090 //     a[i] = i;    // Generate a sequence
00091 //                  // with Vectors, could simply say "indgen(myVector);"
00092 // }
00093 // b.set(-1);       // All positions in b have the value -1
00094 // b.resize(b.nelements()*2); // Make b twice as long, by default the old
00095 //                            // elements are copied over, although this can
00096 //                            // be defeated.
00097 // some_c_function(b.storage());  // Use a fn that takes an
00098 //                                // <src>Int *</src> pointer
00099 // </srcblock> 
00100 // </example>
00101 //
00102 template<class T> class Block {
00103 public:
00104   // Create a zero-length Block. Note that any index into this Block
00105   // is an error.
00106   Block() : npts(0), array(0), destroyPointer(True) {}
00107   // Create a Block with the given number of points. The values in Block
00108   // are uninitialized. Note that indices range between 0 and n-1.
00109   explicit Block(size_t n) : npts(n), array(n>0 ? new T[n] : 0), destroyPointer(True)
00110     {}
00111   // Create a Block of the given length, and initialize (via operator= for 
00112   // objects of type T) with the provided value.
00113   Block(size_t n, T val) : npts(n), array(n > 0 ? new T[n] : 0), destroyPointer(True)
00114     { objset(array, val, n); }
00115 
00116   // Create a <src>Block</src> from a C-array (i.e. pointer). If 
00117   // <src>takeOverStorage</src> is <src>True</src>, The Block assumes that
00118   // it owns the pointer, i.e. that it is safe to <src>delet[]</src> it when
00119   // the Block is destructed, otherwise the actual storage is not destroyed.
00120   // If true, <src>storagePointer</src> is set to <src>0</src>.
00121   Block(size_t n, T *&storagePointer, Bool takeOverStorage = True)
00122     : npts(n), array(storagePointer), destroyPointer(takeOverStorage)
00123     { if (destroyPointer) storagePointer = 0;}
00124 
00125   // Copy the other block into this one. Uses copy, not reference, semantics.
00126   Block(const Block<T> &other)
00127     : npts(other.npts), array(npts > 0 ? new T[npts] : 0), destroyPointer(True)
00128     { objcopy(array, other.array, npts); }
00129   
00130   // Assign other to this. this resizes itself to the size of other, so after
00131   // the assignment, this->nelements() == other.elements() always.
00132   Block<T> &operator=(const Block<T> &other) {
00133     if (&other != this) {
00134       this->resize(other.npts, True, False);
00135       objcopy(array, other.array, npts);
00136     };
00137     return *this; }
00138   
00139   // Frees up the storage pointed contained in the Block.
00140   ~Block() { if (array && destroyPointer) { delete [] array; array = 0;} }
00141 
00142   // Resizes the Block. If <src>n == nelements()</src> resize just returns. If
00143   // a larger size is requested (<src>n > nelements()</src>) the Block always
00144   // resizes. If the requested size is smaller (<src>n < nelements()</src>),
00145   // by default the Block does not resize smaller, although it can be
00146   // forced to with <src>forceSmaller</src>. The reasoning behind this is that
00147   // often the user will just want a buffer of at least a certain size,
00148   // and won't want to pay the cost of multiple resizings.
00149   // <srcblock>
00150   // Block<float> bf(100, 0.0);
00151   // bf.resize(10);        // bf.nelements() == 100
00152   // bf.resize(10, True)   // bf.nelements() == 10
00153   // bf.resize(200)        // bf.nelements() == 200
00154   // </srcblock>
00155   // Normally the old elements are copied over (although if the
00156   // Block is lengthened the trailing elements will have undefined
00157   // values), however this can be turned off by setting copyElements to
00158   // False.
00159   //
00160   // This is written as three functions because default parameters do
00161   // not always work properly with templates.
00162   // <group>
00163   void resize(size_t n, Bool forceSmaller=False, Bool copyElements=True) {
00164     if (!(n == npts || (n < npts && forceSmaller == False))) {
00165       T *tp = n > 0 ? new T[n] : 0;
00166       if (copyElements) {
00167         size_t nmin = npts < n ? npts : n;  // Don't copy too much!
00168         objcopy(tp, array, nmin);
00169       };
00170       if (array && destroyPointer) { // delete...
00171         delete [] array;
00172         array = 0;
00173       };
00174       npts = n;
00175       destroyPointer = True;
00176       array = tp;                       // ... and swap pointer
00177     };
00178   }
00179   // </group>
00180 
00181   // Remove a single element from the Block. If forceSmaller is True this
00182   // will resize the Block and hence involve new memory allocations. This is
00183   // relatively expensive so setting forceSmaller to False is preferred. When
00184   // forcesmaller is False the Block is not resized but the elements with an
00185   // index above the removed element are shuffled down by one. For backward
00186   // compatibility forceSmaller is True by default.
00187   // <group>
00188   void remove(size_t whichOne, Bool forceSmaller=True) {
00189     if (whichOne >= npts) {
00190 #if defined(AIPS_ARRAY_INDEX_CHECK)
00191       throw(indexError<uInt>(whichOne, "Block::remove() - "
00192                              "index out of range"));
00193 #else
00194       return;
00195 #endif
00196     };
00197     if (forceSmaller == True) {
00198       T *tp = new T[npts - 1];
00199       objcopy(tp, array, whichOne);
00200       objcopy(tp+whichOne, array + whichOne + 1, npts - whichOne - 1);
00201       if (array && destroyPointer) {
00202         delete [] array;
00203         array = 0;
00204       };
00205       npts--;
00206       array = tp;
00207       destroyPointer = True;
00208     } else objmove(array+whichOne, array + whichOne + 1, npts - whichOne - 1);
00209   }
00210   // </group>
00211 
00212   // Replace the internal storage with a C-array (i.e. pointer).
00213   // If <src>takeOverStorage</src> is True, The Block assumes that it
00214   // owns the pointer, i.e. that it is safe to <src>delete[]</src> it when the 
00215   // <src>Block</src>is destructed, otherwise the actual storage is not destroyed.
00216   // If true, storagePointer is set to <src>NULL</src>.
00217   void replaceStorage(size_t n, T *&storagePointer, Bool takeOverStorage=True) {
00218     if (array && destroyPointer) {
00219       delete [] array;
00220       array = 0;
00221     };
00222     npts = n;
00223     array = storagePointer;
00224     destroyPointer = takeOverStorage;
00225     if (destroyPointer) storagePointer = 0;
00226   };
00227 
00228   // Index into the block (0-based). If the preprocessor symbol
00229   // <src>AIPS_ARRAY_INDEX_CHECK</src> is defined, index checking will be done
00230   // and an out-of-bounds index will cause an <src>indexError<uInt></src> to be
00231   // thrown. Note that valid indices range between 0 and <src>nelements()-1</src>.
00232   // <thrown>
00233   //    <li> indexError
00234   // </thrown>
00235   // <group>
00236   T &operator[](size_t index) {
00237 #if defined(AIPS_ARRAY_INDEX_CHECK)
00238     // Write it this way to avoid casts; remember index and npts are
00239     // unsigned.
00240     if ((npts == 0) || (index > npts - 1)) {
00241       throw(indexError<uInt>(index, "Block::operator[] - "
00242                              "index out of range"));
00243     };
00244 #endif
00245     return array[index];
00246   }
00247   const T &operator[](size_t index) const {
00248 #if defined(AIPS_ARRAY_INDEX_CHECK)
00249     if ((npts == 0) || (index > npts - 1)) {
00250       throw(indexError<uInt>(index, "Block::operator[] const - "
00251                              "index out of range"));
00252     };
00253 #endif
00254     return array[index];
00255   }
00256   // </group>
00257   
00258   // Set all values in the block to "val".
00259   // <group>
00260   Block<T> &operator=(const T &val)
00261     { T tmp=val; objset(array, tmp, npts); return *this;}
00262   void set(const T &val) { *this = val; }
00263   // </group>
00264 
00265   // If you really, really, need a "raw" pointer to the beginning of the
00266   // storage area this will give it to you. This may leave dangling pointers
00267   // if the block is destructed or if the assignment operator or resize 
00268   // is used. Returns a null pointer if <src>nelements() == 0</src>.
00269   // It is best to only use this if you completely control the extent and
00270   // lifetime of the <src>Block</src>.
00271   // <h3> Examples of misuse </h3> <srcblock>
00272   // Block<Int> *bp = new Block<Int>(100);
00273   // Int *ip = bp->storage();
00274   // delete bp;      // Oops, ip is now dangling
00275   // Block<Int> a(100),b(100);
00276   // Int *ip = a.storage();
00277   // a = b;          // Likewise
00278   // </srcblock>
00279   // <group>
00280   T *storage() {return array;}
00281   const T *storage() const {return array;}
00282   // </group>
00283 
00284   // The number of elements contained in this <src>Block<T></src>.
00285   // <group>
00286   size_t nelements() const {return npts;}
00287   size_t size() const {return npts;}
00288   // </group>
00289 
00290   // Is the block empty (i.e. no elements)?
00291   Bool empty() const {return npts == 0;}
00292 
00293   // Define the STL-style iterators.
00294   // It makes it possible to iterate through all data elements.
00295   // <srcblock>
00296   //  Block<Int> bl(100,0);
00297   //  for (Block<Int>::iterator iter=bl.begin(); iter!=bl.end(); iter++) {
00298   //    *iter += 1;
00299   //  }
00300   // </srcblock>
00301   // <group name=STL-iterator>
00302   // STL-style typedefs.
00303   // <group>
00304   typedef T                 value_type;
00305   typedef T*                iterator;
00306   typedef const T*          const_iterator;
00307   typedef value_type*       pointer;
00308   typedef const value_type* const_pointer; 
00309   typedef value_type&       reference;
00310   typedef const value_type& const_reference;
00311   typedef size_t            size_type;
00312   typedef ptrdiff_t         difference_type;
00313   // </group>
00314   // Get the begin and end iterator object for this block.
00315   // <group>
00316   iterator begin()
00317     { return array; }
00318   const_iterator begin() const
00319     { return array; }
00320   iterator end()
00321     { return array + npts; }
00322   const_iterator end() const
00323     { return array + npts; }
00324   // </group>
00325   // </group>
00326 
00327  private:
00328   // The number of points in the vector
00329   size_t npts;
00330   // The actual storage
00331   T *array;
00332   // Can we delete the storage upon destruction?
00333   Bool destroyPointer;
00334 };
00335 
00336 // <summary>
00337 // A drop-in replacement for <src>Block<T*></src>.
00338 // </summary>
00339  
00340 // <use visibility=export>
00341 // <prerequisite>
00342 //   <li> <linkto class=Block>Block</linkto>
00343 // </prerequisite>
00344  
00345 // <synopsis>
00346 // <src>PtrBlock<T*></src> has exactly the same interface as <src>Block<T*></src>
00347 // and should be used in preference to the latter. It's purpose is solely to
00348 // reduce the number of template instantiations.
00349 // </synopsis>
00350  
00351 // <todo asof="1996/05/01">
00352 //   <li> Partial template specialization is another implementation choice that 
00353 //        will be possible eventually.
00354 //   <li> It might be useful to have functions that know the templte parameter
00355 //        is a pointer, e.g. that delete all the non-null pointers.
00356 // </todo>
00357  
00358  template<class T> class PtrBlock {
00359  public:
00360    PtrBlock() : block_p() {}
00361    explicit PtrBlock(size_t n) : block_p(n) {}
00362    PtrBlock(size_t n, T val) : block_p(n, (void *)val) {}
00363    PtrBlock(size_t n, T *&storagePointer, Bool takeOverStorage = True)
00364      : block_p(n, (void **&)storagePointer, takeOverStorage) {}
00365    PtrBlock(const PtrBlock<T> &other) : block_p(other.block_p) {}
00366    PtrBlock<T> &operator=(const PtrBlock<T> &other)
00367      { block_p = other.block_p; return *this;}
00368    ~PtrBlock() {}
00369    void resize(size_t n, Bool forceSmaller, Bool copyElements)
00370      { block_p.resize(n,forceSmaller, copyElements); }
00371    void resize(size_t n) {block_p.resize(n);}
00372    void resize(size_t n, Bool forceSmaller) {block_p.resize(n, forceSmaller);}
00373    void remove(size_t whichOne, Bool forceSmaller) {
00374      block_p.remove(whichOne, forceSmaller);}
00375    void remove(size_t whichOne) {block_p.remove(whichOne);}
00376    void replaceStorage(size_t n, T *&storagePointer, 
00377                        Bool takeOverStorage=True)
00378      {block_p.replaceStorage(n, (void **&)storagePointer, takeOverStorage);}
00379    T &operator[](size_t index) {return (T &)block_p[index];}
00380    const T &operator[](size_t index) const {return (const T &)block_p[index];}
00381    void set(const T &val) {block_p.set((void *const &)val);}
00382    PtrBlock<T> &operator=(const T &val) {set(val); return *this;}
00383    T *storage()  {return (T *)block_p.storage();}
00384    const T *storage() const {return (const T *)block_p.storage();}
00385    size_t nelements() const {return block_p.nelements();}
00386    size_t size() const {return block_p.size();}
00387    Bool empty() const {return block_p.empty();}
00388  private:
00389    Block<void*> block_p;
00390  };
00391 
00392 
00393 } //# NAMESPACE CASA - END
00394 
00395 #endif