1 /*  -*- c++ -*-  */
2 #ifndef ARRAY_NOPARTIALSPECIALIZATION_H
3 #define ARRAY_NOPARTIALSPECIALIZATION_H
4 
5 #include <algorithm>
6 #include <vector>
7 #include <assert.h>
8 #include <cstddef>	// for std::ptrdiff_t
9 
10 namespace ProtoMol {
11 
12   //*****************************************************************************
13   // Class Template for a Generic Resizable N Dimensional Array        (for N>=2)
14   // By Giovanni Bavestrelli                  Copyright 1999 Giovanni Bavestrelli
15   // Any feedback is welcome, you can contact me at gbavestrelli@yahoo.com
16   //
17   // This is the version for Microsoft Visual C++ 6.0, and perhaps for other
18   // compilers which do not support partial specialization.
19   // To make my classes work, I had to use a trick. I moved the RefArray classes
20   // as nested classes withing class Array, removing template parameter T from
21   // them, allowing me to use full specialization for class RefArray.
22   // I don't think this is up to standard C++. If your compiler supports partial
23   // specialization, be sure to use the other version of these classes.
24   // Thanks to Andrei Alexandrescu for suggesting this trick, which makes my
25   // classes work well with Visual C++, although the solution is not standard.
26   //*****************************************************************************
27 
28 
29   //=============================================================================
30   // Classes for passing a typesafe vector of dimensions to the Array constructor
31   //=============================================================================
32 
33   template <unsigned int N>
34   class ArraySize
35   {
36     std::vector<unsigned int> & Vector;
37 
38     ArraySize<N>(std::vector<unsigned int> & v)
Vector(v)39       :Vector(v)
40     {}
41 
42   public:
43 
operator()44     ArraySize<N+1> operator () (unsigned int dim)
45     {
46       if (Vector.size()>N) Vector.resize(N);
47       Vector.push_back(dim);
48       return ArraySize<N+1>(Vector);
49     }
50 
Vect()51     const std::vector<unsigned int> & Vect() const
52     {
53       assert(Vector.size()==N);
54       return Vector;
55     }
56 
57     friend class ArraySizes;
58     friend class ArraySize<N-1>;
59   };
60 
61   class ArraySizes
62   {
63     std::vector<unsigned int> Vector;
64 
65   public:
66 
ArraySizes(unsigned int dim)67     explicit ArraySizes(unsigned int dim)
68     {
69       Vector.push_back(dim);
70     }
71 
operator()72     ArraySize<2> operator () (unsigned int dim)
73     {
74       if (Vector.size()>1) Vector.resize(1);
75       Vector.push_back(dim);
76       return ArraySize<2>(Vector);
77     }
78   };
79 
80   //=============================================================================
81   // Class Template for a Generic Resizable N Dimensional Array
82   //=============================================================================
83 
84   template <typename T, unsigned int N>
85   class Array
86   {
87   public:
88 
89     //-----------------------------------------------------------------------------
90     // Class Template for N Dimensional SubArrays within an Array
91     //-----------------------------------------------------------------------------
92 
93     template <unsigned int N>
94     class RefArray
95     {
96     public:
97 
98       // STL-like types
99       typedef T         value_type;
100       typedef T &       reference;
101       typedef const T & const_reference;
102       typedef T *       pointer;
103       typedef const T * const_pointer;
104       typedef T *       iterator;
105       typedef const T * const_iterator;
106       typedef size_t    size_type;
107       typedef ptrdiff_t difference_type;
108 
109       // Give access to number of dimensions
110       enum  { array_dims = N };
111 
112     private:
113 
114       const size_type * const m_pNDimensions; // Array dimensions
115       const size_type * const m_pSubArrayLen; // SubArray dimensions
116 
117       T   * const m_pElements; // Point to SubArray with elements within Array
118 
119       RefArray<N>(T * pElements, const size_type * pNDimensions,
120 		  const size_type * pSubArrayLen)
m_pElements(pElements)121 	:m_pElements(pElements),m_pNDimensions(pNDimensions),
122 	 m_pSubArrayLen(pSubArrayLen)
123       {
124 	assert(m_pElements && m_pNDimensions && m_pSubArrayLen);
125 	assert(m_pNDimensions[0]>0 && m_pSubArrayLen[0]>0);
126       }
127 
128     public:
129 
130       RefArray<N-1> operator [](size_type Index)
131       {
132 	assert(m_pElements);
133 	assert(Index<m_pNDimensions[0]);
134 	return RefArray<N-1>(&m_pElements[Index*m_pSubArrayLen[0]],
135 			     m_pNDimensions+1,m_pSubArrayLen+1);
136       }
137 
138       const RefArray<N-1> operator [](size_type Index) const
139       {
140 	assert(m_pElements);
141 	assert(Index<m_pNDimensions[0]);
142 	return RefArray<N-1>(&m_pElements[Index*m_pSubArrayLen[0]],
143 			     m_pNDimensions+1,m_pSubArrayLen+1);
144       }
145 
146       // Return STL-like iterators
begin()147       iterator       begin()       { return m_pElements; }
begin()148       const_iterator begin() const { return m_pElements; }
end()149       iterator       end()         { return m_pElements+size(); }
end()150       const_iterator end()   const { return m_pElements+size(); }
151 
152       // Return size of array
size()153       size_type size() const { return m_pNDimensions[0]*m_pSubArrayLen[0]; }
154 
155       // Return size of subdimensions
size(unsigned int Dim)156       size_type size(unsigned int Dim) const
157       {
158 	assert(Dim>=1 && Dim<=N);
159 	return m_pNDimensions[Dim-1];
160       }
161 
162       // Return number of dimensions
dimensions()163       unsigned int dimensions()  const { return N; }
164 
165     protected:
166 
167       // The following are protected mainly because they are not exception-safe
168       // but the way they are used in the rest of the class is exception-safe
169 
170       // Copy the elements of another subarray on this one where possible
171       // Where not possible, initialize them to a specified value Init
172       void copy(const RefArray<N> & SA, const T & Init=T())
173       {
174 	size_type below=std::min(size(1),SA.size(1));
175 	size_type above=size(1);
176 
177 	// Copy the elements we can copy
178 	for (size_type i=0;i<below;++i)
179 	  (*this)[i].copy(SA[i],Init);
180 
181 	// Reset the elements we can't copy
182 	for (size_type j=below;j<above;++j)
183 	  (*this)[j].initialize(Init);
184       }
185 
186       // Reset all the elements
187       void initialize(const T & Init=T())
188       {
189 	std::fill(begin(),end(),Init);
190       }
191 
192       friend class Array<T,N>;
193       friend class Array<T,N+1>;
194       friend class RefArray<N+1>;
195     };
196 
197 
198     //-----------------------------------------------------------------------------
199     // Partial Specialization for Monodimensional SubArray within an Array
200     //-----------------------------------------------------------------------------
201 
202     template <>
203     class RefArray<1>
204     {
205     public:
206 
207       // STL-like types
208       typedef T         value_type;
209       typedef T &       reference;
210       typedef const T & const_reference;
211       typedef T *       pointer;
212       typedef const T * const_pointer;
213       typedef T *       iterator;
214       typedef const T * const_iterator;
215       typedef size_t    size_type;
216       typedef ptrdiff_t difference_type;
217 
218       // Give access to number of dimensions
219       enum  { array_dims = 1 };
220 
221     private:
222 
223       const size_type * const m_pNDimensions; // Array dimension
224 
225       T   * const     m_pElements; // Point to elements within Array
226 
227       RefArray<1>(T * pElements, const size_type * pNDimensions,
228 		  const size_type * pSubArrayLen)
m_pElements(pElements)229 	:m_pElements(pElements),m_pNDimensions(pNDimensions)
230       {
231 	assert(m_pElements && m_pNDimensions && pSubArrayLen);
232 	assert(m_pNDimensions[0]>0 && pSubArrayLen[0]==1); // We found the elements
233       }
234 
235     public:
236 
237       reference operator [](size_type Index)
238       {
239 	assert(m_pElements);
240 	assert(Index<m_pNDimensions[0]);
241 	return m_pElements[Index];
242       }
243 
244       const_reference operator [](size_type Index) const
245       {
246 	assert(m_pElements);
247 	assert(Index<m_pNDimensions[0]);
248 	return m_pElements[Index];
249       }
250 
251       // Return STL-like iterators
begin()252       iterator       begin()       { return m_pElements; }
begin()253       const_iterator begin() const { return m_pElements; }
end()254       iterator       end()         { return m_pElements+size(); }
end()255       const_iterator end()   const { return m_pElements+size(); }
256 
257       // Return size of array
size()258       size_type size()  const { return m_pNDimensions[0]; }
259 
260       // Return size of subdimensions
size(unsigned int Dim)261       size_type size(unsigned int Dim) const
262       {
263 	assert(Dim==1);
264 	return m_pNDimensions[0];
265       }
266 
267       // Return number of dimensions
dimensions()268       unsigned int dimensions()  const { return 1; }
269 
270     protected:
271 
272       // The following are protected mainly because they are not exception-safe
273       // but the way they are used in the rest of the class is exception-safe
274 
275       // Copy the elements of another subarray on this one where possible
276       // Where not possible, initialize them to a specified value Init
277       void copy(const RefArray<1> & SA, const T & Init=T())
278       {
279 	size_type below=std::min(size(1),SA.size(1));
280 	size_type above=size(1);
281 
282 	// Copy the elements we can copy
283 	for (size_type i=0;i<below;++i)
284 	  m_pElements[i]=SA.m_pElements[i];
285 
286 	// Reset the elements we can't copy
287 	for (size_type j=below;j<above;++j)
288 	  m_pElements[j]=Init;
289       }
290 
291       // Reset all the elements
292       void initialize(const T & Init=T())
293       {
294 	std::fill(begin(),end(),Init);
295       }
296 
297       friend class Array<T,1>;
298       friend class Array<T,2>;
299       friend class RefArray<2>;
300     };
301 
302 
303     //-----------------------------------------------------------------------------
304     // Class Template for a Generic Resizable N Dimensional Array
305     //-----------------------------------------------------------------------------
306 
307   public:
308 
309     // STL-like types
310     typedef T         value_type;
311     typedef T &       reference;
312     typedef const T & const_reference;
313     typedef T *       pointer;
314     typedef const T * const_pointer;
315     typedef T *       iterator;
316     typedef const T * const_iterator;
317     typedef size_t    size_type;
318     typedef ptrdiff_t difference_type;
319 
320     // Give access to number of dimensions
321     enum  { array_dims = N };
322 
323   private:
324 
325     T *        m_pArrayElements; // Pointer to actual array elements
326     size_type  m_nArrayElements; // Total number of array elements
327 
328     size_type  m_NDimensions[N];  // Size of the N array dimensions
329     size_type  m_SubArrayLen[N];  // Size of each subarray
330 
331   public:
332 
333     // Default constructor
334     Array<T,N>()
m_pArrayElements(NULL)335       :m_pArrayElements(NULL),m_nArrayElements(0)
336     {
337       std::fill(m_NDimensions,m_NDimensions+N,0);
338       std::fill(m_SubArrayLen,m_SubArrayLen+N,0);
339     }
340 
341     // This takes an array of N values representing the size of the N dimensions
342     explicit Array<T,N>(const unsigned int * Dimensions, const T & Init=T())
m_pArrayElements(NULL)343       :m_pArrayElements(NULL),m_nArrayElements(0)
344     {
345       std::fill(m_NDimensions,m_NDimensions+N,0);
346       std::fill(m_SubArrayLen,m_SubArrayLen+N,0);
347 
348       resize(Dimensions,Init);
349     }
350 
351     // This takes an ArraySize object with the N dimensions
352     explicit Array<T,N>(const ArraySize<N> & Dimensions, const T & Init=T())
m_pArrayElements(NULL)353       :m_pArrayElements(NULL),m_nArrayElements(0)
354     {
355       std::fill(m_NDimensions,m_NDimensions+N,0);
356       std::fill(m_SubArrayLen,m_SubArrayLen+N,0);
357 
358       resize(Dimensions,Init);
359     }
360 
361     // Copy constructor
362     Array<T,N>(const Array<T,N> & A)
m_pArrayElements(NULL)363       :m_pArrayElements(NULL),m_nArrayElements(0)
364     {
365       std::fill(m_NDimensions,m_NDimensions+N,0);
366       std::fill(m_SubArrayLen,m_SubArrayLen+N,0);
367 
368       Array<T,N> Temp;
369       if (!A.empty() && Temp.resize(A.m_NDimensions))
370 	std::copy(A.begin(),A.end(),Temp.begin());
371       swap(Temp);
372     }
373 
374     // Destructor
375     ~Array<T,N>()
376     {
377       delete [] m_pArrayElements;
378     }
379 
380     // Indexing Array
381     RefArray<N-1> operator [](size_type Index)
382     {
383       assert(m_pArrayElements);
384       assert(Index<m_NDimensions[0]);
385       return RefArray<N-1>(&m_pArrayElements[Index*m_SubArrayLen[0]],
386 			   m_NDimensions+1,m_SubArrayLen+1);
387     }
388 
389     // Indexing Constant Array
390     const RefArray<N-1> operator [](size_type Index) const
391     {
392       assert(m_pArrayElements);
393       assert(Index<m_NDimensions[0]);
394       return RefArray<N-1>(&m_pArrayElements[Index*m_SubArrayLen[0]],
395 			   m_NDimensions+1,m_SubArrayLen+1);
396     }
397 
398     // Return RefArray referencing entire Array
GetRefArray()399     RefArray<N> GetRefArray()
400     {
401       assert(m_pArrayElements);
402       return RefArray<N>(m_pArrayElements,m_NDimensions,m_SubArrayLen);
403     }
404 
405     // Return constant RefArray referencing entire Array
GetRefArray()406     const RefArray<N> GetRefArray() const
407     {
408       assert(m_pArrayElements);
409       return RefArray<N>(m_pArrayElements,m_NDimensions,m_SubArrayLen);
410     }
411 
412     // Set the size of each array dimension
413     // Visual C++ does not accept parameter defined so: const unsigned int (&)[N]
414     // so I accepted a solution which is not type-safe: use it judiciously
415     bool resize(const unsigned int * Dimensions, const T & Init=T(), bool PreserveElems=false)
416     {
417       assert(Dimensions);
418 
419       Array<T,N> Temp;
420 
421       // Calculate all the information you need to use the array
422       Temp.m_nArrayElements=1;
423       for (int i=0;i<N;++i)
424 	{
425 	  if (Dimensions[i]==0)
426             return false; // Check that no dimension was zero
427 	  Temp.m_nArrayElements*=Dimensions[i];
428 	  Temp.m_NDimensions[i]=Dimensions[i];
429 	  Temp.m_SubArrayLen[i]=1;
430 	  for (int k=N-1;k>i;k--)
431             Temp.m_SubArrayLen[i]*=Dimensions[k];
432 	}
433 
434       // Allocate new elements, let exception propagate
435       Temp.m_pArrayElements=new T[Temp.m_nArrayElements];
436 
437       // Some compilers might not throw exception if allocation fails
438       if (!Temp.m_pArrayElements)
439 	return false;
440 
441       // Copy the elements from the previous array if requested
442       if (PreserveElems && !empty())
443 	Temp.copy(*this,Init);
444       // Otherwise initialize them to the specified value
445       else
446 	Temp.initialize(Init);
447 
448       // Now swap this object with the temporary
449       swap(Temp);
450 
451       return true;
452     }
453 
454     // resize accepting a fixed ArraySize, this solution is type-safe
455     bool resize(const ArraySize<N> & Dimensions, const T & Init=T(), bool PreserveElems=false)
456     {
457       unsigned int Dims[N];
458       std::copy(Dimensions.Vect().begin(),Dimensions.Vect().end(),Dims);
459       return resize(Dims,Init,PreserveElems);
460     }
461 
462     // Delete the complete Array
clear()463     void clear()
464     {
465       delete [] m_pArrayElements;
466       m_pArrayElements=NULL;
467       m_nArrayElements=0;
468 
469       std::fill(m_NDimensions,m_NDimensions+N,0);
470       std::fill(m_SubArrayLen,m_SubArrayLen+N,0);
471     }
472 
473     // Assignment operator
474     Array<T,N> & operator = (const Array<T,N> & A)
475     {
476       if (&A!=this) // For efficiency
477 	{
478 	  Array<T,N> Temp(A);
479 	  swap(Temp);
480 	}
481       return *this;
482     }
483 
484     // Return STL-like iterators
begin()485     iterator       begin()       { return m_pArrayElements; }
begin()486     const_iterator begin() const { return m_pArrayElements; }
end()487     iterator       end()         { return m_pArrayElements+m_nArrayElements; }
end()488     const_iterator end()   const { return m_pArrayElements+m_nArrayElements; }
489 
490     // Some more STL-like size members
size()491     size_type size()       const { return m_nArrayElements; }
492 
493     // Return the size of each dimension, 1 to N
size(unsigned int Dim)494     size_type size(unsigned int Dim) const
495     {
496       assert(Dim>=1 && Dim<=N);
497       return m_NDimensions[Dim-1];
498     }
499 
500     // Say if the array is empty
empty()501     bool empty()           const { return m_nArrayElements==0; }
502 
503     // Return number of dimensions
dimensions()504     unsigned int dimensions()  const { return N; }
505 
506     // Swap this array with another, a'la STL
swap(Array<T,N> & A)507     void swap(Array<T,N> & A)
508     {
509       std::swap(m_pArrayElements,A.m_pArrayElements);
510       std::swap(m_nArrayElements,A.m_nArrayElements);
511 
512       std::swap_ranges(m_NDimensions,m_NDimensions+N,A.m_NDimensions);
513       std::swap_ranges(m_SubArrayLen,m_SubArrayLen+N,A.m_SubArrayLen);
514     }
515 
516   protected:
517 
518     // The following are protected mainly because they are not exception-safe
519     // but the way they are used in the rest of the class is exception-safe
520 
521     // Copy the elements of another array on this one where possible
522     // Where not possible, initialize them to a specified value Init
523     void copy(const Array<T,N> & A, const T & Init=T())
524     {
525       size_type below=std::min(size(1),A.size(1));
526       size_type above=size(1);
527 
528       // Copy the elements we can copy
529       for (size_type i=0;i<below;++i)
530 	(*this)[i].copy(A[i],Init);
531 
532       // Reset the elements we can't copy
533       for (size_type j=below;j<above;++j)
534 	(*this)[j].initialize(Init);
535     }
536 
537     // Initialize all the array elements
538     void initialize(const T & Init=T())
539     {
540       std::fill(begin(),end(),Init);
541     }
542 
543     // Prefer non-member operator ==, but it needs to be a friend
544     friend bool operator == (const Array<T,N> & A, const Array<T,N> & B);
545   };
546 
547 
548   // Test for equality between two arrays
549   template <typename T, unsigned int N>
550   bool operator == (const Array<T,N> & A, const Array<T,N> & B)
551   {
552     return std::equal(A.m_NDimensions,A.m_NDimensions+N,B.m_NDimensions)
553       && std::equal(A.begin(),A.end(),B.begin());
554   }
555 
556   // Test for inequality between two arrays
557   template <typename T, unsigned int N>
558   bool operator != (const Array<T,N> & A, const Array<T,N> & B)
559   {
560     return !(A==B);
561   }
562 
563 
564   /* The following don't work for Visual C++
565 
566   // Not implemented, meaningless to have 0 dimensions
567   template <typename T>
568   class Array<T,0>
569   {
570   };
571 
572   // Not implemented, use std::vector for one dimensional arrays
573   template <typename T>
574   class Array<T,1>
575   {
576   };
577 
578   */
579 }
580 #endif
581 
582 
583