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 |