View difference between Paste ID: sx5e7vfC and ky4itKsj
SHOW: | | - or go back to the newest paste.
1
#ifndef COMMON_CONTAINERS_GRID_H
2
#define COMMON_CONTAINERS_GRID_H
3
4
#include "Common/Basics.h"
5
6
#include <utility> //For std::move()
7
#include <stdexcept> //For std::out_of_range exception.
8
9
class cPoint;
10
class cRect;
11
12
namespace Common {
13
14-
template<class Type> class GridIterator;
14+
template<typename Type, typename ContainerType> class GridIterator;
15
16
/*
17
    A resizable 2D array container, that resizes without harming the relative location of the elements held
18
    by the container, and supports negative indices, like a 2D Cartesian grid.
19
*/
20
template<typename Type>
21
class Grid
22
{
23
public:
24-
    typedef GridIterator<Type> iterator;
24+
    typedef GridIterator<Type, Grid> iterator;
25
    typedef GridIterator<const Type, const Grid<Type> > const_iterator;
26
    
27-
    Grid() : memory(nullptr), boundsOffset(0)
27+
28
    Grid() : memory(nullptr)
29
    {   }
30
    
31
    //Construct and call Resize().
32
    Grid(const cRect &newBounds, const Type &value = Type()) : memory(nullptr)
33
    {
34
        this->Resize(newBounds, value);
35
    }
36
    
37
    ~Grid()
38
    {
39
        //Clear the grid, calling the destructors on any constructed elements.
40
        this->Clear();
41
        
42
        //Deallocate any reserved memory.
43
        this->deallocate(this->memory);
44
    }
45
    
46
    //================================================================
47
    
48
    //Erases the grid, resetting the bounds to (0,0,0,0).
49
    //Does not free the reserved capacity. Call Conserve() afterward to do that.
50
    void Clear()
51
    {
52
        this->Resize(cRect(0,0,0,0));
53
    }
54
    
55
    //Resets the grid to (0,0,0,0) and releases the reserved memory as well.
56
    //This is the same as calling Clear() followed by Conserve().
57
    void Reset()
58
    {
59
        this->Clear();
60
        this->Conserve();
61
    }
62
    
63
    //================================================================
64
    
65
    //Returns true if the width *or* the height (or both) of the grid is 0, *even* if its position is *not* at (0,0).
66
    bool IsEmpty() const
67
    {
68
        return this->bounds.IsEmpty();
69
    }
70
    
71
    //True if the grid is 0 by 0 *and* its position is at (0,0). 
72
    bool IsNull() const
73
    {
74
        return this->bounds.IsNull();
75
    }
76
    
77
    //================================================================
78
    
79
    //Resizes the grid to 'newBounds', constructing the new elements with 'value' (or the default constructor if 'value' is not set).
80
    //If Grid doesn't have enough capacity, it'll reallocate storage and move the elements to new memory addresses.
81
    //Throws std::bad_alloc if the allocation failed, and rethrows any exceptions thrown by element constructors or destructors.
82
    void Resize(const cRect &newBounds, const Type &value = Type())
83-
        this->ensureCapacity(newBounds, true);
83+
84-
        this->constructAdditions(this->bounds, newBounds, value);
84+
        try
85
        {
86
            //[CAN THROW - Make sure no member variables are changed before these function calls]
87-
        this->boundsOffset = this->capacity.Index(this->bounds.position);
87+
            cRect newCapacity = this->ensureCapacity(newBounds, true);
88
            
89
            //[CAN THROW - Make sure no member variables are changed before these function calls]
90
            this->constructAdditions(this->bounds, newBounds, value);
91
            
92
            //Record the new capacity, after we are sure no exceptions were thrown and everything succeeded.
93
            this->capacity = newCapacity;
94
        }
95
        catch(std::exception &exception)
96
        {
97
            Log::Message(); //exception.what()
98
            throw;
99
        }
100
        catch(...)
101
        {
102-
        this->ensureCapacity(newCapacity, false);
102+
            Log::Message(); //Unknown exception
103
            throw;
104
        }
105
106
        this->bounds = newBounds;
107
    }
108
109
    const cRect &GetBounds() const
110
    {
111
        return this->bounds;
112
    }
113
    
114
    //================================================================
115
    
116
    //Reserves 'newCapacity' amount of memory, without actually resizing the grid. This will reallocate the memory and move the elements to new addresses.
117
    //Reserve() will not allow the grid to shrink smaller than the capacity currently is reserved to.
118
    //Throws std::bad_alloc if the allocation failed, and rethrows any exceptions thrown by element constructors.
119
    void Reserve(const cRect &newCapacity)
120
    {
121
        //[CAN THROW - Make sure no member variables are changed before this function call]
122
        this->capacity = this->ensureCapacity(newCapacity, false);
123
    }
124
    
125
    //Releases all capacity that is no longer needed. This will reallocate the memory and move the elements to new addresses.
126
    void Conserve()
127
    {
128
        //[CAN THROW - Make sure no member variables are changed before this function call]
129
        this->reallocateMemory(this->bounds);
130
    }
131
    
132
    //Returns the amount of memory held in reserve for future resizing.
133
    const cRect &GetCapacity() const
134
    {
135
        return this->capacity;
136
    }
137
    
138
    //================================================================
139
    
140
    //Resizes the grid. Negative values shrink the grid.
141
    void Expand(int amount)
142
    {
143
        this->Expand(amount, amount, amount, amount);
144
    }
145
    
146
    void Expand(int horizontal, int vertical)
147
    {
148
        this->Expand(horizontal, horizontal, vertical, vertical);
149
    }
150
    
151
    void Expand(int left, int right, int top, int bottom)
152
    {
153
        cRect newBounds = this->bounds;
154
        newBounds.Pad(left, right, top, bottom);
155
        
156
        this->Resize(newBounds);
157
    }
158
    
159
    //Resizes the grid. Negative values enlarge the grid.
160
    void Shrink(int amount)
161
    {
162
        this->Shrink(amount, amount, amount, amount);
163
    }
164
    
165
    void Shrink(int horizontal, int vertical)
166
    {
167
        this->Shrink(horizontal, horizontal, vertical, vertical);
168
    }
169
    
170
    void Shrink(int left, int right, int top, int bottom)
171
    {
172
        cRect newBounds = this->bounds;
173
        newBounds.Pad(-left, -right, -top, -bottom);
174
        
175
        this->Resize(newBounds);
176
    }
177
    
178
    //================================================================
179
    
180
    //Throws std::out_of_range if out of range.
181
    Type &At(const cPoint &pos)
182
    {
183
        if(!this->bounds.Contains(pos))
184
        {
185
            std::string message = std::string("Grid::At() - 'pos' is not within range.\n")
186
                                + "\t'pos' = " + pos.ToString()
187
                                + "\n\t'bounds' = " + this->bounds.ToString();
188
            
189
            throw std::out_of_range(message);
190
        }
191
        
192
        return (*this)[pos];
193
    }
194
195
    //Doesn't check for range.
196
    Type &operator[](const cPoint &pos)
197
    {
198
        //Convert the point to a index into the memory.
199
        size_t index = this->capacity.Index(pos);
200
        
201
        return this->memory[index];
202
    }
203
204
    //Doesn't check for range. Index-based lookup for iteration (from index '0' to "this->bounds: (width * height)").
205
    Type &AtIndex(unsigned index)
206
    {
207
        cPoint pos = this->bounds.FromIndex(index);
208
        int newIndex = this->capacity.Index(std::move(pos));
209
        
210
        return this->memory[newIndex];
211
    }
212-
        size_t constructed = 0, destructed = 0, preserved = 0, ignored = 0;
212+
213
    //Const version, for const_iterator.
214-
        for(cPoint point : totalArea)
214+
    const Type &AtIndex(unsigned index) const
215
    {
216
        cPoint pos = this->bounds.FromIndex(index);
217
        int newIndex = this->capacity.Index(std::move(pos));
218-
                //Do nothing - this area is already constructed.
218+
219-
                preserved++;
219+
220
    }
221-
            else if(newBounds.Contains(point))
221+
222
    //================================================================
223-
                //Needs to be constructed.
223+
224-
                this->construct((*this)[point], value);
224+
225
    { return iterator(*this, 0); }
226-
                constructed++;
226+
227
    { return iterator(*this, this->bounds.Area()); }
228-
            else if(oldBounds.Contains(point))
228+
229
    const_iterator begin() const
230
    { return const_iterator(*this, 0); }
231
    const_iterator end() const
232
    { return const_iterator(*this, this->bounds.Area()); }
233-
                destructed++;
233+
234
    //================================================================
235
    
236
private:
237-
                //Do nothing - this area is already destructed.
237+
238
    //If 'newBounds' is smaller than 'oldBounds', deconstructs the old elements.
239-
                ignored++;
239+
240
    {
241
        //The largest extent of both rects.
242
        cRect totalArea = cRect::Encompassing(oldBounds, newBounds);
243
        //The overlapping portion of both rects (if any).
244
        cRect reducedArea = cRect::Intersection(oldBounds, newBounds);
245
        
246
        //-------------------------------------------
247
        
248
        //If a constructor throws an exception, we want to know which element it was,
249
        //so we keep track of what the last point was before the exception was thrown.
250
        cPoint lastPoint;
251
        
252
        try
253
        {
254
            for(cPoint point : newBounds)
255
            {
256
                if(reducedArea.Contains(point))
257
                {
258
                    //Do nothing - this area is already constructed.
259
                }
260
                else
261
                {
262
                    lastPoint = point;
263
                    
264
                    //Needs to be constructed.
265
                    this->construct((*this)[point], value);
266-
    //Ensures *at least* enough room for 'bounds'.
266+
                }
267
            }
268-
    void ensureCapacity(const cRect &bounds, bool addExtra)
268+
269
        catch(...)
270
        {
271
            //Since we caught an exception, destruct every element we had previously constructed.
272
            for(cPoint point : newBounds)
273
            {
274
                if(reducedArea.Contains(point))
275
                {
276
                    //Do nothing - this area is already constructed.
277
                }
278
                else if(point == lastPoint)
279
                {
280
                    //Stop here - we didn't construct anything beyond this element.
281
                    break;
282
                }
283
                else
284
                {
285
                    //Destruct the element we previously constructed.
286
                    this->destruct((*this)[point]);
287
                }
288
            }
289
            
290
            throw;
291
        }
292
        
293
        //-------------------------------------------
294
        
295
        //Destruct any elements that we no longer want (if we're resizing smaller than our previous size).
296
        for(cPoint point : oldBounds)
297
        {
298
            if(reducedArea.Contains(point))
299
            {
300
                //Do nothing - we're preserving these elements.
301
            }
302
            else
303
            {
304
                //Needs to be destructed.
305
                this->destruct((*this)[point]);
306
            }
307
        }
308
    }
309
    
310
    //================================================================
311
    
312
    //Construct a single element.
313
    void construct(Type &element, const Type &value = Type())
314
    {
315
        new (&element) Type(value); //Call constructor.
316
    } 
317-
           this->memory = this->allocate(newCapacity);
317+
318
    //Constructs an element with move semantics.
319
    void moveConstruct(Type &element, Type &&value)
320
    {
321
        new (&element) Type(value); //Call move constructor.
322
    }
323
324
    //Destruct a single element.
325
    void destruct(Type &element)
326
    {
327
        element.~Type(); //Call destructor.
328
    }
329-
            Type *newMemory = this->allocate(newCapacity);
329+
330
    //================================================================
331
    
332
    //Ensures *at least* enough room for 'bounds'. Returns the resulting capacity.
333
    //If 'addExtra' is true, includes even more capacity for future growth.
334
    cRect ensureCapacity(const cRect &bounds, bool addExtra)
335
    {
336-
            this->moveElements(oldMemory, oldCapacity, newMemory, newCapacity, this->bounds);
336+
337
        if(!this->capacity.Contains(bounds))
338
        {
339
            cRect desiredCapacity = bounds;
340-
            oldMemory = nullptr;
340+
341
            if(addExtra)
342-
            //And store the new pointer.
342+
343-
            this->memory = newMemory;
343+
344
                int quarterWidth = (bounds.size.width / 4) + 1;
345
                int quarterHeight = (bounds.size.height / 4) + 1;
346
                
347
                desiredCapacity.Pad(quarterWidth, quarterWidth, quarterHeight, quarterHeight);
348
            }
349
            
350
            //Allocate and move the elements.
351
            //[CAN THROW - Make sure no member variables are changed before this function call]
352
            this->reallocateMemory(desiredCapacity);
353
            
354
            //Return the new capacity.
355
            return desiredCapacity;
356
        }
357
        
358
        //Return the current capacity.
359
        return this->capacity;
360
    }    
361
    
362
    //================================================================
363
    
364
    //This allocates enough memory for 'capacity', without constructing any elements.
365
    Type *allocate(const cRect &capacity)
366
    {
367
        //Allocate the new memory.
368
        size_t numElements = capacity.size.Area();
369
        size_t numBytes = (sizeof(Type) * numElements);
370
        void *data = ::operator new(numBytes);
371
        
372
        return static_cast<Type*>(data);
373
    }
374
    
375
    //This deallocates 'memory', without calling any destructors.
376
    void deallocate(Type *data)
377
    {
378
        ::operator delete(data);
379
    }
380
    
381
    //================================================================
382
    
383
    //Reallocates the memory and migrates the elements over using moveElements().
384
    //Throws std::bad_alloc if the allocation failed, and throws any exceptions thrown by element constructors.
385
    void reallocateMemory(const cRect &newCapacity)
386-
                size_t newIndex = (row * newStride) + column;
386+
387-
                newIndex += newOffset;
387+
388
        {
389-
                //Construct the new location, and move the element.
389+
390-
                this->moveConstruct(newMemory[newIndex], std::move(oldMemory[oldIndex]));
390+
            //[CAN THROW - Make sure no member variables are changed before this function call]
391
            this->memory = this->allocate(newCapacity);
392
        }
393
        else if(newCapacity.IsEmpty())
394
        {
395
            //Free all the memory.
396
            this->deallocate(this->memory);
397
            this->memory = nullptr;
398
        }
399
        else
400
        {
401
            //Allocate the new memory.
402-
    unsigned int boundsOffset; //The offset of 'bounds' within 'capacity'. Only used for AtIndex, for faster iteration.
402+
            //Note: I put the allocated memory into a unique_ptr here, incase moveElements() throws.
403
            //This way, the new memory is properly released.
404
            //[CAN THROW - Make sure no member variables are changed before this function call]
405
            std::unique_ptr<Type, use_operator_delete> newMemory = this->allocate(newCapacity);
406
407
            //A few extra variables for readability.
408
            Type *oldMemory = this->memory;
409
            const cRect &oldCapacity = this->capacity;
410
            
411
            //Move the elements.
412-
//A random-access iterator for iterating over each element of a Common::Grid.
412+
            //[CAN THROW - Make sure no member variables are changed before this function call]
413-
template <typename Type>
413+
            this->moveElements(oldMemory, oldCapacity, newMemory.get(), newCapacity, this->bounds);
414
415
            //Delete the old memory.
416
            this->deallocate(oldMemory);
417-
    GridIterator(Common::Grid<Type> &grid, unsigned index) : grid(grid), index(index)
417+
418
            //And store the new pointer. Have the unique_ptr release ownership of the memory as well.
419
            this->memory = newMemory.release();
420
        }
421
        
422
        //Record the new capacity.
423
        this->capacity = newCapacity;
424
    }
425
    
426
    //Called by 'reallocateMemory' only.
427
    void moveElements(Type *oldMemory, const cRect &oldCapacity, Type *newMemory,
428
                      const cRect &newCapacity, const cRect &bounds)
429
    {
430
        //Insanity preservation.
431
        //Assert that our elements are actually within the capacity of both the new and old blocks of memory.
432
        BOOST_ASSERT(oldCapacity.Contains(bounds));
433
        BOOST_ASSERT(newCapacity.Contains(bounds));
434
        
435
        //Assert that neither block of memory's size is empty (they have to have a positive non-zero width and height).
436
        BOOST_ASSERT(!oldCapacity.IsEmpty());
437
        BOOST_ASSERT(!newCapacity.IsEmpty());
438
        
439
        //Assert that neither pointer to the allocated memory is empty.
440
        BOOST_ASSERT(oldMemory != nullptr);
441
        BOOST_ASSERT(newMemory != nullptr);
442
        
443
        //The length of each 'row' of the grid's capacity in memory.
444
        size_t oldStride = oldCapacity.size.width;
445
        size_t newStride = newCapacity.size.width;
446
        
447
        //The initial offset of the actual bounds from the memory capacity.
448
        size_t oldOffset = oldCapacity.Index(bounds.position);
449
        size_t newOffset = newCapacity.Index(bounds.position);
450
        
451-
    GridIterator &operator++()
451+
452
        size_t rows = bounds.size.height;
453
        size_t columns = bounds.size.width;
454
        
455
        //=================================================================
456
        
457
        //Incase a constructor throws an exception, keep track of the last row and column.
458-
    GridIterator operator++(int)
458+
        size_t lastRow = 0;
459
        size_t lastColumn = 0;
460
        
461
        try
462
        {
463
            //Move-construct all the new elements.
464
            for(size_t row = 0; row < rows; lastRow = row++)
465-
    GridIterator &operator--()
465+
466
                for(size_t column = 0; column < columns; lastColumn = column++)
467
                {
468
                    size_t oldIndex = (row * oldStride) + column;
469
                    oldIndex += oldOffset;
470
                    
471-
    GridIterator operator--(int)
471+
                    size_t newIndex = (row * newStride) + column;
472
                    newIndex += newOffset;
473
                    
474
                    //Construct the new location, and move the element.
475
                    this->moveConstruct(newMemory[newIndex], std::move(oldMemory[oldIndex]));
476
                }
477
            }
478
        }
479
        catch(...)
480
        {
481-
    GridIterator operator+(int n)
481+
            //Since we just caught an exception thrown from one of the element move-constructors,
482
            //destruct all the new elements we just constructed, up until the failed constructor.
483
            for(size_t row = 0; row < lastRow; row++)
484
            {
485
                for(size_t column = 0; column < lastColumn; column++)
486
                {
487
                    size_t newIndex = (row * newStride) + column;
488-
    GridIterator &operator+=(int n)
488+
                    newIndex += newOffset;
489
                    
490
                    //Destruct the element we constructed in the previous for() loops.
491
                    this->destruct(newMemory[newIndex]);
492
                }
493
            }
494
            
495-
    GridIterator operator-(int n)
495+
            throw;
496
        }
497
        
498
        //=================================================================
499
        
500
        //Destruct all the old elements.
501
        for(size_t row = 0; row < rows; row++)
502-
    GridIterator &operator-=(int n)
502+
503
            for(size_t column = 0; column < columns; column++)
504
            {
505
                size_t oldIndex = (row * oldStride) + column;
506
                oldIndex += oldOffset;
507
                
508
                //Destruct old location.
509
                this->destruct(oldMemory[oldIndex]);
510
            }
511-
    Common::Grid<Type> &grid;
511+
512
    }
513
514
    //================================================================
515
    
516
private:
517
    Type *memory;
518
    
519
    cRect bounds; //Current grid boundries.
520
    cRect capacity; //Currently allocated memory capacity, garunteed to be at least 'bounds' and maybe larger.
521
};
522
523
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
524
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX//
525
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
526
527
/*
528
    A random-access iterator for iterating over each element of a Common::Grid.
529
530
    I'm doing a trick here, by taking the type of the container as a second template parameter.
531
    This allows me to do this:
532
        typedef GridIterator<const Type, const Grid<Type> > const_iterator;
533
    
534
    ...instead of having to create two seperate iterator classes (one for regular iterator and one for const).
535
*/
536
template <typename Type, typename ContainerType = typename Common::Grid<Type> >
537
class GridIterator
538
{
539
public: 
540
    GridIterator(ContainerType &grid, unsigned index) : grid(grid), index(index)
541
    {
542
        this->maxIndex = this->grid.GetBounds().Area();
543
    }
544
    
545
    //================================================================
546
    
547
    //De-reference.
548
    Type &operator*()
549
    {
550
        return this->grid.AtIndex(this->index);
551
    }
552
    Type &operator->()
553
    {
554
        return (operator*());
555
    }
556
    
557
    //================================================================
558
    
559
    //Comparison.
560
    bool operator==(const GridIterator &other) const
561
    {
562
        return (&this->grid == &other.grid) && (this->index == other.index);
563
    }
564
    
565
    bool operator!=(const GridIterator &other) const
566
    {
567
        return !operator==(other);
568
    }
569
    
570
    //================================================================
571
    
572
    //Increment/decrement.
573
    GridIterator &operator++() 
574
    {
575
        this->index++;
576
        Limit(this->index, this->maxIndex);
577
        return *this;
578
    }
579
    
580
    GridIterator operator++(int) 
581
    {
582
        GridIterator temp(*this);
583
        ++(*this);
584
        return temp;
585
    }
586
    
587
    GridIterator &operator--() 
588
    {
589
        this->index--;
590
        return *this;
591
    }
592
    
593
    GridIterator operator--(int) 
594
    {
595
        GridIterator temp(*this);
596
        --(*this);
597
        return temp;
598
    }
599
    
600
    //================================================================
601
602
    //Random access.
603
    GridIterator operator+(int n) const
604
    {
605
        GridIterator temp(*this);
606
        temp.index += n;
607
        return temp;
608
    }
609
    
610
    GridIterator &operator+=(int n) 
611
    {
612
        this->index += n;
613
        Limit(this->index, this->maxIndex);
614
        return *this;
615
    }
616
    
617
    GridIterator operator-(int n) const
618
    {
619
        GridIterator temp(*this);
620
        temp.index -= n;
621
        return temp;
622
    }
623
    
624
    GridIterator &operator-=(int n) 
625
    {
626
        this->index -= n;
627
        return *this;
628
    }
629
    
630
    //================================================================
631
    
632
private:
633
    ContainerType &grid;
634
635
    unsigned index; //The current index.
636
    unsigned maxIndex; //The maximum value (and the result returned by 'std::end()'.
637
};
638
639
//================================================================
640
641
} //End of namespace.
642
643
#endif // COMMON_CONTAINERS_GRID_H