* Fixed Array equality and less than comparisons
[libs.git] / src / Sylph / Core / Array.h
blobbde29d80781ccd90eee157fd213c601777a4c46d
1 /*
2 * LibSylph Class Library
3 * Copyright (C) 2009 Frank "SeySayux" Erens <seysayux@gmail.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the LibSylph Pulbic License as published
7 * by the LibSylph Developers; either version 1.0 of the License, or
8 * (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the LibSylph
13 * Public License for more details.
15 * You should have received a copy of the LibSylph Public License
16 * along with this Library, if not, contact the LibSylph Developers.
18 * Created on 8 februari 2009, 14:18
21 #ifndef ARRAY_H_
22 #define ARRAY_H_
24 #include "Iterable.h"
25 #include "Iterator.h"
26 #include "Comparable.h"
27 #include "Exception.h"
28 #include "Range.h"
29 #include "Primitives.h"
31 #include <algorithm>
32 #include <initializer_list>
33 #include <iostream>
35 SYLPH_BEGIN_NAMESPACE
36 class Any;
37 template<class T> class Array;
39 SYLPH_PUBLIC
41 /**
42 * Array provides a safe array. It works the same like a c-style array (not like
43 * std::vector which can expand), but instead of overflowing, it throws an
44 * @c Exception whenever you try to access data outside its bounds. Therefore it
45 * also keeps track of its own length. <p>
46 * The Array class provided here is reference-counted, which means it's
47 * perfectly safe and even recommended to pass it by value instead of by
48 * reference or by pointer. This way, the Array acts more like a builtin type
49 * and does not obstruct the workflow. <p>
50 * Please note that most constructors copy the contents into the array, which
51 * means that unless the type used is easy to copy, using the specialized
52 * array-to-pointer ( Array<T*> ) is prefered.
54 template<class T>
55 class Array : public virtual Object {
56 private:
57 size_t _length;
58 public:
60 class iterator : public RandomAccessIterator<T, iterator> {
61 public:
62 typedef RandomAccessIterator<T, iterator> super;
64 iterator(bool begin = false, Array<T>* obj = NULL)
65 : super(begin), _obj(obj) {
66 _currentIndex = begin ? 0 : (_obj->length - 1);
69 iterator(bool begin = false, const Array<T>* obj = NULL)
70 : super(begin), _obj(const_cast<Array<T>*> (obj)) {
71 _currentIndex = begin ? 0 : (_obj->length - 1);
74 bool equals(const iterator& other) const {
75 return _currentIndex == other._currentIndex &&
76 _obj == other._obj;
79 iterator(const iterator& other) {
80 _currentIndex = other._currentIndex;
81 _obj = other._obj;
84 typename super::reference current() const {
85 return (*_obj)[_currentIndex];
88 bool hasNext() const {
89 return _currentIndex < (_obj->length - 1);
92 void next() const {
93 _currentIndex++;
96 bool hasPrevious() const {
97 return _currentIndex >= 0;
100 void previous() const {
101 _currentIndex--;
104 idx_t currentIndex() const {
105 return _currentIndex;
108 size_t length() const {
109 return _obj->length;
111 private:
112 mutable idx_t _currentIndex;
113 Array<T>* _obj;
115 S_ITERABLE(T)
116 public:
118 * A function that is used for filtering by the filter() method. This
119 * function takes both an instance of the class this Array contains, and
120 * a reference to a reference to any kind of other data that may need to
121 * be passed to the FilterFunction.
123 typedef bool(*FilterFunction)(const T&, Any&);
125 * The length of the array. This variable is 1-based, while the array itself
126 * is 0-based, i.e. if length == N the highest entry in this array is N-1.
127 * E.g if array.length == 5, then the higest entry is array[4]
129 const size_t & length;
132 * Creates an Array<T> from a pointer to T and a length. The new array will
133 * have the length specified in <code>length</code>. The original array will
134 * not be modified, the contents are copied. No bounds-checking
135 * is done, therefore, use this function at your own responsability!
136 * @param length The length of the original C array
137 * @param orig The original C array, supplied as a pointer.
139 inline static Array<T> fromPointer(std::size_t length, T * orig) {
140 Array<T> ar(length);
141 for (int x = 0; x < length; x++)ar[x] = orig[x];
142 return ar;
144 public:
147 * Creates an Array with the specified length. A new instance of the
148 * reference counted data is created, its reference count set to 1 and the
149 * internal C array is allocated to have the specified length.
150 * @param len The length of the new Array.
152 explicit Array(std::size_t len = 0) : _length(len), length(_length),
153 data(new Data(len)) {
157 * Creates an Array from an intializer list. This constructor allows the
158 * easier, more familiar syntax of Array creation, but requires C++0x. Using
159 * this constructor, arrays can be initialized as following:
160 * <pre>Array<int> myarr = {5,4,7,9};</pre>
161 * A new instance of the reference counted data is created, the reference
162 * count is set to 1, the length is set to the length of the intializer
163 * list, and all data is copied into a newly allocated C array.
164 * @param il The initializer_list used to create the array.
166 Array(const std::initializer_list<T> & il) : _length(il.size()),
167 length(_length), data(new Data(_length)) {
168 for (idx_t i = 0; i < il.size(); i++) {
169 data->_carray[i] = il.begin()[i];
174 * Creates an Array from an existing C-style array. Note that you can only
175 * pass a true array, i.e. you cannot pass a pointer that acts as an array.
176 * If you only have a pointer, you'll have to initialize using
177 * Array::fromPointer(size_t, length) . <p>
178 * A new instance of the reference counted data is created, the reference
179 * count is set to 1, the length is set to the length of the Array, all
180 * data is copied into a newly allocated C array with the same length as
181 * the original array. The original array remains unmodified.
182 * @param array A traditional, C-style array to create this Array from.
184 template<size_t N>
185 Array(const T(&array)[N]) : _length(N), length(_length), data(new Data(N)) {
186 for (idx_t i = 0; i < _length; i++) {
187 data->_carray[i] = array[i];
192 * Creates an Array from another instance of the Array class. The data is
193 * not copied, instead, the pointer to the refernce counted data will be
194 * set to the reference counted data of the other Array, and the reference
195 * count will increase by 1. Other fields of the reference counted data
196 * remain unmodified.
197 * @param other An other Array from which to use the reference counted data.
199 Array(const Array<T> & other) : _length(other._length), length(_length),
200 data(other.data){
201 data->refcount++;
205 * Creates an array from a range of items. Every item within the range will
206 * be added to the array. This is most useful for integral types, as other
207 * types usually don't support the required semantics.<p>
208 * A new instance of the reference counted data is created, the reference
209 * count is set to 1, the length is set to <code>ran.last() - ran.first()
210 * </code>, a new C-style array with this length will be allocated.
211 * @param ran a range class that specifies the lower and upper boundaries.
214 Array(const basic_range<T> & ran) : _length(ran.last() - ran.first()),
215 length(_length), data(new Data(length)) {
216 idx_t idx = 0;
217 for (T x = ran.first(); x < ran.last(); x++) {
218 *this[idx] = x;
219 idx++;
224 * Creates an Array from a single item. This is useful for implicit
225 * conversions, as it allows a single instance of a class to be passed as
226 * an Array of that class with length 1. <p>
227 * A new instance of the reference counted data is created, the reference
228 * count set to 1, the length is set to 1, and a new C-style array with
229 * length 1 is allocated. The object is copied into this array, the original
230 * object remains unmodified.
231 * @param t An object to create a length-1 array from.
233 explicit Array(const T& t) : _length(1), length(_length),
234 data(new Data(1)) {
235 data->_carray[0] = t;
239 * Destructor. Reduces the reference count by 1. If the reference count
240 * reaches 0, the internal backing data will be destroyed.
242 virtual ~Array() {
243 data->refcount--;
244 if (!data->refcount) {
245 delete data;
246 data = NULL;
251 * Creates a copy of this array. The Array returned from this method is
252 * an exact copy of this Array, such that ar == ar.copy() . The returned
253 * Array is different from the one returned by operator=, as the reference
254 * counted data gets copied as well, in other words, both Arrays will have
255 * a different, equal instance of the reference counted data.
256 * @return A new Array containing the same data as this Array.
258 Array<T> copy() const {
259 Array<T> toReturn((std::size_t)length);
260 for (idx_t i = 0; i < length; i++) {
261 toReturn[i] = (*this)[i];
263 return toReturn;
267 * Returns a c-style array representing the contents of this Array. The
268 * array returned is not a copy of this array, in fact, changes to the
269 * returned array are reflected in this Array.
271 T * carray() {
272 return data->_carray;
276 * Returns a c-style array representing the contents of this Array. The
277 * array returned is not a copy of this array, in fact, changes to the
278 * returned array are reflected in this Array. This version returns a const
279 * c-style array and is used when this Array is const.
281 const T *carray() const {
282 return data->_carray;
286 * This will filter the Array according to a FilterFunction. This function
287 * returns a new, 'filtered' Array, which only contains the entries for
288 * which the FilterFunction returns true.
289 * @param func A pointer to the function which is used for filtering
290 * @param clientData %Any data to be passed to the FilterFunction
291 * @return A new array containing only the filtered data
293 Array<T> filter(FilterFunction func, Any& clientData) {
294 // I'm terribly sorry but we'll need to iterate twice over the Array...
295 size_t newlength;
296 for (idx_t i = 0; i < length; i++) {
297 if (func(*this[i], clientData)) newlength++;
299 Array<T> toReturn(newlength);
300 idx_t curpos = 0;
301 for (idx_t i = 0; i < length; i++) {
302 if (func(*this[i], clientData)) {
303 toReturn[curpos] = *this[i];
304 curpos++;
307 return toReturn;
311 * Sets every entry in the array to the object returned by that classes'
312 * default constructor.
314 void clear() {
315 delete this->data->_carray;
316 this->data->_carray = new T[this->data->_length];
320 * Swaps the data pointer of this Array with the other Array. The refcount
321 * for the current data pointer gets decreased by 1, the refcount for the
322 * data pointer of the other array gets increased by 1. In case the this
323 * Array'soriginal data pointer's refcount reaches zero, the original data
324 * will be deleted.
325 * @param other The other array from which to use the data pointer
327 Array<T> & operator=(const Array<T> & other) {
328 if (this->data == other.data) return *this;
329 this->data->refcount--;
330 if (!this->data->refcount) delete this->data;
331 this->data = other.data;
332 this->_length = other.data->_length;
333 data->refcount++;
334 return *this;
338 * Used for accessing the Array's contents. Its behaviour is identical to
339 * that of c-style arrays, but throws an exception instead of overflowing
340 * or causing segfaults. <p>
341 * The Array will assume ownership over any pointers entered in this way.
342 * @param idx the index in the array from which to return an element
343 * @throw ArrayException if <code>idx > length</code>
345 T & operator[](std::sidx_t idx) throw (Exception) {
346 if ((idx < (sidx_t)length) && (idx >= -(sidx_t)length)) {
347 return idx >= 0 ? data->_carray[idx] : data->_carray[length + idx];
348 } else {
349 sthrow(ArrayException, "Array overflow");
354 * This is the <code>const</code> version of T& operator[] . Its behaviour
355 * is identical to that of c-style const arrays, but throws an exception
356 * instead of overflowing or causing segfaults.
357 * @param idx the index in the array from which to return an element
358 * @throw ArrayException if <code>idx > length</code>
360 const T & operator[](std::sidx_t idx) const throw (Exception) {
361 if ((idx < (sidx_t)length) && (idx >= -(sidx_t)length)) {
362 return idx >= 0 ? data->_carray[idx] : data->_carray[length + idx];
363 } else {
364 sthrow(ArrayException, "Array overflow");
369 * Slices the array and returns the subarray. E.g. :
370 * <pre>Array<String> subarr = myarr[range(5,8)]</pre>
371 * <code>subarr</code> now contains the values of @c myarr[5] to @c myarr[8]
372 * . Please note that the subarray contains a copy of the original.
373 * @param ran The range describing the slice.
374 * @throw ArrayException if ran.last() > length
376 Array<T> operator[](const range & ran) throw (Exception) {
377 if (ran.first() < 0 || ran.last() >= length)
378 sthrow(ArrayException, "Array overflow");
380 Array<T> toReturn = Array<T>::fromPointer((ran.last() - ran.first())+1,
381 data->_carray + ran.first());
382 return toReturn;
386 * Const-version of operator[](const range &) .
387 * @param ran The range describing the slice
388 * @throw ArrayException if ran.last() > length
390 const Array<T> operator[](const range & ran) const throw (Exception) {
391 if (ran.first() < 0 || ran.last() >= length)
392 sthrow(ArrayException, "Array overflow");
394 Array<T> toReturn = Array<T>::fromPointer((ran.last() - ran.first())+1,
395 data->_carray + ran.first());
396 return toReturn;
399 #ifndef SYLPH_DOXYGEN
400 protected:
402 struct Data {
404 explicit Data(size_t length) : _length(length),
405 _carray(new T[length]()), refcount(1) {
409 virtual ~Data() {
410 delete _carray;
412 const size_t _length;
413 T * _carray;
414 suint refcount;
415 } * data;
416 #endif
420 * Compares the two Arrays on equality. To Arrays compare equal when their
421 * lengths are identical and each of the items compare equal to the item on the
422 * same position in the other array.
423 * @return <i>true</i> when the two arrays compare equal, <i>false</i>
424 * otherwise.
426 template<class T>
427 inline bool operator==(const Array<T>& lhs, const Array<T>& rhs) {
428 if(lhs.length == rhs.length) {
429 for(idx_t i = 0; i < lhs.length; i++) {
430 if(lhs[i] != rhs[i]) return false;
432 return true;
434 return false;
438 * Compares the two Arrays. To Arrays compare less than when their lengths are
439 * identical and each of the items compare less than to the item on the same
440 * position in the other array.
441 * @return <i>true</i> when the first array compares less than the first,
442 * <i>false</i> otherwise.
444 template<class T>
445 inline bool operator<(const Array<T>& lhs, const Array<T>& rhs) {
446 return std::lexicographical_compare(&lhs[0], &lhs[lhs.length-1],
447 &rhs[0], &rhs[rhs.length-1]);
450 SYLPH_END_NAMESPACE
452 #endif /* ARRAY_H_ */