View difference between Paste ID: 5iSZnj8F and VRQ2YwYp
SHOW: | | - or go back to the newest paste.
1
template<typename Type>
2
class Grid
3
{
4
public:
5
    Grid() : memory(nullptr)
6
    {   }
7
    
8
    //Construct and call Resize().
9
    Grid(const cRect &newBounds, const Type &value = Type()) : memory(nullptr)
10
    {
11
        this->Resize(newBounds, value);
12
    }
13
    
14
    ~Grid()
15
    {
16
        //Clear the grid, calling the destructors on any constructed elements.
17
        this->Clear();
18
        
19
        //Deallocate any reserved memory.
20
        this->deallocate(this->memory);
21
    }
22
    
23
    //Erases the grid, resetting the bounds to (0,0,0,0).
24
    //Does not free the reserved capacity. Call Conserve() afterward to do that.
25
    void Clear()
26
    {
27
        this->Resize(cRect(0,0,0,0));
28
    }
29
    
30
    //================================================================
31
    
32
    //Resizes the grid to 'newBounds', constructing the new elements with 'value' (or the default constructor if 'value' is not set).
33
    //If Grid doesn't have enough capacity, it'll reallocate storage and move the elements to new memory addresses.
34
    //Throws std::bad_alloc if the allocation failed, and rethrows any exceptions thrown by element constructors or destructors.
35
    void Resize(const cRect &newBounds, const Type &value = Type())
36
    {
37
        try
38
        {
39
            //[CAN THROW - Make sure no member variables are changed before these function calls]
40
            cRect newCapacity = this->ensureCapacity(newBounds, true);
41
            
42
            //[CAN THROW - Make sure no member variables are changed before these function calls]
43
            this->constructAdditions(this->bounds, newBounds, value);
44
            
45
            //Record the new capacity, after we are sure no exceptions were thrown and everything succeeded.
46
            this->capacity = newCapacity;
47
        }
48
        catch(std::exception &exception)
49
        {
50
            Log::Message("...blah..."); //exception.what()
51
            throw;
52
        }
53
        catch(...)
54
        {
55
            Log::Message("...blah..."); //Unknown exception
56
            throw;
57
        }
58
59
        this->bounds = newBounds;
60
    }
61
62
    //================================================================
63
    
64
private:
65
    //Constructs all the new elements between oldBounds and newBounds setting them to 'value'.
66
    //If 'newBounds' is smaller than 'oldBounds', deconstructs the old elements.
67
    void constructAdditions(const cRect &oldBounds, const cRect &newBounds, const Type &value = Type())
68
    {
69
        //The largest extent of both rects.
70
        cRect totalArea = cRect::Encompassing(oldBounds, newBounds);
71
        //The overlapping portion of both rects (if any).
72
        cRect reducedArea = cRect::Intersection(oldBounds, newBounds);
73
        
74
        //-------------------------------------------
75
        
76
        //If a constructor throws an exception, we want to know which element it was,
77
        //so we keep track of what the last point was before the exception was thrown.
78
        cPoint lastPoint;
79
        
80
        try
81
        {
82
            for(cPoint point : newBounds)
83
            {
84
                if(reducedArea.Contains(point))
85
                {
86
                    //Do nothing - this area is already constructed.
87
                }
88
                else
89
                {
90
                    lastPoint = point;
91
                    
92
                    //Needs to be constructed.
93
                    this->construct((*this)[point], value);
94
                }
95
            }
96
        }
97
        catch(...)
98
        {
99
            //Since we caught an exception, destruct every element we had previously constructed.
100
            for(cPoint point : newBounds)
101
            {
102
                if(reducedArea.Contains(point))
103
                {
104
                    //Do nothing - this area is already constructed.
105
                }
106
                else if(point == lastPoint)
107
                {
108
                    //Stop here - we didn't construct anything beyond this element.
109
                    break;
110
                }
111
                else
112
                {
113
                    //Destruct the element we previously constructed.
114
                    this->destruct((*this)[point]);
115
                }
116
            }
117
            
118
            throw;
119
        }
120
        
121
        //-------------------------------------------
122
        
123
        //Destruct any elements that we no longer want (if we're resizing smaller than our previous size).
124
        for(cPoint point : oldBounds)
125
        {
126
            if(reducedArea.Contains(point))
127
            {
128
                //Do nothing - we're preserving these elements.
129
            }
130
            else
131
            {
132
                //Needs to be destructed.
133
                this->destruct((*this)[point]);
134
            }
135
        }
136
    }
137
    
138
    //================================================================
139
    
140
    //Construct a single element.
141
    void construct(Type &element, const Type &value = Type())
142
    {
143
        new (&element) Type(value); //Call constructor.
144
    } 
145
    
146
    //Constructs an element with move semantics.
147
    void moveConstruct(Type &element, Type &&value)
148
    {
149
        new (&element) Type(value); //Call move constructor.
150
    }
151
152
    //Destruct a single element.
153
    void destruct(Type &element)
154
    {
155
        element.~Type(); //Call destructor.
156
    }
157
    
158
    //================================================================
159
    
160
    //Ensures *at least* enough room for 'bounds'. Returns the resulting capacity.
161
    //If 'addExtra' is true, includes even more capacity for future growth.
162
    cRect ensureCapacity(const cRect &bounds, bool addExtra)
163
    {
164
        //Check whether we have enough capacity to resize.
165
        if(!this->capacity.Contains(bounds))
166
        {
167
            cRect desiredCapacity = bounds;
168
            
169
            if(addExtra)
170
            {
171
                //If we're bothering to grow in size, we might as well reserve a little extra for future growth.
172
                int quarterWidth = (bounds.size.width / 4) + 1;
173
                int quarterHeight = (bounds.size.height / 4) + 1;
174
                
175
                desiredCapacity.Pad(quarterWidth, quarterWidth, quarterHeight, quarterHeight);
176
            }
177
            
178
            //Allocate and move the elements.
179
            //[CAN THROW - Make sure no member variables are changed before this function call]
180
            this->reallocateMemory(desiredCapacity);
181
            
182
            //Return the new capacity.
183
            return desiredCapacity;
184
        }
185
        
186
        //Return the current capacity.
187
        return this->capacity;
188
    }    
189
    
190
    //================================================================
191
    
192
    //This allocates enough memory for 'capacity', without constructing any elements.
193
    Type *allocate(const cRect &capacity)
194
    {
195
        //Allocate the new memory.
196
        size_t numElements = capacity.size.Area();
197
        size_t numBytes = (sizeof(Type) * numElements);
198
        void *data = ::operator new(numBytes);
199
        
200
        return static_cast<Type*>(data);
201
    }
202
    
203
    //This deallocates 'memory', without calling any destructors.
204
    void deallocate(Type *data)
205
    {
206
        ::operator delete(data);
207
    }
208
    
209
    //================================================================
210
    
211
    //Reallocates the memory and migrates the elements over using moveElements().
212
    //Throws std::bad_alloc if the allocation failed, and throws any exceptions thrown by element constructors.
213
    void reallocateMemory(const cRect &newCapacity)
214
    {
215
        if(!this->memory)
216
        {
217
            //If we don't have any memory, just allocate some and don't worry about moving any elements.
218
            //[CAN THROW - Make sure no member variables are changed before this function call]
219
            this->memory = this->allocate(newCapacity);
220
        }
221
        else if(newCapacity.IsEmpty())
222
        {
223
            //Free all the memory.
224
            this->deallocate(this->memory);
225
            this->memory = nullptr;
226
        }
227
        else
228
        {
229
            //Allocate the new memory.
230
            //Note: I put the allocated memory into a unique_ptr here, incase moveElements() throws.
231
            //This way, the new memory is properly released.
232
            //[CAN THROW - Make sure no member variables are changed before this function call]
233
            std::unique_ptr<Type, use_operator_delete> newMemory = this->allocate(newCapacity);
234
235
            //A few extra variables for readability.
236
            Type *oldMemory = this->memory;
237
            const cRect &oldCapacity = this->capacity;
238
            
239
            //Move the elements.
240
            //[CAN THROW - Make sure no member variables are changed before this function call]
241
            this->moveElements(oldMemory, oldCapacity, newMemory.get(), newCapacity, this->bounds);
242
243
            //Delete the old memory.
244
            this->deallocate(oldMemory);
245
            
246
            //And store the new pointer. Have the unique_ptr release ownership of the memory as well.
247
            this->memory = newMemory.release();
248
        }
249
        
250
        //Record the new capacity.
251
        this->capacity = newCapacity;
252
    }
253
    
254
    //Called by 'reallocateMemory' only.
255
    void moveElements(Type *oldMemory, const cRect &oldCapacity, Type *newMemory,
256
                      const cRect &newCapacity, const cRect &bounds)
257
    {
258
        //Insanity preservation.
259
        //Assert that our elements are actually within the capacity of both the new and old blocks of memory.
260
        BOOST_ASSERT(oldCapacity.Contains(bounds));
261
        BOOST_ASSERT(newCapacity.Contains(bounds));
262
        
263
        //Assert that neither block of memory's size is empty (they have to have a positive non-zero width and height).
264
        BOOST_ASSERT(!oldCapacity.IsEmpty());
265
        BOOST_ASSERT(!newCapacity.IsEmpty());
266
        
267
        //Assert that neither pointer to the allocated memory is empty.
268
        BOOST_ASSERT(oldMemory != nullptr);
269
        BOOST_ASSERT(newMemory != nullptr);
270
        
271
        //The length of each 'row' of the grid's capacity in memory.
272
        size_t oldStride = oldCapacity.size.width;
273
        size_t newStride = newCapacity.size.width;
274
        
275
        //The initial offset of the actual bounds from the memory capacity.
276
        size_t oldOffset = oldCapacity.Index(bounds.position);
277
        size_t newOffset = newCapacity.Index(bounds.position);
278
        
279
        //The number of rows and columns of actual elements we need to move.
280
        size_t rows = bounds.size.height;
281
        size_t columns = bounds.size.width;
282
        
283
        //=================================================================
284
        
285
        //Incase a constructor throws an exception, keep track of the last row and column.
286
        size_t lastRow = 0;
287
        size_t lastColumn = 0;
288
        
289
        try
290
        {
291
            //Move-construct all the new elements.
292
            for(size_t row = 0; row < rows; lastRow = row++)
293
            {
294
                for(size_t column = 0; column < columns; lastColumn = column++)
295
                {
296
                    size_t oldIndex = (row * oldStride) + column;
297
                    oldIndex += oldOffset;
298
                    
299
                    size_t newIndex = (row * newStride) + column;
300
                    newIndex += newOffset;
301
                    
302
                    //Construct the new location, and move the element.
303
                    this->moveConstruct(newMemory[newIndex], std::move(oldMemory[oldIndex]));
304
                }
305
            }
306
        }
307
        catch(...)
308
        {
309
            //Since we just caught an exception thrown from one of the element move-constructors,
310
            //destruct all the new elements we just constructed, up until the failed constructor.
311
            for(size_t row = 0; row < lastRow; row++)
312
            {
313-
                for(size_t column = 0; column < lastColumn; column++)
313+
		//Go to the end of every row, but on the last row, just go to the last element we encountered.
314
                int &endOfColumn = (row == lastRow? lastColumn : columns);
315
		
316
                for(size_t column = 0; column < endOfColumn; column++)
317
                {
318
                    size_t newIndex = (row * newStride) + column;
319
                    newIndex += newOffset;
320
                    
321
                    //Destruct the element we constructed in the previous for() loops.
322
                    this->destruct(newMemory[newIndex]);
323
                }
324
            }
325
            
326
            throw;
327
        }
328
        
329
        //=================================================================
330
        
331
        //Destruct all the old elements.
332
        for(size_t row = 0; row < rows; row++)
333
        {
334
            for(size_t column = 0; column < columns; column++)
335
            {
336
                size_t oldIndex = (row * oldStride) + column;
337
                oldIndex += oldOffset;
338
                
339
                //Destruct old location.
340
                this->destruct(oldMemory[oldIndex]);
341
            }
342
        }
343
    }
344
345
    //================================================================
346
    
347
private:
348
    Type *memory;
349
    
350
    cRect bounds; //Current grid boundaries.
351
    cRect capacity; //Currently allocated memory capacity, guaranteed to be at least 'bounds' and maybe larger.
352
};