kozouu kozouu - 2 months ago 14
C++ Question

How to create a matrix with user-specified dimension

I need to store some data based on user's input. My program should create different data structures based on an int value that I get from cin.

For example, a value of 0 stores a scalar, 1 - vector, 2 - 2dArray, 3 - 3dArray, 4 - 4d array and so on...

My question is whether it's possible to write some code that will do this.

I know this might sound confusing, so I'll provide a few more examples.

Let's say the user enters 0 5, then my program should create an int variable and store 5 in it.

If the user enters 1 5,7,6, my program should create a vector = {5,7,6};

If the user enters 2 2,3 1,2,3,4,5,6 my program should create a 2d array a[2][3] and store the values there.

I could figure out a solution if I knew the maximum number of dimensions the user is going to have, but the goal of the assignment is that any number of dimensions can be specified...

Help please

Answer

It is possible if you relax some requirements ("create different data structures based on an int value that I get from cin"). Here's just a sketch, not a full answer, but hopefully it will put you on track.

Access to storage:

You will need to store the numbers in a single array of the desired type and wrap the access to them through an index mapping function. For example, in 2D, one such function is

int indexIn2D(uint rowCol[2], int rowCount) {
  // rowCol[0] - is row index, rowCol[1] is col index
  return rowCol[0]*rowCount + rowCol[1];
}

float* values=new float[numRows*numCols];

// I want the element at (i,j)
float& myIJElement=values[indexIn2D({i,j}, numRows)];

Transforming this into a N-dimension will require something on the line of

// suppose I'm storing a 3D matrix with [3 slices, 4 rows, 5 columns]
// as a 1D array and I want the element at [x,y,z] - what's it would be
// the position of that element in my 1D array?
// - fill coodIndex[3] with {x,y,z}
// - pass nDim=3
// - the dimSignature will be {3 /*slices*/, 4 /*rows*/, 5 /*columns*}
int indexInND(uint coordIndex[], uint numDim, uint[] dimSignature) {
   int ret=coordIndex[0];
   for(uint i=0; i<numDim-; i++) {
      ret=ret*dimSignature[i]+coordIndex[i+1];
   }
   return ret;
}

Variant-like type of storage

Well, we already know we'll be storing the entire "N-dim block" as an unidim array of the target type. So we can make use of pointers and have our "storage" something like

struct NDimStorage {
  // 0 for int, 1 for float, etc.
  int whichType; // or make it enum
  union {
    int* int_st;
    float* float_st;
  };
  uint numDims;
  uint dimSignature[];
};

Fallback fom variant to std::vector

Something like:

template <typename NumType> class VectNStorage {
   std::vector<NumType> storage;
   std::vector<uint> dimSignature;
protected:
   size_t indexMapping(const std::vector<uint>& indices) {
     size_t ret=indices[0];
     for(uint i=0; i<this->dimSignature.size()-1) {
       ret=ret*this->dimSignature[i]+indices[i+1];
     }
     return ret;
   }
public:
  VectNStorage(const std::vector<uint> dimsDef) : storage(), dimSignature(dimsDef) {
    uint howMany=1;
    for(auto d : this->dimSignature) {
      howMany*=d;
    }
    this->storage.resize(howMany);
    std::fill(this->storage.begin(), this->storage.end(), 0);
  }

  NumType& at(const std::vector<uint>& indices) {
    return this->storage[this->indexMapping(indices)];
  }
}