I have a multi-dimensional array represented contiguously in memory. I want to keep this representation hidden and just let the user access the array elements as if it were a multi-dimensional one: e.g. my_array[0][3][5] or my_array(0,3,5) or something similar. The dimensions of the object are not determined until runtime, but the object is created with a type that specifies how many dimensions it has. This element look-up will need to be called billions of times, and so should hopefully involve minimal overhead for each call.
I have looked at similar questions but not really found a good solution. Using the [] operator requires the creation of N-1 dimensional objects, which is fine for multi-dimensional structures like vectors-of-vectors because the object already exists, but for a contiguous array it seems like it would get convoluted very quickly and require some kind of slicing through the original array.
I have also looked at overloading (), which seems more promising, but requires specifying the number of arguments, which will vary depending upon the number of dimensions of the array. I have thought about using list initialization or vectors, but wanted to avoid instantiating objects.
I am only a little familiar with templates and figure that there should be some way with C++'s majestic template powers to specify a unique overloading of () for arrays with different types (e.g. different numbers of dimensions). But I have only used templates in really basic generic cases like making a function use both float and double.
I am imagining something like this:
template<typename TDim>
class MultiArray {
public:
MultiArray() {} //build some things
~MultiArray() {} //destroy some things
// The number of arguments would be == to TDim for the instantiated class
float& operator() (int dim1, int dim2, ...) {
//convert to contiguous index and return ref to element
// I believe the conversion equation is something like:
// dim1 + Maxdim1 * ( dim2 + MaxDim2 * ( dim3 + MaxDim3 * (...)))
}
private:
vector<float> internal_array;
vector<int> MaxDimX; // Each element says how large each corresponding dim is.
};
So if I initialize this class and attempted to access an element, it would look something like this:
my_array = MultiArray<4>();
element = my_array(2,5,4,1);
How might I go about doing this using templates? Is this even possible?
template<class T>
struct slice {
T* data = 0;
std::size_t const* stride = 0;
slice operator[](std::size_t I)const {
return{ data + I* *stride, stride + 1 };
}
operator T&()const {
return *data;
}
T& operator=(typename std::remove_const<T>::type in)const {
*data = std::move(in); return *data;
}
};
store a vector<T> of data, and an std::vector<std::size_t> stride of strides, where stride[0] is the element-stride that the first index wants.
template<class T>
struct buffer {
std::vector<T> data;
std::vector<std::size_t> strides;
buffer( std::vector<std::size_t> sizes, std::vector<T> d ):
data(std::move(d)),
strides(sizes)
{
std::size_t scale = 1;
for (std::size_t i = 0; i<sizes.size(); ++i){
auto next = scale*strides[sizes.size()-1-i];
strides[sizes.size()-1-i] = scale;
scale=next;
}
}
slice<T> get(){ return {data.data(), strides.data()}; }
slice<T const> get()const{ return {data.data(), strides.data()}; }
};
c++14. Live example.
If you use not enough []s it refers to the first element of the subarray in question. If you use too many it does UB. It does zero dimension checking, both in count of dimensions and in size.
Both can be added, but would cost performance.
The number of dimensions is dynamic. You can split buffer into two types, one that owns the buffer and the other that provides the dimensioned view of it.
It seems to me that you could use Boost.MultiArray, boost::multi_array_ref to be more specific. boost::multi_array_ref does exactly what you want: it wraps continuous data array into an object that may be treated as a multidimensional array. You may also use boost::multi_array_ref::array_view for slicing purposes.
I cannot provide you with any benchmark results, but from my experience, I can say that boost::multi_array_ref works pretty fast.
If you can use C++17, so variadic template folding, and row major order, I suppose you can write something like (caution: not tested)
template <template ... Args>
float & operator() (Args ... dims)
{
static_assert( sizeof...(Args) == TDim , "wrong number of indexes" );
// or SFINAE enable instead of static_assert()
std::size_t pos { 0U };
std::size_t i { 0U };
( pos *= MaxDimX[i++], pos += dims, ... );
return internal_array[pos];
}
OTPS (Off Topic Post Scriptum): your MaxDimX, if I understand correctly, is a vector of dimensions; so should be an unsigned integer, non a signed int; usually, for indexes, is used std::size_t [see Note 1].
OTPS 2: if you know compile time the number of dimensions (TDim, right?) instead of a std::vector, I suggest the use of a std::array; I mean
std::array<std::size_t, TDim> MaxDimX;
-- EDIT --
If you can't use C++17, you can use the trick of the unused array initialization to obtain something similar.
I mean
template <template ... Args>
float & operator() (Args ... dims)
{
using unused = int[];
static_assert( sizeof...(Args) == TDim , "wrong number of indexes" );
// or SFINAE enable instead of static_assert()
std::size_t pos { 0U };
std::size_t i { 0U };
(void)unused { (pos *= MaxDimX[i++], pos += dims, 0) ... };
return internal_array[pos];
}
Note 1: as pointed by Julius, the use of a signed or an unsigned integer for indexes is controversial.
So I try to explain better why I suggest to use an unsigned value (std::size_t, by example) for they.
The point is that (as far I know) all Standard Template Library is designed to use unsigned integer for index values. You can see it by the value returned by the size() method and by the fact that access methods that receive an index, as at() or operator[], receive an unsigned value.
Right or wrong, the language itself is designed to return a std::size_t from the old sizeof() and from more recent variadic sizeof...(). The same class std::index_sequence is an alias for std::integer_sequence with a fixed unsigned, again std::size_t, type.
In a world designed to use unsigned integers for indexes, the use of a signed integer for they it's possible but, IMHO, dangerous (because error prone).
I've used this pattern several times when creating a class templates of a matrix class with variable dimensions.
Matrix.h
#ifndef MATRIX_H
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_; // Technically this vector contains the size of each dimension... (its shape)
// actual strides would be the width in memory of each element to that dimension of the container.
// A better name for this container would be dimensionSizes_ or shape_
public:
Matrix() noexcept;
template<typename... Arg>
Matrix( Arg&&... as ) noexcept;
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_;
}
}; // matrix
#include "Matrix.inl"
#endif // MATRIX_H
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_ );
} // Matrix
template<typename Type, size_t... Dims>
template<typename... Arg>
Matrix<Type, Dims...>::Matrix( Arg&&... as ) noexcept :
elements_( { as... } ),
strides_( { Dims... } ){
numElements_ = elements_.size();
} // Matrix
template<typename T, size_t... d>
const T& Matrix<T,d...>::operator[]( size_t idx ) const {
return elements_[idx];
} // Operator[]
Matrix.cpp
#include "Matrix.h"
#include <vector>
#include <numeric>
#include <functional>
#include <algorithm>
main.cpp
#include <vector>
#include <iostream>
#include "matrix.h"
int main() {
Matrix<int, 3, 3> mat3x3( 1, 2, 3, 4, 5, 6, 7, 8, 9 );
for ( size_t idx = 0; idx < mat3x3.numElements(); idx++ ) {
std::cout << mat3x3.elements()[idx] << " ";
}
std::cout << "\n\nnow using array index operator\n\n";
for ( size_t idx = 0; idx < mat3x3.numElements(); idx++ ) {
std::cout << mat3x3[idx] << " ";
}
std::cout << "\n\ncheck the strides\n\n";
for ( size_t idx = 0; idx < mat3x3.numDims_; idx++ ) {
std::cout << mat3x3.strides()[idx] << " ";
}
std::cout << "\n\n";
std::cout << "=================================\n\n";
Matrix<float, 5, 2, 9, 7> mf5x2x9x7;
// Check Strides
// Num Elements
// Total Size
std::cout << "The total number of dimensions are: " << mf5x2x9x7.numDims_ << "\n";
std::cout << "The total number of elements are: " << mf5x2x9x7.numElements() << "\n";
std::cout << "These are the strides: \n";
for ( size_t n = 0; n < mf5x2x9x7.numDims_; n++ ) {
std::cout << mf5x2x9x7.strides()[n] << " ";
}
std::cout << "\n";
std::cout << "The elements are: ";
for ( size_t n = 0; n < mf5x2x9x7.numElements(); n++ ) {
std::cout << mf5x2x9x7[n] << " ";
}
std::cout << "\n";
std::cout << "\nPress any key and enter to quit." << std::endl;
char c;
std::cin >> c;
return 0;
} // main
This is a simple variable multidimensional matrix class of the Same Type <T>
You can create a matrix of floats, ints, chars etc of varying sizes such as a 2x2, 2x3, 5x3x7, 4x9x8x12x2x19. This is a very simple but versatile class.
It is using std::vector<> so the search time is linear. The larger the multi - dimensional matrix grows in dimensions the larger the internal container will grow depending on the size of each dimension; this can "explode" fairly quick if each individual dimensions are of a large dimensional size for example: a 9x9x9 is only a 3 dimensional volumetric matrix that has many more elements than a 2x2x2x2x2 which is a 5 dimensional volumetric matrix. The first matrix has 729 elements where the second matrix has only 32 elements.
I did not include a default constructor, copy constructor, move constructor, nor any overloaded constructors that would accept either a std::container<T> or another Matrix<T,...>. This can be done as an exercise for the OP.
I also did not include any simple functions that would give the size of total elements from the main container, nor the number of total dimensions which would be the size of the strides container size. The OP should be able to implement these very simply.
As for the strides and for indexing with multiple dimensional coordinates the OP would need to use the stride values to compute the appropriate indexes again I leave this as the main exercise.
EDIT - I went ahead and added a default constructor, moved some members to private section of the class, and added a few access functions. I did this because I just wanted to demonstrate in the main function the power of this class even when creating an empty container of its type.
Even more you can take user Yakk's answer with his "stride & slice" algorithm and should easily be able to plug it right into this class giving you the full functionality of what you are looking for.
Related
I have a class variable defined like this:
std::shared_ptr<int[]> variable;
I want to make it store ints from 0 to 10
so that when I call variable[1] it returns 1 and so on.
I would use the std::array syntax like this
#include <array>
#include <iostream>
#include <memory>
int main()
{
// use the std::array syntax it will make the dereferenced smartpointer
// directly behave like an array. You can use index operator and even
// use it in range based for loops directly
auto variables = std::make_shared<std::array<int, 10>>();
for (std::size_t n = 0ul; n < variables->size(); ++n)
{
(*variables)[n] = n;
}
for (const int value : *variables)
{
std::cout << value << " ";
}
return 0;
}
How to initialize a shared_ptr as an array of int in C++
If there are only a limited number of values you'd like in the array, the most practical may be to use new[] with an initializer:
std::shared_ptr<int[]> variable(new int[]{0,1,2,3,4,5,6,7,8,9,10});
If you want to initialize it with a bigger range, you could create a helper that uses an index_sequence and pack expansion:
template<std::size_t N, class T>
std::shared_ptr<T[]> make_shared_iota(T start, T stride) {
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return std::shared_ptr<T[]>(new T[]{start + stride * T{Is}...});
}(std::make_index_sequence<N>());
}
// create an array of 20 `int` {3,5,7,...,37,39,41}
auto variable = make_shared_iota<20>(3, 2);
or with a little more freedom, using a functor to provide a formula for calculating the values to initialize the array with:
template<std::size_t N, class Func>
auto make_shared_iota(Func&& func) {
using type = decltype(func(std::size_t{}));
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return std::shared_ptr<type[]>(new type[]{std::invoke(func, Is)...});
}(std::make_index_sequence<N>());
}
// create an array of 20 `int` {3,5,7,...,37,39,41}
auto variable = make_shared_iota<20>([](std::size_t x) {
return static_cast<int>(3 + x * 2); });
I have a 3D Matrix with the MxNxL elements. Both M and N are known at the compile time. Whereas, L is variable and depends on user's input. But it will be provided at the beginning of the program and will never change during the life time of the program. I want to implement this Matrix in C++11 and I want to give the user the flexibility to rotate the 3D Matrix along both the the 1st and 2nd dimension of the matrix.
I would like to know what is the best and most efficient design option to implement this Matrix?
I have seen the below solution which uses std::vector. With std::vector the user can rotate any dimension using std::rotate. The solution is taken from this thread. However, vsoftco mentions that it is not good to use nested vector but rather make it linear. But since I have the requirement of rotating across dimension, having linear array will make the processing hard.
#include <vector>
#define M_SIZE 360
#define N_SIZE 180
template<typename T>
using vec = std::vector<T>;
int main()
{
uint16_t L;
std::cin << L;
vec<vec<vec<double>>> v{M_SIZE, vec<vec<double>>{N_SIZE, vec<double>{L}}};
}
Again, using dynamic c array is a possible solution but std::rotate would only work on the last dimension of the 3D Matrix.
Note: I would prefer to do this without relying on a third party library.
I don't know if this will help you to achieve what you are after but this is a Matrix class that I have started to build using modern C++ features; such as variadic templates. The Matrix Class is self contained into a single header file.
#ifndef MATRIX_H
#define MATRIX_H
#include <cstddef> // std::size_t
#include <numeric> // std::accumulate
#include <vector>
namespace /*Your namespace name here*/ {
template<typename Type, std::size_t... Dims>
class Matrix {
public:
static const std::size_t numDims_ = sizeof...(Dims);
private:
std::size_t numElements_;
std::vector<Type> elements_;
std::vector<std::size_t> strides_;
public:
Matrix() noexcept;
template<typename... Args>
Matrix(Args&&... args) noexcept;
const Type& operator[](std::size_t idx) const;
std::size_t numElements() const { return elements_.size(); }
const std::vector<std::size_t>& strides() const { return strides_; }
const std::vector<Type>& elements() const { return elements_; }
};
template<typename Type, std::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, std::size_t... Dims>
template<typename... Args>
Matrix<Type, Dims...>::Matrix(Args&&... args) noexcept :
elements_({ args... }),
strides_({ Dims... }) {
numElements_ = elements_.size();
}
template<typename T, std::size_t... d>
const T& Matrix<T, d...>::operator[](std::size_t idx) const {
if (idx > numElements_)
throw std::runtime_error("Invalid index");
return elements_[idx];
}
} // Your namespace name here
#endif MATRIX_H
And a small sample program using it:
#include <iostream>
#include <exception>
#include "Matrix.h"
int main() {
try {
using /*your namespace name here*/;
Matrix<double, 2, 2, 2> mat( 1.0 ,1.1, 1.2, 1.3,
1.4, 1.5, 1.6, 1.7 );
// Print the elements from the use of getting the vector
for (auto& e : mat.elements()) {
std::cout << e << " ";
}
std::cout << '\n';
// Print the elements from the use of using operator[]
for ( std::size_t n = 0; n < mat.numElements(); n++ ) {
std::cout << mat[n] << " ";
}
std::cout << '\n';
// Print out the strides
std::cout << "Number of strides: " << mat.numDims_ << '\n';
for (auto& s : mat.strides()) {
std::cout << s << " ";
}
std::cout << '\n';
} catch ( std::exception& e ) {
std::cerr << e.what();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
-Output-
1 1.1 1.2 1.3 1.4 1.5 1.6 1.7
1 1.1 1.2 1.3 1.4 1.5 1.6 1.7
Number of strides: 3
2 2 2
This class is far from complete as it is only just the containing shell of any arbitrary Dimensional Size of Any MxNx...Zx... Matrix. In the template argument list it expects a single Type: int, float, char, user defined etc. The variadic arguments for the tempale arguments after the Type determines how many Dimensions this matrix has, and the dimensional size of each dimension. Examples:
Matrix<type,1> = a 1 matrix which in essence would be a scalar
Matrix<type,1,2> = a 1x2 matrix and would be considered a vector
Matrix<type,3,1> = a 3x1 matrix and would be considered a vector
Matrix<type,2,2> = a 2x2 matrix
Matrix<type,3,4> = a 3x4 matrix
Matrix<type,3,3,3> = a 3x3x3 Matrix (3D-Matrix) and has 27 elements
Matrix<type,3,4,5,3,2> = a 3x4x5x3x2 (5D - Matrix) and has 360 elements
// The number of elements will always equal the product of all of the strides.
// When creating an arbitrary matrix size; careful consideration needs to be taken
// when it comes to how many dimensions and the size of that dimension. Even lower
// dimensional matrices can explode in the amount of elements...
Matrix<type, 128, 356, 242> = a 128x356x242 (3D - Matrix) but has 11,027,456 elements
// It is the Matrix's user defined variadic constructor that the number of arguments
// in the parameter list that has to equal the total amount of elements defined by
// the product of all of its strides.
When working with higher dimensional matrices it is hard to visualize them, but they can easily be managed with the use of the strides vector. We can use these for proper indexing.
The challenging aspect of this design approach is being able to implement the actual arithmetic-computational operators. For this if we have two different matrices that have different strides, they will be treated as different types... and this is just with simple matrix arithmetic; when it comes to matrix multiplication things even get harder because the edges of Matrix A and Matrix B have to be of the same size. Ex: 4x2 * 2x3 would give a 4x3 Matrix. And this is only a 2D matrix issue, when getting into 3D, 4D, ... ND matrices the notation even gets harder. However this template approach does allow the code to be more generic and readable to be self contained into a single class as opposed to having many different matrix classes for various matrix sizes...
What I am trying to do is to shuffle an existing array (vector). There is a catch here, there are actually two arrays (vectors) that depend on each other.
To be more exact, I have a 2d vector which contains patterns , so each row denotes a single pattern, and then there is another 2d vector which contains the desired output of each pattern.
So it would look like something like this :
vector<vector<float>> P{ vector < float > {0, 0},
vector < float > {1, 0},
vector < float > {0, 1},
vector < float > {1, 1} };
vector<vector<float>> T{ vector < float > {0},
vector < float > {1},
vector < float > {1},
vector < float > {0} };
Now I need to shuffle the patterns collection, so their individual rows order differ each time we traverse P. again I mean, since P's size() here is 4, and thus we have 4 patterns, and we want to select one at a time till we access all of them.
When all of the patterns are selected one after the other, an epoch is completed, and we need to change the patterns order for the next epoch. We are going to do this for an arbitrary number of times, and each time, these patterns order need to get changed, (e.g. the first time (0,0) is first, followed by (0,1) and (1,0) and finally (1,1), in the second epoch we might be having (1,1) (1,0) (0,0) (0,1) as the patterns).
So when we shuffle the pattern collection, we need to have the targets collection shuffled exactly the same as well. What is fastest way of doing so? There are different ways that ran through my head, such as:
creating a map out of these two arrays, and map each pattern with the corresponding target, and then shuffle the the patterns collection. Whenever there is a need for a target it can easily be accessed by the map.
use tuples to create a new list and shuffle the newly created tuple and get going.
just use a random number between 0 to 3 and pick a number, (a pattern index) and use that, store the index in an array, which is used to prevent selecting the same index twice in one epoch.
What would you suggest in this scenario?
It seems that you want to shuffle indexes:
std::vector<std::size_t> indexes{0, 1, 2, 3}; // or initialize with std::iota
std::shuffle(indexes.begin(), indexes.end(), my_random_generator);
Your question is very hard to answer definitively as it lacks a lot of information. And even with all the information needed, a definitive answer would still be very hard to give without measuring different options.
The first and most important question is: what is it that you're trying to make fast - generating a new epoch or accessing your data? Answering this question would require knowing the size of the actual data you have, the ways and number of times you access your data in the other code, how is your data modified/generated during runtime, etc.
Here's some general advice though. If you know the size of inner vectors of your T and P - use std::array instead of std::vector. This way your inner arrays will be laid out in a single chunk of memory improving cache behaviour. For the same reason, if you can, combine the patterns and outputs into a std::tuple or a std::pair or a struct for that matter and put them all in one array.
Let's assume you can put them into a single vector. Then the regarding the shuffling itself, you can either take the approach with shuffling indices into a static vector or shuffling the vector itself. Shuffling a vector of indices will likely be faster, but you will pay an additional indirection every time you access your pattern-outcome pairs which might make your overall performance way worse than shuffling the vector itself. Your access patterns are of the utmost importance when making the decision - measure your options!
If for some reason you absolutely cannot put everything in one vector and additional array of indices is too expensive, consider using this code (note, you need boost and c++14 compiler for this to work, live demo here):
#include <iostream>
#include <string>
#include <random>
#include <vector>
#include <tuple>
#include <utility>
#include <algorithm>
#include <boost/iterator/iterator_facade.hpp>
template <typename... IteratorTypes>
using value_tuple = std::tuple<typename IteratorTypes::value_type...>;
template <typename... IteratorTypes>
class reference_tuple : public std::tuple<typename IteratorTypes::value_type&...> {
using std::tuple<typename IteratorTypes::value_type&...>::tuple;
};
template<typename... IteratorTypes, size_t... Index>
void swap_impl(reference_tuple<IteratorTypes...> left, reference_tuple<IteratorTypes...> right, std::index_sequence<Index...>)
{
using std::swap;
int dummy[] = {(swap(std::get<Index>(left), std::get<Index>(right)), 0)...};
(void)dummy;
}
template <typename... IteratorTypes>
void swap(reference_tuple<IteratorTypes...> left, reference_tuple<IteratorTypes...> right)
{
swap_impl(left, right, std::index_sequence_for<IteratorTypes...>{});
}
template <typename... IteratorTypes>
class zip_iter
: public boost::iterator_facade<
zip_iter<IteratorTypes...> // Derived
, value_tuple<IteratorTypes...> // Value
, boost::random_access_traversal_tag
, reference_tuple<IteratorTypes...> // Reference
>
{
public:
zip_iter() = default;
explicit zip_iter(IteratorTypes... iters)
: iterators(iters...)
{
}
private:
friend class boost::iterator_core_access;
void increment() { increment_impl(std::index_sequence_for<IteratorTypes...>()); }
template<size_t... Index>
void increment_impl(std::index_sequence<Index...>)
{
int dummy[] = {(++std::get<Index>(iterators), 0)...};
(void)dummy;
}
void decrement() { decrement_impl(std::index_sequence_for<IteratorTypes...>()); }
template<size_t... Index>
void decrement_impl(std::index_sequence<Index...>)
{
int dummy[] = {(--std::get<Index>(iterators), 0)...};
(void)dummy;
}
template<typename diff_t>
void advance(diff_t n) { advance_impl(n, std::index_sequence_for<IteratorTypes...>()); }
template<typename diff_t, size_t... Index>
void advance_impl(diff_t n, std::index_sequence<Index...>)
{
int dummy[] = {(std::advance(std::get<Index>(iterators), n), 0)...};
(void)dummy;
}
bool equal(zip_iter const& other) const
{
return std::get<0>(iterators) == std::get<0>(other.iterators);
}
auto dereference() const {
return dereferenceImpl(std::index_sequence_for<IteratorTypes...>{});
}
template<std::size_t... Index>
auto dereferenceImpl(std::index_sequence<Index...>) const
{
return reference_tuple<IteratorTypes...>(*std::get<Index>(iterators)...);
}
auto distance_to(zip_iter const& r) const
{
return std::distance(std::get<0>(iterators), std::get<0>(r.iterators));
}
std::tuple<IteratorTypes...> iterators;
};
template<typename... Iterators>
auto make_zip_iter(Iterators... iters)
{
return zip_iter<Iterators...>(iters...);
}
int main()
{
std::mt19937 rng(std::random_device{}());
std::vector<int> ints(10);
std::iota(ints.begin(), ints.end(), 0);
std::cout << "Before: ";
for (auto i : ints) {
std::cout << i << " ";
}
std::cout << "\n";
std::vector<int> ints2{ints};
std::shuffle(make_zip_iter(ints.begin(), ints2.begin()), make_zip_iter(ints.end(), ints2.end()), rng);
std::cout << "Are equal: " << (ints == ints2) << "\n";
std::cout << "After: ";
for (auto i : ints) {
std::cout << i << " ";
}
}
I have several constant matrices of defferent sizes of both dimensions, say
const int denoise[][3] = {...}.
const int deconv[][4] = {...}
Then I define a function like void handleMatrix(const int* const*){...} hoping to handle these matrices. But it is uncorrect.
one effort I tried is using a template like:
template<typename Ty> void handle Matrix(const Ty m){...}
It works perfectly on vs2013.
But how should I pass those matrices to a function without using template?
You should use a typedef so that you don't have to use any awful syntax:
using matrix_t = int[3][3];
And you should pass your args by reference whenever possible:
void handle_matrix(const matrix_t &mat){
// do something with 'mat'
}
If you want to use the original syntax without a typedef:
void handle_matrix(const int (&mat)[3][3]){
// ...
}
and if you want to use the original syntax and pass by pointer:
void handle_matrix(const int (*mat)[3]){
// ...
}
But then you lose type safety, so I'd recommend against this and just go with the nicest option: typedef and pass by reference.
EDIT
You said in a comment on #Kerrek SB's answer that your matrices will be different sizes.
So here is how to handle that and still keep the nice method:
template<size_t Columns, size_t Rows>
using matrix_t = int[Columns][Rows];
template<size_t Columns, size_t Rows>
void handle_matrix(const matrix_t<Columns, Rows> &mat){
// ...
}
Take into account that I'm presuming you can use C++14 in my answer, if you leave a comment I can modify it for any other version.
Your matrices are arrays of int[3]s. If you want C-style argument passing, you'd pass a pointer the first element of the array plus a size:
using Row = int[3];
void foo(const Row * p, std::size_t n_cols)
{
for (std::size_t i = 0; i != n_cols; ++i)
{
for (int n : p[i]) { std::cout << n << ' '; }
std::cout << '\n';
}
}
Usage example:
Row * matrix = new Row[40]();
foo(matrix, 40);
delete [] matrix;
With a typed variable:
Row matrix[] = { {1,2,3}, {2,3,4} };
foo(matrix, std::distance(std::begin(matrix), std::end(matrix)));
Is it possible for a class to have a member which is a multidimensional array whose dimensions and extents are not known until runtime?
I have found (via this guide) a way to create a struct to easily nest std::arrays at compile time using template metaprogramming:
#include <array>
/*
this struct allows for the creation of an n-dimensional array type
*/
template <typename T,size_t CurrentDimExtent,size_t... NextDimExtent>
struct MultiDimArray{
public:
//define the type name nestedType to be a recursive template definition.
using nestedType=typename MultiDimArray<T,NextDimExtent...>::type;
using type=std::array<nestedType,CurrentDimExtent>;
};
/*
This struct is the template specialization which handles the base case of the
final dimensional extent
*/
template <typename T,size_t DimExtent>
struct MultiDimArray<T,DimExtent>{
using type=std::array<T,DimExtent>;
};
this still falls short of satisfying my requirement in two ways (that I know of):
In order to declare a variable (or a pointer to a variable) of this type you must state the dimensions.
This only works when the DimExtents are constant expressions (set at compile time).
To demonstrate why number 2 is a distinct problem, here is a class with a set number of dimensions (2) using a void* to reference the multidimensional array:
template <typename T>
class TwoDimGrid{
public:
TwoDimGrid(const size_t extent1,const size_t extent2):
_twoDimArray(new MultiDimArray<T,extent1,extent2>);
private:
void* _twoDimArray;
};
This will not compile as extent1 and extent2 are not constant expressions.
other notes:
I would like to see if it's possible to accomplish using std:array, rather than native arrays or a dynamically resizing container like std::vector.
Please use smart pointers where appropriate (I didn't as I'm not really sure how to handle a smart void pointer).
Edit
I have fallen into the trap of The XY Problem with X being the first sentence of this question and Y being how to accomplish it with std::array. I therefore created a new question and am leaving this one here in case it's ever possible to solve Y problem.
old school multidimensional arrays, something along these lines:
template <typename T>
class multi
{
T*myArray;
size_t x_dim;
size_t y_dim;
public:
multi(size_t x, size t y) : x_dim(x), y_dim(y)
{
myArray = new T[x*y];
}
T& get(int x, int y)
{
return myArray[x*y_dim+y];
}
};
template <typename T>
class vvc
{
//possible ragged array ..non rigorous approach
//with management memory cost per element
//clearly not as efficient as .... linearized access where access index is
//row size * r + column
//memory management courtesy of vector
public:
std::vector< std::vector<T> > v;
};
int double_vector()
{
int x1 = 5;
int x2 = 3;
std::vector<int> r(x2);
vvc<int> vv;
int k = 0;
for (int i1 = 0; i1 < x1; ++i1)
{
for (int i2 = 0; i2 < x2; ++i2)
{
k += 1;
r[i2] = k;
}
vv.v.push_back(r);
}
//inspect
cout << vv.v[0][0] << " first " << endl;
for (auto const & t1 : vv.v)
{
for (auto const &t2 : t1 )
{
cout << t2 << " ";
}
cout << endl;
}
return 0;
}