4
votes

I originally asked using nested std::array to create an multidimensional array without knowing dimensions or extents until runtime but this had The XY Problem of trying to accomplish it with std::array.

The questions One-line initialiser for Boost.MultiArray and How do I make a multidimensional array of undetermined size a member of a class in c++? and their answers give some helpful information how to use Boost::MultiArray to avoid needing to know the extents of the dimensions at runtime, but fail to demonstrate how to have a class member that can store an array (created at runtime) whose dimensions and extents are not known until runtime.

4
Do you mean the number of dimensions are unknown at compile time, or that there are N dimensions of unknown size?Zac Howland
As far as I can tell (from a quick look at the boost documentation) you can simply specify everything in the constructor as I would have expectedSebastian Hoffmann
If you look at my solution to your 1st try: stackoverflow.com/a/20207616/2963099 and then you keep a vector of dimensions and variable args for the constuctor, you should have an idea of how to do thisGlenn Teitelbaum
@ZacHowland I mean that both the number of dimensions, as well as their extents (sizes) are unkown at runtime.Key Lay
Glenn's answer will solve your problem (and is frankly the only want to solve it), but it sounds like you are still suffering from an XY problem. Why do you think you need a dynamic number of dimensions?Zac Howland

4 Answers

4
votes

Just avoid multidimensional arrays:

template<typename T>
class Matrix
{
    public:
    Matrix(unsigned m, unsigned n)
    :   n(n), data(m * n)
    {}

    T& operator ()(unsigned i, unsigned j)  {
        return data[ i * n + j ];
    }

    private:
    unsigned n;
    std::vector<T> data;
};

int main()
{
    Matrix<int> m(3, 5);
    m(0, 0) = 0;
    // ...
    return 0;
}

A 3D access (in a proper 3D matrix) would be:

T& operator ()(unsigned i, unsigned j, unsigned k)  { 
    // Please optimize this (See @Alexandre C) 
    return data[ i*m*n + j*n + k ]; 
}

Getting arbitrary dimensions and extent would follow the scheme and add overloads (and dimensional/extent information) and/or take advantage of variadic templates.

Having a lot of dimensions you may avoid above (even in C++11) and replace the arguments by a std::vector. Eg: T& operator(std::vector indices). Each dimension (besides the last) would have an extend stored in a vector n (as the first dimension in the 2D example above).

1
votes

Yes. with a single pointer member.

A n multidimensional array is actually a pointer. so you can alocate a dynamic n array and with casting, and put this array in the member pointer.

In your class should be something like this

int * holder;

void setHolder(int* anyArray){
  holder = anyArray;
}

use:

int *** multy = new int[2][1][56];
yourClass.setHolder((int*)multy);
1
votes

You can solve the problem in at least two ways, depending on your preferences. First of all - you don't need the Boost library, and you can do it yourself.

class array{
   unsigned int dimNumber;
   vector<unsigned int> dimSizes;

   float *array;

   array(const unsigned int dimNumber, ...){
     va_list arguments;
     va_start(arguments,dimNumber);
     this->dimNumber = dimNumber;
     unsigned int totalSize = 1;
     for(unsigned int i=0;i<dimNumber;i++)
     {
        dimSizes.push_back(va_arg(arguments,double));
        totalSize *= dimSizes[dimSizes.size()-1];
     }
     va_end(arguments);
     array = new float[totalSize]; 
   };

   float getElement(unsigned int dimNumber, ...){
     va_list arguments;
     va_start(arguments,dimNumber);
     unsgned int elementPos = 0, dimAdd = 1;
     for(unsigned int i=0;i<dimNumber;i++)
     {
        unsigned int val = va_arg(arguments,double);
        elementPos += dimAdd * val;
        dimAdd *= dimsizes[i];
     }
     return array[elementPos]
   };
};

Setting an element value would be the same, you will just have to specify the new value. Of course you can use any type you want, not just float... and of course remember to delete[] the array in the destructor.

I haven't tested the code (just wrote it straight down here from memory), so there can be some problems with calculating the position, but I'm sure you'll fix them if you encounter them. This code should give you the general idea.

The second way would be to create a dimension class, which would store a vector<dimension*> which would store sub-dimensions. But that's a bit complicated and too long to write down here.

0
votes

Instead of a multidimensional array you could use a 1D-array with an equal amount of indices. I could not test this code, but I hope it will work or give you an idea of how to solve your problem. You should remember that arrays, which do not have a constant length from the time of being compiled, should be allocated via malloc() or your code might not run on other computers. (Maybe you should create a class array for the code below)

#include <malloc.h>

int* IndexOffset; //Array which contains how many indices need to be skipped per  dimension
int  DimAmount;   //Amount of dimensions
int  SizeOfArray = 1; //Amount of indices of the array

void AllocateArray(int* output,         //pointer to the array which will be allocated
                   int* dimLengths,     //Amount of indices for each dimension: {1D, 2D, 3D,..., nD}
                   int dimCount){       //Length of the array above

DimAmount = dimCount;
int* IndexOffset = (int*) malloc(sizeof(int) * dimCount);
int temp = 1;

for(int i = 0; i < dimCount; i++){

temp = temp * dimLengths[i];
IndexOffset[i] = temp; 

}


for(int i = 0; i < dimCount; i++){

SizeOfArray = SizeOfArray * dimLengths[i];

}

output = (int*)malloc(sizeof(int) * SizeOfArray);

}

To get an index use this:

int getArrayIndex(int* coordinates //Coordinates of the wished index as an array (like dimLengths)
            ){

int index;
int temp = coordinates[0];

for(int i = 1; i < DimAmount; i++){

     temp = temp + IndexOffset[i-1] * coordinates[i];

}
index = temp;
return index;
}

Remember to free() your array as soon as you do not need it anymore:

for(int i = 0; i < SizeOfArray; i++){

    free(output[i]);

}
free(output);