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