How do you create a multidimensional array (matrix) whose dimensions are determined at runtime.
The best way seems to be to take a vector of dimensions for construction and also a vector of offsets to access individual elements
This will also allow using initializer lists:
This should take a matrix of types determined at compile time, so templates make sense
C++11 features should be used as appropriate, bonus points for lambdas
Example usage:
int main(int, char **)
{
static const std::size_t d1{2};
static const std::size_t d2{3};
static const std::size_t d3{4};
multi_vec<std::size_t> q({d1,d2,d3});
for (std::size_t i1=0; i1 < d1; ++i1)
for (std::size_t i2=0; i2 < d2; ++i2)
for (std::size_t i3=0; i3 < d3; ++i3)
q[{i1,i2,i3}]=foo(i1,i2,i3);
for (std::size_t i1=0; i1 < d1; ++i1)
for (std::size_t i2=0; i2 < d2; ++i2)
for (std::size_t i3=0; i3 < d3; ++i3)
std::cout << "{"
<< i1 << ","
<< i2 << ","
<< i3 << "}=> "
<< foo(i1,i2,i3) << "=="
<< q[{i1,i2,i3}]
<< ((foo(i1,i2,i3) == q[{i1,i2,i3}])
? " good" : " ERROR")
<< std::endl;
};
This is actually a good place to use lambdas with std::for_each
and unique_ptr for memory management:
template <class T>
class multi_vec
{
public:
using param=std::vector<size_t>;
explicit multi_vec(const param& dimensions)
: dim{dimensions}, prod {1}
{
std::for_each(dim.begin(), dim.end(), [this] (std::size_t val)
{
mult.emplace_back(prod);
prod *= val;
} );
ptr.reset(new T[prod]);
}
std::size_t capacity() const { return prod; }
// undefined if elements in lookup != elemenets in dim
// undefined if any element in lookup
// is greater than or equal to corresponding dim element
T& operator[](const param& lookup)
{
return ptr[get_offset(lookup)];
}
const T operator[](const param& lookup) const
{
return ptr[get_offset(lookup)];
}
private:
std::size_t get_offset(const param& lookup) const
{
std::size_t offset=0;
auto mit=mult.begin();
std::for_each(lookup.begin(), lookup.end(), [&offset, &mit] (std::size_t val)
{
offset+=*mit * val;
++mit;
} );
return offset;
}
param dim;
param mult;
std::size_t prod;
std::unique_ptr<T[]> ptr;
};
This uses a single dimensional array for actual storage and caches multipliers for reuse
Since a normal multidimension array e.g. x[2][3][4]
does not bounds check, going out of bounds is deemed undefined instead of constantly checking values that are probably valid. In the same way diminsionality is not checked for lookups, if required, a static member can be added to check if a parameter is valid, in terms of bounds or dimensionality.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With