Better understanding of 3D to 1D array conversion in C++ - c++

I'm working on a school project in which I need to dynamically manage a 3D matrix (or array, I don't think it makes a difference, right?).
My first idea was to use a C-like approach, like this:
3Dmatrix(unsigned int height, unsigned int col, unsigned int row) : _3D_matrix(0), _row(0), _col(0), _height(0) {
try {
_3D_matrix = new T**[height];
for(int i =0; i<height; i++){
_3D_matrix[i] = new T*[col];
for(int j =0; j<col; j++){
_3D_matrix[i][j] = new T[row];
}
}
}
catch(...) {
delete[] _3D_matrix;
throw;
}
_row = row;
_col = col;
_height = height;
}
With this approach, though, the memory is not contiguous, and trying to work with iterators is almost impossible. So I decided to switch to a different strategy, "indexing" the 3D array to a 1D array using the formula
A[ x * height * depth + y * depth + z] 
to index the element M[x][y][z]. Since I'm not really sure this approach is what I'm looking for, and I also find other discussions on this topic not very helpful, do you think this approach can serve my purpose?
In particular, I'm worried about getter and setter methods, as well as iterators for reading and writing.
PS: since this project is for didactic use, I'm not allowed to use std library classes like vector or similar, and C++11 or later as well

You could try an approach like this:
struct Vector {
unsigned int X;
unsigned int Y;
unsigned int Z;
};
struct Matrix {
Vector rows[3]; // Depends on if you want row or col major.
// Vector cols[3];
};
// Or Matrix {
Vector* pRows; // Depends on if you want row or col major.
// Vector* pCols;
// Or
struct Matrix { // Row Major
Vector row1;
Vector row2;
Vector row3;
};
// Or
struct Matrix { // Col Major
Vector col1;
Vector col2;
Vector col3;
};
I did not add any constructors, operators nor functions only just shown basic data structure to illustrate the main point.
Note: This kind of pattern has a fixed dimensional size as it is a 3x3x3 matrix.
I do have a class template that can be a variable size matrix of any number of dimensions however it does use advanced techniques in which you stated that you were not able to use such as the standard library and c++11 or higher features. However as a bonus and for future use I can show it here as a good reference to look back on. This does not have anything to do with actually answering your question above; but this is what modern c++ would look like.
file Matrix.h
#ifndef MATRIX_H
#define MATRIX_H
#include <vector>
#include <algorithm>
#include <numeric>
namespace foo {
template<typename Type, size_t... Dims>
class Matrix {
public:
static const size_t _numDims = sizeof...(Dims);
private:
size_t _numElements;
std::vector<Type> _elements;
std::vector<size_t> _strides;
public:
Matrix() noexcept;
template<typename... Args>
Matrix( Args&&... args ) noexcept;
const Type& operator[]( size_t idx );
const Type operator[]( size_t idx ) const;
const Type& operator() ( size_t idx );
const Type operator() ( size_t idx ) const;
size_t numElements() const {
return _elements.size();
}
const std::vector<size_t>& strides() const {
return _strides;
}
const std::vector<Type>& elements() const {
return _elements;
}
};
#include "Matrix.inl"
} // namespace foo
#endif // !MATRIX_H
file Matrix.inl
template<typename Type, size_t... Dims>
Matrix<Type, Dims...>::Matrix() noexcept :
_strides( { Dims... } ) {
using std::begin;
using std::end;
auto mult = std::accumulate( begin( _strides ), end( strides ), 1, std::multiplies<>() );
_numElements = mult;
_elements.resize( _numElements );
}
template<typename Type, size_t... Dims>
template<typename... Args>
Matrix<Type, Dims...>::Matrix( Args&&... args ) noexcept :
_elements( { args... } ),
_strides( { Dims... } ) {
_numElements = _elements.size();
}
template<typename Type, size_t... Dims>
const Type Matrix<Type, Dims...>::operator[]( size_t idx ) const {
return _elements[idx];
}
template<typename Type, size_t... Dims>
const Type& Matrix<Type, Dims...>::operator[]( size_t idx ) {
return _elements[idx];
}
template<typename Type, size_t... Dims>
const Type Matrix<Type, Dims...>::operator()( size_t idx ) const {
return _elements[idx];
}
template<typename Type, size_t... Dims>
const Type& Matrix<Type, Dims...>::operator()( size_t idx ) {
return _elements[idx];
}
typical uses:
{
Matrix<int,2,3,4> iMat2x3x4;
Matrix<double,5,9> dMat5x9;
struct MyStruct {
int x;
float y;
char z;
};
Matrix<MyStruct, 4, 9, 7, 2, 3, 6> massiveMyStructMatrix;
}
The class upon instantiation will store the elements into one of its member vectors while calculating the strides for the dimensions and storing them into another vector. If a matrix is a 2x3x4x5 which is a 4D matrix the _strides container will have 4 values {2,3,4,5} respectively. This way if you need to do any kind of indexing the values are stored sequentially and you don't have to remember them, you can just index into the vector to get the size of the stride to do the appropriate indexing in the current level of your loop. All of the elements are contiguous in memory via the vector. There are also a few basic operators [] and () for both non const and const types. A method to return the number of elements which is the size of the _elements container. And two methods to return the actual containers.
Now you could take this idea and abstract the vectors out of the way however it still does not remove the dependency of the c++11 and higher features especially the use of variadic templates. This is nothing more than a good reference for future use.

With this approach, though, the memory is not contiguous ...
That's not wholly true. Each row is contiguous, and if you mostly operate row-by-row, that might be fine. It's a performance question anyway, so get it working as simply as possible first and worry about optimization later (if at all).
[indexing] ... I'm not really sure this approach is what I'm looking for ... do you think this approach can serve my purpose?
What is your purpose?
A flattened array is easier to manage in the sense that there's only one dynamic (de)allocation - your existing nested structure is buggy because it's hard to clean up correctly part-way through construction.
A flattened array is probably slightly faster, if you care, depending on size and access pattern.
A nested structure is easier to write (and test, and generalize) in that you can layer it up one dimension at a time, roughly like
template <typename T> class Vector;
template <typename T> using Matrix2d = Vector<Vector<T>>;
template <typename T> using Matrix3d = Vector<Matrix2d<T>>;
trying to work with iterators is almost impossible
Slow down. Write one layer of abstraction at a time. If you can write the get(x,y,z) accessor, do that first and layer iteration on top of it.

Related

Multidimensional arrays represented as 1D (templatised for n dimensions)

Declaring multidimensional arrays with static size is quite easy in C++ and the array then is stored in one continuous block of memory (row major layout).
Problem
However declaring dynamically allocated multidimensional arrays (size known only at runtime) in C++ is quite tricky as discussed in other SO thread regarding arrays. To preserve the same syntax with multiple square brackets (in case of 2D array) you need to create an array of pointers, which point to another set of arrays (rows). With more dimensions it adds more (unnecessary) levels of indirection, memory fragmentation, and with small sizes of the array the pointers can take more memory than then the actual data.
One of the solutions is to use 1D array and then recalculate the indices.
Example:
3D array with sizes 10, 3 and 5. I want an element at positions 3, 1, 4 instead of writing 3darray[3][1][4] I would write 3darray[index], where index would be calculated as 3*(y_dym_size*z_dym_size) + 1*(z_dym_size) + 4 which, when substituted, results to 3*(3*5)+1*(5)+4.
I can easily make a class that encapsulates a dynamically allocated array and recomputes in indices in the presented manner, but this is not practical, as it needs to be written for every number of dimensions.
Question:
I would like to create a template that would work for arbitrary number of dimensions with zero overhead (which is the spirit of modern C++ - having reusable code/classes where more work is being shifted to the compiler). I have the following code that works for n-dimensional array, however does not have 0 overhead. It contains for loop and also have an array which is being used in the 1D resolution:
template <class T, size_t DIM>
class arrayND{
std::array<size_t, DIM> sizes;
std::array<size_t, DIM-1> access_multiplier;
vector<T> data;
public:
using iterator = typename vector<T>::iterator;
using const_iterator = typename vector<T>::const_iterator;
template <typename... Args, typename std::enable_if_t<sizeof...(Args) == DIM, int> = 0>
arrayND(Args&&... args) {
std::array<size_t, DIM> temp{args...};
sizes = temp;
size_t mult = 1;
for(int i = DIM-2; i >= 0; --i){
mult *= sizes[i+1];
access_multiplier[i] = mult;
}
data.resize(mult*temp[0]);
}
template <typename... Args, typename std::enable_if_t<sizeof...(Args) == DIM, int> = 0>
T& get(Args&&... args){
std::array<size_t, DIM> idx_copy{args...};
size_t index = idx_copy[DIM-1];
for(int i = DIM-2; i >= 0; --i){
index += idx_copy[i]*access_multiplier[i];
}
return data[index];
}
template <typename... Args, typename std::enable_if_t<sizeof...(Args) == DIM, int> = 0>
T& operator()(Args&&... args){
return get(args...);
}
void set(const T& elem){
fill(begin(data), end(data), elem);
}
iterator begin(){
return begin(data);
}
iterator end(){
return end(data);
}
const_iterator begin() const{
return cbegin(data);
}
const_iterator end() const{
return cend(data);
}
};
Other approach I was thinking of was to utilise variadic templates, which would be hopefully - after compiler optimization - identical to code written specially for some number of dimensions:
int getIndex(size_t index){
return index;
}
template<typename... Args>
int getIndex(size_t index, Args... args){
return access_multiplier[DIM-sizeof...(Args)-1]*index + getIndex(args...);
}
template <typename... Args, typename std::enable_if_t<sizeof...(Args) == DIM, int> = 0>
T& get(Args&&... args){
return data[getIndex(args...)];
/*std::array<size_t, DIM> idx_copy{args...};
size_t index = idx_copy[DIM-1];
for(int i = DIM-2; i >= 0; --i){
index += idx_copy[i]*access_multiplier[i];
}
return data[index];*/
}
Is there a way in the current version (C++17) or the C++ language how to obtain both flexibility (arbitrary number of dimensions) and performance (zero overhead compares to code written specially for some number of dimensions)? If there has to be overhead then it makes more sense to hardcode it for lets say up to 5 dimensions.
Is there already an implementation of dynamics multidimensional array in some existing library?
Split the view from the storage.
An n-dimensional array view of T is a class with a pointer to T and some way of getting n-1 stride sizes. [] returns an n-1 dimensional array view.
There are two different flavours of such views. The first stores the strides, the second a pointer to a contiguous buffer of strides. Both have their advantages; the first with care can even optimize when some or all dimensions are fixed. But I'll do the 2nd.
template<class T, std::size_t N>
struct slice {
T* ptr=0;
std::size_t const* strides=0;
slice<T,N-1> operator[]( std::size_t i )const{
return { ptr + i**strides, strides+1 };
}
};
template<class T>
struct slice<T,1> {
T* ptr=0;
std::size_t const* strides=0;
T& operator[]( std::size_t i )const{
return *(ptr + i**strides);
}
};
this one permits per-element strides.
Now you just have to expose a stride<T,N> to do chained [] on. This is similar to how I'd write it for 3 dimensions.
If you prefer (x,y,z) syntax and your only problem is the for loop and are afraid the compiler did not flatten it, you can write it force flattened using pack expansion. But profile and examine optimized assembly first.

Swap values and indexes of a vector

I have a std::vector<int> with contiguous shuffled values from 0 to N and want to swap, as efficiently as possible, each value with its position in the vector.
Example:
v[6] = 3;
becomes
v[3] = 6;
This is a simple problem, but I do not know how to handle it in order to make it trivial and, above all, very fast. Thank you very much for your suggestions.
Given N at compile time and given the array contains each index in [0,N) exactly once,
it's relatively straight forward (as long as it doesn't have to be in-place, as mentioned in the comments above) :
Construct a new array so that v'[n] = find_index(v, n) and assign it to the old one.
Here I used variadic templates with std::index_sequence to roll it into a single assignment:
template<typename T, std::size_t N>
std::size_t find_index(const std::array<T,N>& arr, std::size_t index) {
return static_cast<std::size_t>(std::distance(arr.begin(), std::find(arr.begin(), arr.end(), index)));
}
template<typename T, std::size_t N, std::size_t... Index>
void swap_index_value(std::array<T,N>& arr, std::index_sequence<Index...> seq){
arr = { find_index(arr, Index)... };
}
template<typename Integer, std::size_t N>
void swap_index_value(std::array<Integer,N>& arr) {
swap_index_value(arr, std::make_index_sequence<N>{});
}
The complexity of this is does not look great though. Calling find_index(arr, n) for each n in [0,N)
will take N * (N+1) / 2 comparisons total (std::sort would only take N * log(N)).
However, since we know each index is present in the array, we could just fill out an array of indices
as we walk over the original array, and assuming T is an integral type we can skip some std::size_t <-> T conversions, too:
template<typename T, std::size_t N>
void swap_index_value(std::array<T,N>& arr){
std::array<T, N> indices;
for (T i = 0; i < N; ++i)
indices[arr[i]] = i;
arr = indices;
}
We're still using twice the space and doing some randomly ordered writes to our array,
but essentially we're down to 2*N assignments, and the code is simpler than before.
Alternatively, we could also std::sort if we keep a copy to do lookups in:
template<typename T, std::size_t N>
void swap_index_value(std::array<T,N>& arr){
std::sort(arr.begin(), arr.end(), [copy = arr](const T& lhs, const T& rhs) {
return copy[lhs] < copy[rhs];
});
}
First version here,
second version here,
std::sort version here
Benchmarking which one is faster is left as an exercise to the reader ;)

c++ Returning reference to single row of matrix

So I have a struct called Vector, using a template to allow it to have a varied length if needed. It contains an array to store the elements of the Vector.
template <class T = float, int N = 3>
struct Vector
{
T data[N];
and another called Matrix which is essentially a Vector with N*N elements in the data array.
template <class T = float, int N = 3>
struct Matrix : public Vector <T, N*N>
{
Matrix() {}
In one of the functions for Matrix, I need to return one of its "rows" as a Vector so that any changes made to that Vector are also changed in the row of the Matrix.
inline Vector<T, N>& operator[](int row)
{
}
I have no idea how to do this.
It seems to me that you should change your Matrix class to be a "vector of vectors" rather than a single linear vector:
struct Matrix : public Vector <Vector<T, N> , N>
Now you get the desired behavior for free assuming a correct implementation of operator[] for Vector.
The data for the i-th row starts at data[i*N].
template <class T = float, int N = 3>
struct Matrix : public Vector <T, N*N>
{
Matrix() {}
T* operator[](int i)
{
return &data[i*N];
}
};
Usage:
Matrix<int, 10> m;
m[5][5] = 10;
If what are looking for is just to be able to address individual elements then you might also consider overloading the function call operator instead.
template <class T = float, int N = 3>
struct Matrix : public Vector <T, N*N>
{
Matrix() {}
T& operator()(int row, int col)
{
return /*element*/;
}
};
This way all element access is done through the Matrix class without the requirement for the intermediate Vector.
The usage then becomes:
Matrix< int, 10 > m;
m( 5, 5 ) = 10;
The answer ended up being
return *(Vector<T, N>*)(data+N*row);
I don't fully understand the process, I think the explanation was data+N*row gives a pointer to the element in the array you want to start from, then you cast that pointer as a Vector type pointer, then dereference so it can be sent and used elsewhere. Maybe someone better suited can explain in the comments.

Overloading std::swap() using CRTP

Ok I have two classes, matrix_row and matrix_column, which (as their names say) represent rows and columns of matrices. I'm doing this to do operations between matrix rows/columns ("Lines") in an easy way. Something like m1.row(1) = m1.column(0) + m2.row(7).
Since the two types of lines are almost the same thing, except of the way to access the underlying elements, I have a CRTP base class holding all the operations. Then the two classes menctioned above inherit from it and specify its own way to access an element through static dispatch:
template<typename T , std::size_t ROWS , std::size_t COLUMNS , typename DERIVED>
struct line
{
std::reference_wrapper<matrix<T,ROWS,COLUMNS>> ref;
std::size_t index;
...
};
template<typename T , std::size_t ROWS , std::size_t COLUMNS>
struct matrix_row : public line<T,ROWS,COLUMNS,matrix_row<T,ROWS,COLUMNS>>
{
const T& at( std::size_t index ) const { return this->matrix_ref.get()[this->index][index]; }
T& at( std::size_t index ) { return this->matrix_ref.get()[this->index][index]; }
using base_t::operator=;
};
template<typename T , std::size_t ROWS , std::size_t COLUMNS>
struct matrix_column : public line<T,ROWS,COLUMNS,matrix_column<T,ROWS,COLUMNS>>
{
const T& at( std::size_t index ) const { return this->matrix_ref.get()[index][this->index]; }
T& at( std::size_t index ) { return this->matrix_ref.get()[index][this->index]; }
using base_t::operator=;
};
Everything seems to work until here, assigment between different types of lines included. Now I want to overload std::swap() for lines, for algorithms that require matrix line swapping (Like Gaussian elimination for example). So I have added a swap() function in my own namespace:
template<typename LHS_T , typename RHS_T ,
std::size_t LHS_Rs , std::size_t RHS_Rs ,
std::size_t LHS_Cs , std::size_t RHS_Cs ,
typename LHS_D , typename RHS_D
>
friend void swap( line<LHS_T,LHS_Rs,LHS_Cs,LHS_D>& lhs , line<RHS_T,RHS_Rs,RHS_Cs,RHS_D>& rhs )
{
assert( lhs.length == rhs.length );
for( std::size_t i = 0 ; i < lhs.length ; ++i )
std::swap( lhs[i] , rhs[i] );
}
Since I need different line types to be swapable (Exchange one column of a matrix with one row of the same length, for example), I defined the function to work with the base instead of the derived types.
I have two questions:
All the machinery is on the base class, the derived classes only serve to provide a custom access (at()) method. I'm incurring on slicing problems doing swap on this way even if the memory layout of the derived classes is (Thats what I understand since there is no virtual nor extra data members) the same as the layout of the base?
When using that swap() template, the compiler complains with no known conversion from [derived] to [base]& errors. What I'm missing? Why is a conversion needed if I'm using references to the base class?

One-line initialiser for Boost.MultiArray

I have a n-dimensional Boost.MultiArray I initialize as follows:
const int n=3, size=4; //# of dimensions and size of one dimension
boost::multi_array<char,n> arr;
boost::array<size_t,n> extents; //size of each dimension
extents.assign(size); //assign size to each dimension -> {{4, 4, 4}}
arr.resize(extents);
So I have 4 lines of code to get the MultiArray, but I'd like to do it in one line.
Is there any simple way to generate an MultiArray with n dimensions each having size length (so I can write arr(samevaluearray(n,size))) or did I miss a handy constructor for MultiArray?
Edit: It should work without depending on a certain value of n, i.e. arr({{size,size}} would only work for n=2.
Since it may not be clear: boost::multi_array<char,n>(boost::extents[4][4][4]) correctly initializes a 4x4x4-array, but every time n is changed in the sourcecode, every initialization has to be updated by hand, so it's not an option.
You can encapsulate the creation of the array into an helper function:
template <typename T, size_t N>
boost::multi_array<T, N> make_regular_matrix(const size_t m)
{
boost::multi_array<T, N> arr;
boost::array<size_t, N> extents;
extents.assign(m);
arr.resize(extents);
return arr;
}
const int n = 3;
int size = 4; // Can be const as well, but this is not mandatory
auto arr = make_regular_matrix<char, n>(size);
If you can't use auto, you'll have to duplicate the template parameters:
boost::multi_array<char, n> arr = make_regular_matrix<char, n>(size);
The make_regular_matrix function could be shortened to use std::vector, as you did in your answer; I don't know if this implementation would be better. The aim of the helper function is to hide the creation of the array, but other versions could be written, for example to initialize the array elements with a given value:
template <size_t N, typename T> //switched order for deduction
boost::multi_array<T, N> make_regular_matrix(const size_t m, const T & value)
{
boost::multi_array<T, N> arr(std::vector<size_t>(n, m));
std::fill(arr.data(), arr.data() + arr.num_elements(), value);
return arr;
}
auto arr = make_regular_matrix<4>(3, 'z'); //creates a 3x3x3x3 matrix
//filled with 'z's
Turns out, std::vector has a constructor, that constructs a vector with a constant value repeated n times, so a possible solution looks like this:
const int n=2, size=4; //# of dimensions and size of one dimension
boost::multi_array<char,n> arr(std::vector<size_t>(n,size));
This initializes a n-dimensional multi_array with each dimension's size set to size.
From the Boost Multi-Array documentation, yes, you can initialize it one line:
typedef boost::multi_array<double, 3> array_type;
typedef array_type::index index;
array_type A(boost::extents[3][4][2]);
The typedefs are for readability, one can just as easily do for your example:
boost::multi_array<int, 2> arr(boost::extents[2][4]);