How can I use a std::valarray to store/manipulate a contiguous 2D array?
Off the top of my head:
template <class element_type>
class matrix
{
public:
matrix(size_t width, size_t height): m_stride(width), m_height(height), m_storage(width*height) { }
element_type &operator()(size_t row, size_t column)
{
// column major
return m_storage[std::slice(column, m_height, m_stride)][row];
// row major
return m_storage[std::slice(row, m_stride, m_height)][column];
}
private:
std::valarray<element_type> m_storage;
size_t m_stride;
size_t m_height;
};
std::valarray
provides many interesting ways to access elements, via slices, masks, multidimentional slices, or an indirection table. See std::slice_array
, std::gslice_array
, std::mask_array
, and std::indirect_array
for more details.
Accessing column slices of 2D valarrays
First off, you don't have a 2D valarray
. You have a valarray
of valarray
s, a difference you should not ignore.
x = g[m][n];
only looks like an array-style access. It's really closer to
temp = g[m];
x = temp[n];
A valarray
's datastore is a nice contiguous block of memory, but if you have an M by N structure, you have M+1 valarray
s potentially scattered throughout memory. This can turn into a nightmare of performance-killing cache misses.
You are going to have to decide which is more important to be fast, row slicing or column slicing, because only one will be going with the flow of memory and the other require a cache-thrashing copy against the grain.
Currently
g[1][std::slice(0,10,1)];
works because it is slicing a contiguous block of memory, and
g[std::slice(0,1,3)][0]
fails because it must reach across M distinct valarray
s to gather the slice and std::slice
can't do that. You will have to manually copy the elements you want from each of the valarray
s that make up the column. Sucks, huh?
So what do you do?
You fake it! Muhuhahahahahahahaha!
Don't make a valarray
of valarray
s. Make one big valarray
of size MxN. So say goodbye to
std::valarray<std::valarray<int> > g(std::valarray<int>(10),4);
and hello to
std::valarray<int>(10*4);
Now you can take advantage of std::slice
's stride parameter to grab every tenth element
std::slice(column_to_slice,4,10);
And as an added bonus you now have one contiguous block of memory so at least some of that cache-grinding abuse should be mitigated. You're still smurfed if the stride is too large.
I whole-heartedly recommend wrapping this in an object to make access and management easier. Something like this, except you use the valarray
instead of the raw pointer.
dynamic allocating memory for 3d array that uses minimum memory and in contiguous form in c++?
You can write a wrapper around a std::vector
and overload operator()
to access matrix elements. The elements are stored contiguously in the 1D std::vector
and operator()
converts 3D indices into a 1D index into the std::vector
. If the matrix was 2D, this is how the mapping from 2D to 1D would look like:
| 1 2 3 |
| 4 5 6 | ---> [1 2 3 4 5 6 7 8 9]
| 7 8 9 |
This ordering is called row major.
Here's an example of a class that overloads operator()
to convert 3D indices into a row-major 1D index:
#include <iostream>
#include <vector>
template <class T>
class Matrix3D
{
public:
Matrix3D(size_t m, size_t n, size_t o)
: m_(m), n_(n), o_(o), data_(m*n*o) {}
T& operator()(size_t i, size_t j, size_t k)
{
return data_[(i * n_ * o_) + (j * o_) + k];
}
const T& operator()(size_t i, size_t j, size_t k) const
{
return data_[(i * n_ * o_) + (j * o_) + k];
}
private:
std::vector<T> data_;
size_t m_, n_, o_;
};
int main()
{
Matrix3D<float> m(4, 3, 2);
m(0,0,0) = 12.3f;
m(3,2,1) = 45.6f;
std::cout << m(0,0,0) << " " << m(3,2,1) << "\n";
}
The Boost.MultiArray library does essentially the same thing as this (and much more), but can be used for any dimension N.
Pointer of a 2D array in C++?
Are the dimensions of the matrix known at compile time? If so:
template <size_t n>
void rotateMatrix(int (&matrix)[n][n])
{
...
}
How to initialize 3D array in C++
The array in your question has only one element, so you only need one value to completely initialise it. You need three sets of braces, one for each dimension of the array.
int min[1][1][1] = {{{100}}};
A clearer example might be:
int arr[2][3][4] = { { {1, 2, 3, 4}, {1, 2, 3, 4}, {1, 2, 3, 4} },
{ {1, 2, 3, 4}, {1, 2, 3, 4}, {1, 2, 3, 4} } };
As you can see, there are two groups, each containing three groups of 4 numbers.
Related Topics
Passing Optional Parameter by Reference in C++
How to Use C++20's Likely/Unlikely Attribute in If-Else Statement
How to Make a Variadic Is_Same
What Does (Template) Rebind<> Do
How Does C++ Exception Handling Translate to MAChine Code
C++: Pointer to Monomorphic Version of Virtual Member Function
How to Copy One Map into Another Using Std::Copy
How to Convert Rgb -> Yuv -> Rgb (Both Ways)
In Lambda Functions Syntax, What Purpose Does a 'Capture List' Serve
Double-Checked Lock Singleton in C++11
Dependent Scope and Nested Templates
How to Install C++ Plugin to Eclipse
"To_String" Isn't a Member of "Std"
Is It True That There Is No Need to Learn C Because C++ Contains Everything
Portable Compare and Swap (Atomic Operations) C/C++ Library
"Proper" Way to Store Binary Data with C++/Stl