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

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.

Related

C++ filling multidimensional std::arrays

To declare a matrix with all of its elements having a certain value, using std::array the only way I know to do so looks like the following:
std::array<std::array<int, dim_1>, dim_2> matrix;
for (auto it = matrix.begin(); it != matrix.end(); ++it)
std::fill(it->begin(), it->end(), number);
Is there a better, more concise way?
auto matrix = std::array<std::array<int, 4>, 4>();
const auto value = 64;
A one-liner:
std::for_each(matrix.begin(), matrix.end(), [value](auto& column) { std::for_each(column.begin(), column.end(), [value](auto& element) {element = value; }); });
Another one:
std::for_each(matrix.begin(), matrix.end(), [value](auto& column) { std::fill(column.begin(), column.end(), value); });
Something actually readable:
for (auto& column : matrix) {
for (auto& element : column) {
element = value;
}
}
I have no idea if those are actually columns though
This compiles on latest MSVC too and looks funny enough:
std::fill(matrix.begin(), matrix.end(), std::array{ value, value, value, value });
If you're looking for conciseness, you can fill it with a filled array:
decltype(matrix[0]) temp;
temp.fill(5);
matrix.fill(temp);
You can technically remove the temporary assuming a non-zero dimension:
matrix[0].fill(5);
matrix.fill(matrix[0]);
In order for this to perform as well as filling in place, you'll have to rely on the compiler seeing through it. Alternatively, you can plop this in a constexpr function since C++20 and guarantee a compile-time result if you wish.
With helper function:
namespace detail
{
template <typename T, std::size_t...Is>
constexpr std::array<T, sizeof...(Is)>
make_array(const T& value, std::index_sequence<Is...>)
{
return {{(static_cast<void>(Is), value)...}};
}
}
template <std::size_t N, typename T>
constexpr std::array<T, N> make_array(const T& value)
{
return detail::make_array(value, std::make_index_sequence<N>());
}
And then
/*const*/ auto matrix = make_array<dim_2>(make_array<dim_1>(value));
Advantage over fill is that it support non-default constructible types.
And advantage of helper function (even if implemented with std::fill) over assignation afterward is that you can initialize const variable :)

Better understanding of 3D to 1D array conversion in 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.

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

Array compile-time generated?

In this answer (it is not strictly necessary to read the whole question + answer) some code produces a compile-time array like:
template<unsigned...Is,class Tuple>
unsigned index_of(indexes<Is...>,void const* p, Tuple const&t){
void const* r[]={ nullptr, &std::get<Is>(t)... }; // <-- this is the array I'm referring to
auto it = std::find( std::begin(r), std::end(r), p );
if (it==std::end(r))
return -1;
else
return (it-std::begin(r))-1;
}
My question is: is that array entirely compile-time generated with the addresses of each element of a tuple? Is that the compile-time advantage? Or is the array runtime-generated (in that case, where's the compile-time advantage at all?)
Rephrased: why is the complex templated code here necessary at all instead of a simple runtime function which iterates with a for loop over all the elements of the tuple and compares the pointers? What's the gain in all that?? Something regarding the array creation? I don't really see it and I can't believe all that work was done for nothing or just to brag around "I can mess stuff up with templates, take a look"
No, since the tuple address is not known at compile-time. The only thing thats known at compile time is the size of the array, since its extracted from the size of the tuple (Via pack expansion).
Thats a weird algorithm thats only doing type erasure via void* to be able to store tuple elements on an array and then do something on them via standard algorithms.
This has much more sense (Indices trick ommitted to be more clear):
template<typename Tuple , typename T>
bool find_in_tuple( const Tuple& t , const T& e )
{
bool result[] = { (std::get<Indices>( t ) == e)... };
return std::any_of(std::begin(result) , std::end(result) , [](bool b){ return b; } );
}
Here is a running example.
For the index_of feature, you can add a counter to the closure when doing any_of(), or do something more complicated like this:
template<typename Tuple , typename T>
std::index_of index_of( const Tuple& t , const T& e )
{
std::size_t index = 0;
std::tuple<bool,std::size_t> result[] = { std::make_tuple(Indices,std::get<Indices>( t ) == e)... };
bool exists = std::any_of(std::begin(result) , std::end(result) , [&](const std::pair<std::size_t,bool>& p){ index = p.first; return p.second; } );
if(exists)
return index;
else
throw std::bad_argument{};
}

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