I am doing the "Game of life" as an assignment for the college, the tutor has given to us part of the code, but I am having problem for understand two parts.
First:
grid(int scale) : scale_(scale), width_(problem_definition::width(scale)), height_(problem_definition::height(scale)), data_(new problem_definition::data_type[width_*height_])
Until I can understand is the moment that he requests for the memory which is used for the grid for the simulation. However, I can not grasp the syntax beyond the new. why are those brackets with an empty space? what does mean the colon after grid (int scale)?
Second:
void initialize()
{
for (std::size_t y = 0; y < height_; ++y)
{
for (std::size_t x = 0; x < width_; ++x)
{
at(x,y) = problem_definition::initialize(x, y, scale_);
}
}
}
what is this "at()" inside the second for loop?
I would appreciate any insight here is the full struct definition:
struct grid
{
grid(int scale) : scale_(scale), width_(problem_definition::width(scale)), height_(problem_definition::height(scale)), data_(new problem_definition::data_type[width_*height_])
{
}
void initialize()
{
for (std::size_t y = 0; y < height_; ++y)
{
for (std::size_t x = 0; x < width_; ++x)
{
at(x,y) = problem_definition::initialize(x, y, scale_);
}
}
}
problem_definition::data_type at(std::size_t x, std::size_t y, int dx, int dy)
{
if (dx<0 && -dx>x) return problem_definition::out_of_bounds_value();
if (dy<0 && -dy>y) return problem_definition::out_of_bounds_value();
if (x + dx >= width_) return problem_definition::out_of_bounds_value();
if (y + dy >= height_) return problem_definition::out_of_bounds_value();
return at((int)x + dx, (int)y + dy);
}
problem_definition::data_type& at(std::size_t x, std::size_t y)
{
return data_[x + y*width_];
}
void swap(grid& other)
{
std::swap(scale_, other.scale_);
std::swap(width_, other.width_);
std::swap(height_, other.height_);
std::swap(data_, other.data_);
}
void print(std::ostream& str)
{
for (std::size_t y = 0; y < height_; ++y)
{
for (std::size_t x = 0; x < width_; ++x)
{
str << at(x, y);
}
str << std::endl;
}
}
private:
int scale_;
std::size_t width_;
std::size_t height_;
std::unique_ptr<problem_definition::data_type[]> data_;
};
Let's add some white space to the first line to make it easier to read:
grid(int scale) : scale_(scale),
width_(problem_definition::width(scale)),
height_(problem_definition::height(scale)),
data_(new problem_definition::data_type[width_*height_])
Now we can easily see the colon (a : is a colon, a ; is a semicolon) defines an initialiser list which takes a series of var(value) pairs and assigns each value to each var.
I don't see any "brackets with an empty space" so can't answer that. But beyond new is just a standard array type of the form: element_type[length]. The element type is problem_definition::data_type and the array is of length width_ times height_.
Finally, at() is just a function call that returns a reference. If you study the function definition:
problem_definition::data_type& at(std::size_t x, std::size_t y)
{
return data_[x + y*width_];
}
then you can see the line with the call to at(), via the magic of references, is the same as the following:
data_[x + y*width_] = problem_definition::initialize(x, y, scale_);
Related
I want to write a fuction to creat a 3D Matrix,but I have a broblem when I try to display it,Please help me,Thanks.
My teacher gave me his codes.His codes can create a 2D array which has Dynamic memory, I want to change his codes to creat a 3D matrix. When I tried to display the Matrix,I realize the array I created is a 2D array,And Because I just begin to use C++,I can't find my mistake.
/*<Array3D.h>*/
#pragma once
#ifndef _ARRAY_3D_H_
#define _ARRAY_3D_H_
//-----------------------------------------
#include <cassert>
#include <vector>
using namespace std;
template<typename T>
class Array3D
{
public:
typedef Array3D<T> _Myt;
Array3D()
: m_nRows(0)
, m_nCols(0)
, m_nDepths(0)
{}
Array3D(size_t r, size_t c,size_t d)
{
Resize(r, c, d);
}
//Allocating memory size
void Resize(size_t r, size_t c,size_t d)
{
if (r == m_nRows && c == m_nCols && d==m_nDepths) { return; }
bool bValid = r > 0 && c > 0 && d>0;
if (!bValid) return;
m_nRows = r;
m_nCols = c;
m_nDepths = d;
m_v.resize(m_nRows * m_nCols * m_nDepths);
}
const T* operator[](size_t r) const
{
assert(r >= 0 && r < m_nRows);
return &(*(m_v.begin() + (r * m_nCols * m_nDepths)));
}
T* operator[](size_t r)
{
assert(r >= 0 && r < m_nRows);
return &(*(m_v.begin() + (r * m_nCols * m_nDepths)));
}
//Gets the start position of a one-dimensional array
const T* GetRawPointer() const { return &(m_v[0]); }
T* GetRawPointer() { return &(m_v[0]); }
long Rows() const
{
return static_cast<long>(m_nRows);
}
long Cols() const
{
return static_cast<long>(m_nCols);
}
long Depths() const
{
return static_cast<long>(m_nDepths);
}
long TotalSize() const
{
return static_cast<long>(m_nRows * m_nCols * m_nDepths);
}
void ClearUp()
{
m_nRows = 0;
m_nCols = 0;
m_nDepths = 0;
m_v.clear();
}
bool IsEmpty() const { return m_v.empty(); }
protected:
vector<T> m_v;// Internally a one-dimensional is used
size_t m_nRows;
size_t m_nCols;
size_t m_nDepths;
};
<Matrix3D.h>
#pragma once
#include "Array3D.h"
class Matrix3D
{
public:
Matrix3D(int r = 0, int c = 0, int d = 0)
{
setSize(r, c, d);
}
void setSize(int r, int c, int d) { m_data3D.Resize(r, c, d); }
void clear() { m_data3D.ClearUp(); }
int rows() const { return m_data3D.Rows(); }
int cols() const { return m_data3D.Cols(); }
int Depths() const { return m_data3D.Depths(); }
void display() const;
//Operator Overloading
float* operator[](int rIndex) { return m_data3D[rIndex]; }
const float* operator[](int rIndex) const { return m_data3D[rIndex]; }
private:
Array3D<float> m_data3D;
};
#include "Matrix3D.h"
#include <iostream>
using namespace std;
void Matrix3D::display() const
{
if (m_data3D.IsEmpty())
{
cout << "empty matrix" << endl;
return;
}
cout << "------------------------" << endl;
const int rows = this->rows();
const int cols = this->cols();
const int Depths = this->Depths();
cout << rows << "x" << cols << "x" << Depths << endl;
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
for(int k = 0 ;k < Depths; k++)
{
cout << m_data3D[i][j][k] << ' '; /*This section will pose an error "E0142"*/
}
cout << endl;
}
}
}
I am going to guess that your teachers class was Array2D and that you mostly just added m_nDepths to it to turn it into Array3D. If this assumption is not right, then my answer here is most likely going to be completely wrong.
But if I am right, then his operator[] looked something like:
T* operator[](size_t r)
{
assert(r >= 0 && r < m_nRows);
return &(*(m_v.begin() + (r * m_nCols)));
}
Which means that when you then do m_data2D[i][j], what happens is that the first [i] applies to the Array2D. This, as shown above, returns a pointer (in your case likely a float*). The [j] is then applied to this pointer, which results in some pointer arithmetic that results in a float (for a float *x; x[y] means *(x+y)).
In other words, your teacher stores his 2D array line by line in a 1D array, and when you [] into it you get a pointer to the right line that you can then further [] into.
This is all fine.
The problem is when you add a third dimension to this and try the same approach: The first [] still returns a float* (but this time to the correct 2D matrix), the second [] returns a float (the first element in the correct line of the 2D matrix), and the third [] tries to apply to a float - which it cannot, and you get the error.
There are two ways to fix this:
Change the return type of Array3D::operator[] to return some kind of 2D array type whose operator[] works like the original Array2D.
Abandon the operator[] approach and replace it with a member function that takes 3 arguments and returns the correct element right away. I think this is what I would prefer myself.
For the second option, something like (not tested, rather large chance I messed up the order of the arguments):
T element(size_t x, size_t y, size_t z) const
{
assert(x >= 0 && x < m_nDepths);
assert(y >= 0 && y < m_nCols);
assert(z >= 0 && z < m_nRows);
return *(m_v.begin() + (x * m_nCols * m_nDepths +
y * m_nCols +
z));
}
T& element(size_t x, size_t y, size_t z)
{
assert(x >= 0 && x < m_nDepths);
assert(y >= 0 && y < m_nCols);
assert(z >= 0 && z < m_nRows);
return *(m_v.begin() + (x * m_nCols * m_nDepths +
y * m_nCols +
z));
}
Which turns cout << m_data3D[i][j][k] << ' '; into cout << m_data3D.element(i,j,k) << ' ';
In my project, I had an error with a vector that I solved by including this line in the header file:
std::vector<std::vector<int, int>, int> cells;
However, this produced 181 errors, all of them found either in the 'vector' file or the 'xmemory' file. How do I fix these errors?
The first of these errors is: '_Alloc': must be a class or namespace when followed by '::'
My two files:
Cells.cpp
#include "Cells.h"
#include <vector>
vector<vector<int, int>, int> cells; // [[x, y], state]
// initiates the list of cells
void Cells::init(int w, int h, int s) {
cells.clear();
for (int y = s; y <= h; y += s) {
for (int x = s; x <= w; x += s) {
cells.push_back({ x, y }, 0);
}
}
}
// finds the index of a cell given its coordinates
int find(int x, int y) {
for (int i = 0; i < cells.size(); i++) {
if (cells[i][0] == { x, y }) {
return i
}
}
}
void Cells::setState(int x, int y, int s) {
cells[find(x, y)][1] = s;
}
// the logic of the game
int Cells::brain(int x, int y, int s) { // 1 alive 0 dead, s = size + space
vector<int, int, int, int> around;
int tAlive;
// getting the states of all the cells in a 1 cell radius around the focused one
around.push_back(cells[find(x - s, y - s)][1]);
around.push_back(cells[find(x, y - s)][1]);
around.push_back(cells[find(x + s, y - s)][1]);
around.push_back(cells[find(x - s, y)][1]);
around.push_back(cells[find(x + s, y)][1]);
around.push_back(cells[find(x - s, y + s)][1]);
around.push_back(cells[find(x, y + s)][1]);
around.push_back(cells[find(x + s, y + s)][1]);
for (int i = 0; i < 8; i++) {
if (around[i] == 1) {
tAlive++;
}
}
if (cells[find(x, y)][1] == 1) {
if (tAlive <= 1 || tAlive >= 4) {
cells[find(x, y)][1] = 0;
} else {
cells[find(x, y)][1] = 1;
}
} else {
if (tAlive == 3) {
cells[find(x, y)][1] = 1;
}
}
return cells[find(x, y)][1];
}
Cells.h
#pragma once
#include <vector>
class Cells {
public:
std::vector<std::vector<int, int>, int> cells;
void init(int w, int h, int s);
int brain(int x, int y, int s);
void setState(int x, int y, int s);
};
Firstly, vector> isn’t allowed before C++11 standard. So, you could select Properties->Configuration Properties->C/C++->Language->C++ Language Standard and set ISO C++17 Standard (/std:c++17).
Secondly, you could add std:: before vector and ; after return i.
Finally , if you use vector,int>>, you could change cells.push_back({ x, y }, 0); to cells.push_back(make_pair(make_pair(x, y), 0)); and change cells[i][0] == { x, y } to cells.at[i].first.first == x&& cells.at[i].first.second==y.
I'm trying to code a function that, given an index as the input argument, extract the corresponding layer from a 3D matrix, returning a 2D matrix.
My default 3D matrix constructor looks something like this:
Matrice3D(unsigned int depth, unsigned int height, unsigned int width, const T &value) : _3D_matrix(0), _height(0), _width(0), _depth(0) {
try {
_3D_matrix = new T[height * width * depth];
for (int z = 0; z < depth; z++) {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
_3D_matrix[y * width * depth + x * depth + z] = value;
}
}
}
}
catch(...) {
delete[] _3D_matrix;
throw;
}
_height = height;
_width = width;
_depth = depth;
}
(the try/catch is still a wip, I know it's not a thing to do).
So far, I've coded this:
void slice(int index) {
try{
_2D_matrix = new T[_height * _width];
}catch(...){
delete[] _2D_matrix;
_height = 0;
_width = 0;
_depth = 0;
throw;
}
_depth = 1;
for (int k = 0; k< _depth; k++) {
for (int j = 0; j< _height; j++) {
for (int i = 0; i< _width; i++) {
_2D_matrix[j * _width + i] =
_3D_matrix[j * _width * _depth + i * _depth + index];
}
}
}
}
I guess the logic behind the assignment done with the nested for cycles is right, but I don't really know how to return the new matrix.
From the main, used to test the code, I'm calling
std::cout << "--------TEST SLICE------------" << std::endl;
Matrice3D<int> m1(3,3,3,17);
std::cout << "Setter su (0,0,2,99)" << std::endl;
m1(0,0,2,91); //setting a value
m1.slice(2);
std::cout << "Matrix obtained from slicing the layer 2: " <<std::endl;
std::cout << m1;
but I keep getting the first layer of the matrix, whatever index I choose for the input.
Create a new class Matrice2D and return that in slice().
The reason why your get total garbage in your code is that you destroy the _depth of the 3D matrix. It's not even the first layer but really just garbage.
The only time new and delete should appear is in classes named something_ptr. You don't need raw pointers here, and you should be returning a 2DMatrix from slice.
template <typename T>
class 2DMatrix;
template <typename T>
class 3DMatrix {
std::vector<T> data;
std::size_t height, width, depth;
public:
3DMatrix(std::size_t height, std::size_t width, std::size_t depth, T value = {})
: data(height * width * depth, value),
height(height),
width(width),
depth(depth)
{}
2DMatrix<T> slice(std::size_t index) {
2DMatrix<T> result(height, width);
for (std::size_t i = index; i < data.size(); i += depth) {
result.data[i / depth] = data[i];
}
return result;
}
// other members
}
template <typename T>
class 2DMatrix {
std::vector<T> data;
std::size_t height, width;
friend class 3DMatrix<T>;
public:
2DMatrix(std::size_t height, std::size_t width, T value = {})
: data(height * width, value),
height(height),
width(width)
{}
// other members
}
I have declared a class Image with two operator() functions, one for read-only, the other for read-and-write access.
Here an extract:
class Image {
//...
public:
uint16_t operator()(int x, int y) const
{
return data_[x + y*width_]; // read-only at pixle (x,y)
}
uint16_t & operator()(int x, int y)
{
return data_[x + y*width_]; // read/write to pixle (x,y)
}
//...
}
After this, I declared an object of Image in the main() function and wrote to it (which has to work because of the mentioned public Interface operator()), but several compilers only keep recognizing the first operator() function which has only permission to read.
Example of this:
if (count_alive_neighbors(image, i, j) == 3) {
image(i, j) = 255;
}
My thought was that maybe one could overcome this problem by declaring a pointer and by this changing the value. Code for this:
uint16_t* f = &image(i, j);
*f = 255;
On Microsoft Visual Studio 2015 this first worked tested alone outside the if-loop, but inside the just mentioned function it didn't. But it's not a compiler error, I've tested it with Clang, g++ and MinGW.
All are printing out an error message like this:
error: lvalue required as unary '&' operand
uint16_t* f = &(image(i, j));
^
To sum it up, the question is the following: how could one overcome this problem by not focusing on that pointer declarations, what can one do to tell the compiler which version of operator() it has to use? It doesn't recognize it on its own or maybe I'm not recognizing which settings/code one has to change to make the program work.
Thanks in advance.
edit: whole class definition and function
#include <vector>
#include <string>
#include <stdexcept>
#include <fstream>
#include <cstdint>
class Image
{
int width_;
int height_;
std::vector<uint16_t> data_;
public:
Image()
: width_(0)
, height_(0)
{}
Image(unsigned int width, unsigned int height)
: width_(width)
, height_(height)
, data_(width*height, uint16_t())
{}
int width() const
{
return width_;
}
int height() const
{
return height_;
}
int size() const
{
return width_*height_;
}
void resize(unsigned int new_width, unsigned int new_height)
{
data_.resize(new_width*new_height, uint16_t());
width_ = new_width;
height_ = new_height;
}
uint16_t operator()(int x, int y) const
{
return data_[x + y*width_];
}
uint16_t & operator()(int x, int y)
{
return data_[x + y*width_];
}
uint16_t get_periodic(int x, int y) const
{
int xres = x % width_;
int yres = y % height_;
return data_[xres + yres*width_];
}
};
int count_alive_neighbors(Image const & image, int x, int y) {
int res = 0;
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
if (image.get_periodic(x + i, y + j) == 255) {
res += 1;
}
}
}
if (image.get_periodic(x, y) == 255) {
res -= 1;
}
}
Image conway_step(Image const & image) {
for (int i = 0; i <= image.width(); i++) {
for (int j = 0; j <= image.height(); j++) {
if (count_alive_neighbors(image, i, j) == 2)
{
}
if (count_alive_neighbors(image, i, j) == 3) {
image(i, j) = 255;
}
if (count_alive_neighbors(image, i, j) < 2) {
image(i, j) = 0;
}
if (count_alive_neighbors(image, i, j) > 3) {
image(i, j) = 255;
}
}
}
}
int main() {
Image img(3, 3); // declaring object
img(3, 3) = 255; /* !ATTENTION! this works, but inside
conway_steps it doesn't */
}
[Based on the comments] conway_step is supposed to make a copy of its argument, modify that copy, and return the copy. Your problem is that you don't make the copy and try to modify the original.
Please change your function signature from
Image conway_step(Image const & image)
to
Image conway_step(Image & const image) //ill formed
or in a better way
Image conway_step(Image & image)
In first case, it is equivalent to const Image & image, so it calls the const qualified operator => image(i, j)=255; => uint16_t=255 => left operand must be l-value error
In second/third case, you declare a const reference to a non const object.
I have a class Object, which has a vec3 attribute to store its position
class Object{
public:
Object();
~Object();
glm::vec3 position;
virtual float getX(); //these methods get the x, y and z value of the vec3 position
virtual float getY();
virtual float getZ();
private:
};
Then I have a class Linker, which would "link" Objects, based on their positions.
class Linker
{
Object *obj;
public:
Linker(Object *obj);
virtual void link(Object *o); //this method should perform actions based on Object position
};
In my main.cpp I create few Objects and I'm storing them in a std::vector
static vector<unique_ptr<Object>> allObj;
static vector<Linker> allLinkers;
unique_ptr<Object> createNewObj(int x, int y, int z) {
unique_ptr<Object> obj(new Object());
obj->position(vec3(x, y, z));
return obj;
}
void createPattern()
{
for (int x = 0; x < 3; x++)
{
for (int z = 0; z < 3; z++)
{
allObj.push_back(createNewObj(x, 1.0f, z));
}
}
for (auto &p : allObj) {
Linker lnkr = Linker(p);
//EDIT
allLinkers.push_back(lnkr);
}
}
void linkPattern()
{
for (int i = 0; i < allObj.size() - 1; i++)
{
auto p = allObj[i+1]; //this is where my problem comes up
allLinkers[i].link(p); //takes the linker of the first object and "links" it with the second
}
}
The nested loop in createPattern() creates a grid of Objects. I would like to link Objects based on their position, and not only allObj[i+1], but I would like to be able to link the Object with vec3 position = <0.0, 1.0, 0.0> like:
And I would like to do the same with every other Object and its neighbours.
My loop creates really few Objects at the moment, but I probably need to create a huge amount of them later.
In this case, is std::vector the best way to store Objects?
Is there a way to store them so I can directly access them by their position?
I was dealing with similar problem in this question. I also wrote an answer on how I solved my problem. So basically I've created my own container consisting of multiple private ones, which you can access by pubic methods. In my case these being overloaded operator() for a direct X/Y access. In your case the base structure holding your data would be compound vector of unique pointers and for direct access you can make overloaded operator()(unsigned x, unsigned y, unsigned z) that would look something like this:
class Grid {
public:
Object& operator()(unsigned x, unsigned y, unsigned z) noexcept {
return *_data[z][y][x];
}
// same method returning const reference(read-only)
const Object& operator()(unsigned x, unsigned y, unsigned z) const noexcept {
return *_data[z][y][x];
}
/* Safer but throws std::out_of_range exception, which you should handle
Object& operator()(unsigned x, unsigned y, unsigned z) {
return *_data.at(z).at(y).at(z);
}
*/
private:
vector<vector<vector<unique_ptr<Object> > > > _data;
}
This way you can directly give the Linker objects by their X/Y/Z position. Hope this solves your problem.
P.S.: Instead of vector<vector<vector... you could use simple vector<unique_ptr<Object>> but in that case for operator() you would return something like _data[x + y * width + z * height * width] but I'm not quite sure if this it the right formula for object from 3D matrix on pos x/y/z. For 2D matrix it would be _data[x + y * width]
EDIT: Implementation:
class Grid {
public:
// Constructor fills the Grid with Objects created from theirs default constructor
Grid(unsigned width, unsigned height, unsigned depth)
: _width(width), _height(height), _depth(depth) {
_data.resize(depth);
for (unsigned i = 0; i < depth; ++i) {
_data[i].resize(height);
for (unsigned j = 0; i < height; ++i)
_data[i][j].push_back(make_unique<Object>());
// Calls a consturctor of Object
// If you don't plan on filling every single position you can instead fill it with nullptr to save memory
}
}
Object& operator()(unsigned x, unsigned y, unsigned z) {
return *_data[z][y][x];
}
unsigned size() { return _width * _height * _depth; }
unsigned width() { return _width; }
unsigned height() { return _height; }
unsigned depth() { return _depth; }
private:
vector<vector<vector<unique_ptr<Object> > > > _data;
unsigned _width;
unsigned _height;
unsigned _depth;
}
static Grid allObj(width, height, depth);
static vector<Linker> allLinkers;
unique_ptr<Object> createNewObj(int x, int y, int z) {
unique_ptr<Object> obj(new Object());
obj->position(vec3(x, y, z));
return obj;
}
void createPattern()
{
// no need for inserting because Objects are created on allObj creation
// changed the iterator based range for to normal for loops
for (unsigned k = 0; k < allObj.depth - 1; ++k)
for (unsigned j = 0; j < allObj.height - 1; ++j)
for (unsigned i = 0; i < allObj.width - 1; ++i)
Linker.push_back({ allObj(i, j, k) });
}
While writing this I've realized that I don't really know what exactly your linker does and what is the purpose of linking the i-th object with (i+1)-th object and how it will translate to getting them by X/Y/Z and not a single index.
EDIT2: If you want to link these object like the image shows then the linking process would look something like this:
for (unsigned k = 0; k < allObj.depth - 1; ++k)
for (unsigned j = 0; j < allObj.height - 1; ++j)
for (unsigned i = 0; i < allObj.width - 1; ++i) {
auto p = allObj(i + 1, j, k);
allLinkers[i].link(p);
p = allObj(i, j + 1, k);
allLinkers[i].link(p);
p = allObj(i, j, k + 1);
allLinkers[i].link(p);
// and continue with whatever else you want to link
// as you can see this is quite unefective so maybe modifying link method
// so it takes no parameters and automatically links all neighbouring objects would be better
}
This will link every object to its directly neighbouring objects. So for example object at 3/4/5 will be linked to 4/4/5, 3/5/5 and 3/4/6.
EDIT3: Simplified the program structure. Placed all functionality into to Grid class. Here is the code:
class Grid {
public:
// Create a grid with set width, height and depth
Grid(unsigned width, unsigned height, unsigned depth)
: _width(width), _height(height), _depth(depth) {
// This replaces the createPattern function
// Creates both objects and linkers
for (unsigned i = 0; i < size(); ++i) {
_objects.push_back(make_unique<Object>());
_linkers.push_back({ _objects[i].get() });
}
// This replaces the linkPattern function
// Does the linking exactly as shown on the picture
for (unsigned i = 0; i < size(); ++i) {
_linkers[i].link(&object(_objects[i]->getX(), _objects[i]->getY(), _objects[i]->getZ() + 1));
_linkers[i].link(&object(_objects[i]->() + 1, _objects[i]->getY(), _objects[i]->getZ()));
_linkers[i].link(&object(_objects[i]->getX() + 1, _objects[i]->getY(), _objects[i]->getZ() + 1));
}
}
// Direct access operator
Object& object(unsigned x, unsigned y, unsigned z) noexcept {
return *_objects[x + y * _width + z * _height * _width];
}
// another possible implementation of Direct access operator
// checks if element you want is 'in range'
// NOTE: may throw std::out_of_range
const Object& operator()(unsigned x, unsigned y, unsigned z) const {
size_t position = x + y * _width + z * _height * _width;
if (position >= _objects.size() || x > _width || y > _height || z > _depth)
throw std::out_of_range("index is out of range");
return *_objects[x + y * _width + z * _height * _width];
}
// Direct access for linkers
Linker& linker(unsigned x, unsigned y, unsigned z) noexcept {
return _linkers[x + y * _width + z * _height * _width];
}
// Getters
constexpr unsigned size() const noexcept { return _width * _height * _depth; }
constexpr unsigned width() const noexcept { return _width; }
constexpr unsigned height() const noexcept { return _height; }
constexpr unsigned depth() const noexcept { return _depth; }
// Iterators - using ranged for would loop throught all the Objects from _objects
using iterator = std::vector<unique_ptr<Object> >::iterator;
using const_iterator = std::vector<unique_ptr<Object> >::const_iterator;
using reverse_iterator = std::vector<unique_ptr<Object> >::reverse_iterator;
using const_reverse_iterator = std::vector<unique_ptr<Object> >::const_reverse_iterator;
iterator begin() noexcept { return _objects.begin(); }
const_iterator begin() const noexcept { return _objects.begin(); }
iterator end() noexcept { return _objects.end(); }
const_iterator end() const noexcept { return _objects.end(); }
reverse_iterator rbegin() noexcept { return _objects.rbegin(); }
const_reverse_iterator rbegin() const noexcept { return _objects.rbegin(); }
reverse_iterator rend() noexcept { return _objects.rend(); }
const_reverse_iterator rend() const noexcept { return _objects.rend(); }
private:
vector<Linker> _linkers;
vector<unique_ptr<Object> > _objects;
const unsigned _width;
const unsigned _height;
const unsigned _depth;
};
And this would be the usage of said class doing everything your code samples do:
// The grid containing all the objects and linkers
Grid allObj(3, 1, 3);
// You can access objects like this
allObj.object(x, y, z);
// or like this (returns const& (read-only))
allObj(x, y, z);
// Likewise the linker
allObj.linker(x, y, z);