C++11 Cannot cast template inheritance - c++

I'm writing the Math module for an OpenGLES project.
I wrote a class for managing float matrices for a generic size
template <unsigned int N>
class MatrixN {
public:
float m[N*N];
MatrixN(){}
virtual ~MatrixN(){}
void identify();
MatrixN& operator=(const MatrixN& b);
};
template <unsigned int N>
MatrixN<N> operator*(const MatrixN<N>& a, const MatrixN<N>& b);
//CPP file implementation
template<unsigned int N>
MatrixN<N> operator*(const MatrixN<N>&a, const MatrixN<N> &b) {
MatrixN<N> matrix;
for(unsigned int i = 0; i < N; i++){
for(unsigned int j = 0; j < N; j++){
matrix.m[i * N + j] = 0;
for(unsigned int z = 0; z < N; z++){
matrix.m[i * N + j] += a.m[i * N + z] * b.m[z * N + j];
}
}
}
return matrix;
}
And the i create a sub-class for managing 3x3 matrices
class Matrix3 : public MatrixN<3> {
public:
void rotateX(const float radians);
void rotateY(const float radians);
void rotateZ(const float radians);
};
Why when i perform this operation
//Rotations instances of Matrix3
Matrix3 rotation = this->rotation * input.rotation;
i get this error at compile time?
no viable conversion from 'MatrixN<3U>' to 'const Matrix3'

It's because the multiply operation return MatrixN<3> and is not Matrix3
In this case, you can create a constructor in Matrix3 that accept MatrixN<3>
code (not tested) :
class Matrix3 : public MatrixN<3> {
public:
Matrix3 (const MatrixN<3>& mat){/*set internal values*/}
void rotateX(const float radians);
void rotateY(const float radians);
void rotateZ(const float radians);
};

The problem is that the result type of operator* is MatrixN<N> but the type of rotation is Matrix3 and the implicit cast does not work because this would be a downcast.
As a possible solution you can overwrite the operator* for Matrix3 inputs and outputs. With a helper function you can spare some code if you would like to reuse it. For example:
template< MatrixType >
MatrixType multiple(const MatrixType& a, const MatrixType& b, size_t N )
{
MatrixType matrix;
for(unsigned int i = 0; i < N; i++)
{
for(unsigned int j = 0; j < N; j++)
{
matrix.m[i * N + j] = 0;
for(unsigned int z = 0; z < N; z++)
{
matrix.m[i * N + j] += a.m[i * N + z] * b.m[z * N + j];
}
}
}
return matrix;
}
Matrix3 operator*(const Matrix3& a, const Matrix3& b)
{
return multiple< Matrix3 >( a, b, 3 );
}
Note that this can be a bit dangerous because there is not any protection to avoid overflow, so the "user" of the multiple function should be careful.

Your multiplication operator is implemented in terms of MatrixN<N>. Your derived type Matrix3 isn't immediately one of those but has one of those as base. So there is still a derived to base conversion to call the operator and the type returned isn't immediately the type you want.
You can define your multiplication operator to take any type as arguments and return this type and then constrain it to only really take types which are derived from MatrixN<N>:
template<typename Matrix,
typename = std::enable_if_t<std::is_base_of<MatrixN<Matrix::size>, Matrix>::value>>
Matrix operator*(const Matrix&a, const Matrix &b) {
constexpr unsigned N = Matrix::size;
Matrix matrix;
for(unsigned int i = 0; i < N; i++){
for(unsigned int j = 0; j < N; j++){
matrix.m[i * N + j] = 0;
for(unsigned int z = 0; z < N; z++){
matrix.m[i * N + j] += a.m[i * N + z] * b.m[z * N + j];
}
}
}
return matrix;
}
To statically determine the size N of the matrix, the class template needs to have a nested constant expression value size, e.g.:
template <unsigned int N>
class MatrixN {
public:
static constexpr unsigned int size = N;
// ...
};

This code may work. But you should know this is very very dangerous.
MatrixN<3> tmp_scoped_var = this->rotation * input.rotation;
Matrix3 &rotation = reinterpret_cast<Matrix3 &>(tmp_scoped_var);
rotation.rotateX(1.1); // Call the method of B
Because tmp_scoped_var hold the data returned by MatrixN<N> 's operator *. So the memory will be freed while out of the scope. This code tells the compile to force using Matrix3 's method on the MatrixN<N> variable. The memory layouts of Matrix3 and MatrixN<N> are identical, otherwise, the program may crash due to segment fault.
According to you code, you may desire to add some particular method while the template parameter N equals 3. So class template specialization could be used.
template <>
class MatrixN<3> {
public:
float m[3*3];
MatrixN(){}
virtual ~MatrixN(){}
void identify();
MatrixN& operator=(const MatrixN<3>& b);
public:
void rotateX(const float radians);
void rotateY(const float radians);
void rotateZ(const float radians);
};

Related

Memory leakage C++ threading

I have a problem, probably, with memory leaking in C++ threads. I receive a runtime error with code 11. I am writing an optimization algorithm, which aims to optimize parameters of 2D reactors. It generates instances of reforming function, which creates Reformer objects. The reformers have 2 different parameters, which can differ locally in a single reformer and are passed to the reforming function from the main function. To specify, each reformer is divided into a specified number of zones (same dimensions and locations in each reformer), and each zone can have different parameters. Therefore, size of each of 2 vectors is equal to [NUMBER OF REFORMERS] * [NUMBER OF ZONES]. Then, the reforming function creates Segment objects, which number is equal to the number of zones.
I assume that the issue here is that threads try to access the same vector simultaneously and I would really appreciate a solution for that matter.
Remarks:
If I change the main.cpp to substitute the threads with a usual loop, no error is returned.
If I comment out the setProp method in the set_segments functions, no error is returned (with threads).
Threads are highly recommended here, due to long computation time of a single Reformer, and I have an access to a multi-core computing units.
To clarify, I will explain everything with a minimal reproducible example:
input.h
#include <iostream>
#include <fstream>
#include <vector>
#include <thread>
int reactor_no = 2; // number of reformers
int zones_X = 5; // number of zones in a single reformer, X direction
int zones_Y = 2; // number of zones in a single reformer, Y direction
double dim_X = 0.5; // reactor's length
double dim_Y = 0.2; // reactor's height
double wall_t = 0.1; // thickness of the reactor wall
size_t zones = zones_X * zones_Y;
Reformer.h:
#include "input.h"
class Reformer {
public:
Reformer() {}
Reformer(const double& L, const double& Y, const double& wall_t,
const int& zones_X = 1, const int& zones_Y = 1) {
length_ = L;
height_ = Y;
zonesX_ = zones_X;
zonesY_ = zones_Y;
wall_thickness_ = wall_t;
dx_ = length_ / static_cast<double> (zonesX_);
dr_ = height_ / static_cast<double> (zonesY_);
}
private:
double wall_thickness_; // wall thickness (m)
double length_; // recactor length (m)
double height_; // reactor height (m) (excluding wall thickness)
int zonesX_; // number of segments in the X direction
int zonesY_; // number of segments in the Y direction
double dx_; // segment width (m)
double dr_; // segment height (m)
}
Segment.h:
#include "input.h"
class Segment{
public:
Segment() : Segment(0, 0) {}
Segment(int i, int j) {
i_ = i;
j_ = j;
}
void setXR(const double& dx, const double& dr, const int& SL, const int& SR) {
x0_ = i_ * dx;
x1_ = x0_ + dx;
r0_ = j_ * dr;
r1_ = r0_ + dr;
if (i_ == SL - 1) {
x1_ = length;
}
if (j_ == SR - 1) {
r1_ = radius;
}
}
void setWall() {
x0_ = 0;
x1_ = length;
r0_ = radius;
r1_ = radius + wall_t;
}
void setProp(const double& por, const double& por_s, const bool& cat) {
porosity_ = por;
catalyst_ = cat;
}
private:
size_t i_; //segment column no.
size_t j_; //segment row no.
double x0_; //beginning of segment - x coordinate (m)
double x1_; //ending of segment - x coordinate (m)
double r0_; //beginning of segment - r coordinate (m)
double r1_; //ending of segment - r coordinate (m)
int catalyst_; //1 - catalytic, 0 - non-catalytic
double porosity_; //porosity (-)
};
main.cpp:
#include "input.h"
int main() {
int zones = zones_X * zones_Y;
size_t pop_size = reactor_no * zones;
std::vector<int> cat;
cat.reserve(pop_size);
std::vector<double> porosity;
porosity.reserve(pop_size); // the values in the vectors are not important, therefore I will just fill them with 1s
for (int i = 0; i < pop_size; i++) {
cat[i] = 1;
porosity[i] = 1.0;
}
std::vector<std::thread> Ref;
Ref.reserve(reactor_no);
for (k = 0; k < reactor_no; k++) {
Ref.emplace_back(reforming, k, cat, porosity);
}
for (auto &X : Ref) { X.join(); }
}
reforming.cpp:
#include "input.h"
void reforming(const int m, const std::vector<int>& cat_check, const std::vector<double>& por) {
Reformer reactor(length, radius, wall_t, zonesX, zonesY);
std::vector<Segment> seg; // vector holding segment objects
seg.reserve(zones);
set_segments(seg, reactor, zones, m, por, por_s, cat_check);
}
set_segments function:
#include "input.h"
void set_segments(std::vector<Segment> &seg, Reformer &reac, const int m,
const std::vector<double> &por, const std::vector<int> &check) {
int i, j, k, n;
double dx = dim_X / static_cast<double> (zones_X);
double dy = dim_Y / static_cast<double> (zones_Y);
std::vector<Segment*> ptr_seg;
ptr_seg.reserve(zones);
k = 0;
for (i = 0; i < zones_X; i++) {
for (j = 0; j < zones_Y; j++) {
n = m * zones + (i * zones_Y + j);
seg.emplace_back(Segment(i, j));
seg[k].setProp(por[n], check[n]);
seg[k].setXR(dx, dy, zones_X, zones_Y);
k++;
}
}
}
Adding std::ref() to the reforming function call parameters solved the problem.
for (k = 0; k < spec_max; k++) {
Ref.emplace_back(reforming, k, std::ref(cat), std::ref(porosity));
}

How should I declare this operator overload?

I need to multiply a const Vector by an int, but I have to declare the overload as a non-member function and not as a method or it will not compile; how would I write the overload as a method?
namespace N
{
class Vector {
public:
double x, y, z;
Vector( );
Vector(double x, double y = 0, double z = 0);
Vector operator*(double k);
//friend Vector operator*(const Vector u, double k);
};
}
namespace N
{
Vector::Vector( )
{
x = 0;
y = 0;
z = 0;
}
// Creates a vector with initial Cartesian components.
//
Vector::Vector(double x, double y, double z) :
x(x),
y(y),
z(z)
{
}
// Allows multiplying a vector by a scalar.
//
Vector Vector::operator*(double k)
{
Vector scaled;
scaled.x = x * k;
scaled.y = y * k;
scaled.z = z * k;
return scaled;
}
// Allows multiplying a vector by a scalar.
//
/*Vector operator*(const Vector u, double k)
{
return Vector(u.x * k, u.y * k, u.z * k);
}*/
}
const N::Vector A(3, 4);
const N::Vector B(4, 3);
int main( )
{
N::Vector resulting = A * 3;
return 0;
}
As a member, simply change the code to
Vector operator*(double k) const;
and in the definition
Vector Vector::operator*(double k) const ...
As top-level:
friend Vector operator*(const Vector& u, double k);
and
Vector operator*(const Vector& u, double k)
{
return Vector(u.x * k, u.y * k, u.z * k);
}

Can an arithmetic expression be passed as argument to a function to describe the logic therein?

I'm working on visualizing the Mandelbrot set as well as a few other fractals and there's a lot of duplicated code but no code reuse.
One of the functions I am using is below:
/**
* determines whether a pixel lies in the set
* #params x, y - x and y coordinates on R/I axes
* #param c - a complex number
*/
void calculateSet(int x, int y, Complex c) {
Complex z = c.clone();
int n = 0;
for (; n < maxDepth; n++) {
if (z.dis() > 4) { break; }
z = z^2 + c;
}
// some code using n to color the set
}
This follows the Mandelbrot set:
z_(n+1) = z_n^2 + c
But look at the relevant code for the Burning Ship set:
void calculateSet(int x, int y, Complex c) {
Complex z = c.clone();
int n = 0;
for (; n < maxDepth; n++) {
if (z.dis() > 4) { break; }
z = abs(z)^2 + c; // ***
}
// follows z_(n+1) = abs(z_1)^2 + c
}
All the code save for the starred line is identical. Right now I have separate classes for Mandelbrot, BurningShip, and a few others with the only difference being that one line.
Is there a way to define this expression and pass to a generalized Set class?
Some pseudocode:
class Set {
// ...
Set(Type expression) {
// ...
// x, y, c initialized
// ...
calculateSet(x, y, c, expression);
}
void calculateSet(int x, int y, Complex c, Type e) {
Complex z = c.clone();
int n = 0;
for (; n < maxDepth; n++) {
if (z.dis() > 4) { break; }
z = e;
}
}
};
And I can just use Set to describe any kind of set I wish?
Set mandelbrot = Set(Type("z^2 + c"));
Set burningship = Set(Type("abs(z)^2 + c"));
// etc
I could use if/else statements to have just one class, but it's not generalized.
Since you're limited to C++03, you can use a function pointer relatively painlessly.
Complex mandlebrotCompute(Complex z, Complex c) {
return z*z + c;
}
void calculateSet(int x, int y, Complex c, Complex (*func)(Complex, Complex)) {
Complex z = c.clone();
int n = 0;
for (; n < maxDepth; n++) {
if (z.dis() > 4) { break; }
z = func(z, c);
}
}
It is used like the following:
Complex foo;
calculateSet(1, 2, foo, mandlebrotCompute);
It might help make the code cleaner to use a typedef for the function pointer.
You can make a template, with the function as template argument.
I believe this is the method that provides the most inlining opportunities.
typedef Complex (*Function)(const Complex&, const Complex&);
template<Function fn>
class Set
{
// ...
void calculateSet(int x, int y, Complex c) {
Complex z = c;
int n = 0;
for (; n < maxDepth; n++) {
if (z.dis() > 4) { break; }
z = fn(z, c)
}
// some code...
}
}
Complex mandelbrot_fn(const Complex& z, const Complex& c)
{
return z^2 + c;
}
Complex burning_fn(const Complex& z, const Complex& c)
{
return abs(z)^2 + c;
}
Set<mandelbrot_fn> mandelbrot;
Set<burning_fn> burning_ship;
That is what lambdas are for I guess.
template<typename Lam>
class Set
{
private:
Lam lam;
public:
Set (Lam&& lam) : lam(lam) {}
void calculateSet(int x, int y, Complex c)
{
Complex z = c.clone();
int n = 0;
for (; n < maxDepth; n++) {
if (z.dis() > 4) { break; }
z = lam(z, c);
}
}
};
You can use this class like this:
auto mandelbrot = Set([](Complex z, Complex c) -> Complex {
return (z*z) + c;
});
auto burningShip = Set([](Complex z, Complex c) -> Complex {
return abs((z*z)) + c;
});
mandelbrot.calculateSet(...);
burningShip .calculateSet(...);

Storing and accessing Object position vector

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);

How to allocate & access 3D, 4D, 5D arrays?

How can I allocate 3D, 4D, 5D arrays with one malloc in a contigious way and access the individual items?
Something like this:
int* array = malloc(sizeof(int) * width * height);
int item = array[x + y * width];
A 3D array is an array of 2D arrays. A 4D array is an array of 3D arrays. You just multiply by your other dimensions. For example, a 3D array can be allocated in this way:
int *array = malloc(sizeof(int) * width * height * depth);
A 4D array can be made by multiplying by your other dimension:
int *array = malloc(sizeof(int) * width * height * depth * other_dimension);
and so on for 5D, 6D, etc. arrays.
You can access elements by using something like this (for 3D arrays, easily extended), assuming you have access to the width and height of the array:
int get_element(int x, int y, int z)
{
return array[(z * width * height) + (y * width) + x];
}
For 4D arrays:
int get_element(int x, int y, int z, int dimension_4)
{
return array[(dimension_4 * width * height * depth) + (z * width * height) + (y * width) + x];
}
As answered here (Setting pointer to arbitrary dimension array?
):
Look specially computeIndex/computeIndexes.
#include <cstddef>
#include <vector>
template <typename T>
class MultiArray
{
public:
explicit MultiArray(const std::vector<size_t>& dimensions) :
dimensions(dimensions),
values(computeTotalSize(dimensions))
{
assert(!dimensions.empty());
assert(!values.empty());
}
const T& get(const std::vector<size_t>& indexes) const
{
return values[computeIndex(indexes)];
}
T& get(const std::vector<size_t>& indexes)
{
return values[computeIndex(indexes)];
}
size_t computeIndex(const std::vector<size_t>& indexes) const
{
assert(indexes.size() == dimensions.size());
size_t index = 0;
size_t mul = 1;
for (size_t i = 0; i != dimensions.size(); ++i) {
assert(indexes[i] < dimensions[i]);
index += indexes[i] * mul;
mul *= dimensions[i];
}
assert(index < values.size());
return index;
}
std::vector<size_t> computeIndexes(size_t index) const
{
assert(index < values.size());
std::vector<size_t> res(dimensions.size());
size_t mul = values.size();
for (size_t i = dimensions.size(); i != 0; --i) {
mul /= dimensions[i - 1];
res[i - 1] = index / mul;
assert(res[i - 1] < dimensions[i - 1]);
index -= res[i - 1] * mul;
}
return res;
}
private:
size_t computeTotalSize(const std::vector<size_t>& dimensions) const
{
size_t totalSize = 1;
for (auto i : dimensions) {
totalSize *= i;
}
return totalSize;
}
private:
std::vector<size_t> dimensions;
std::vector<T> values;
};
int main()
{
MultiArray<int> m({3, 2, 4});
m.get({0, 0, 3}) = 42;
m.get({2, 1, 3}) = 42;
for (size_t i = 0; i != 24; ++i) {
assert(m.computeIndex(m.computeIndexes(i)) == i);
}
}
Demo
Arrays are by nature allocated as a single dimension. You bestow dimensionality on them via the way you compute indexes to them. The size you need to allocate is the size of a scalar element multiplied by the number of elements in each of however many dimensions you intend to use, e.g., if you want a 10 x 20 x 30 array of 4-byte elements, multiply 4 x 10 x 20 x 30 to get the size of the malloc you need. Then, I'd probably write a function such as my_index(int i, int j, int k) that would compute the one-dimensional index for any valid (i,j,k) combination. This idea can be extended into as many dimensions as you wish.