简体   繁体   中英

A class to manage multidimensional array! How can I do to manage different data types in the cells?

I state that I am a C programmer more than a C++ programmer (with C++ I'm a beginner: p)

I've written a C++ class to manage multidimensional arrays (n-dimensional matrices). This class has methods to create the matrix and to set and to get value in/from the matrix (also to set the position inside it).

I've two issues:

  • With the idea that I want a syntax similar to m(x,y,z, ..., n) to set/get values some methods use the ellipsis EG: getValue(int dim0,...); But I think it's dangerous; although in the functions I've written I assume that the passed arguments are the same number of the matrix dimensions, the user might pass less values than necessary, then he will have no compiler errors or warnings.

  • I want to be able to manage the data type that the matrix cells contain during runtime at matrix creation time (without the use of union and somehow declaring the type). In the class code I've inserted a typedef (for development purposes) which indicates the points where such a modifications would/might impact.

For the first issue I've no ideas better than those implemented. Do you have suggestions?

To solve the second issue, I might think to create overloads for the methods getValue(), setValue() and createMatrix(), but this idea requires some workarounds and probably to rewrite completely each single method and then to have more copies of the "same" code (I want avoid that with the intent to have a better maintenance capability) , moreover this solution doesn't grant to manage all possible types. I'm thinking about the use of templates, but I don't understand if such a way is the better way. Do you have suggestions?

These are the main methods to be modified to solve both the issues:

methods in the CPP module:

int Matrix::createMatrix(Matrix::value_t *values)
{
    int retval=1;

    if ( (m_values!=NULL || !numOfDim()) && valAreIntAlloc()==true ) {
        return 0;
    }

    if (values!=NULL) {
        m_values=values;
        setValAreIntAlloc(false);
    } else {
        setValAreIntAlloc(true);
        // Compute the number of elements for
        // the whole matrix
        for(int i=0;i<numOfDim();i++) {
            retval*=numOfElemInDim(i);
            if (!retval) {
                //Indicate that a dimension has a 0 value
                //as numOfElemInDim! The caller will be allowed
                //to know the such a dimension using: -retval-1;
                retval=-(i+1);
                break;
            }
        }
        if (retval>0) {
            m_values=new value_t[retval];
            if (m_values!=NULL)
                retval=0;
        }
    }

    //Returns:
    //1 if values is an external allocated memory,
    //0 if nothing has been allocated or the
    //m_values is already set as internal!
    //elsewhere the number of allocated elements.
    return retval;
}

void Matrix::setPositions(int dim0, ...)
{
    va_list vl;
    va_start(vl,dim0);

    setPosition(0,dim0);
    for (int i=1;i<numOfDim();i++)
        setPosition(i,va_arg(vl,int));

    va_end(vl);
}

Matrix::value_t Matrix::getValue(int dim0, ...)
{
    va_list vl;
    va_start(vl,dim0);

    setPosition(0,dim0);
    for (int i=1;i<numOfDim();i++)
        setPosition(i,va_arg(vl,int));

    va_end(vl);
    return getValue();
}

void Matrix::setValue(Matrix::value_t value, int dim0, ...)
{
    va_list vl;
    va_start(vl,dim0);

    setPosition(0,dim0);
    for (int i=1;i<numOfDim();i++)
        setPosition(i,va_arg(vl,int));

    va_end(vl);
    setValue(value);
}

methods inline in the Header file:

inline value_t getValue() { return m_values[posInValueVector()]; }

inline void setValue(value_t value) { m_values[posInValueVector()]=value; }

The methods setPosition(), setPositions() are used to set the coordinates of the matrix cell to be managed; the method posInValueVector() computes the index inside the vector (created by the method createMatrix) of the matrix cell using the coordinates.

Here al the code:

main.cpp

#include <iostream>
#include <cstdio>

#include "matrix.h"

using namespace std;

int main()
{
    Matrix m(3);
    m.setNumOfElemInDims(4,5,6);
    m.createMatrix();

    for(int i=0;i<m.numOfElemInDim(0);i++)
        for(int j=0;j<m.numOfElemInDim(1);j++)
            for(int k=0;k<m.numOfElemInDim(2);k++)
                m.setValue(i*100+j*10+k,i,j,k); // matrix(i,j,k)=i*100+j*10+k

    //printout the values of all matrix(i,j,k) cells
    //I've used the printf because I find it very simple!
    for(int i=0;i<m.numOfElemInDim(0);i++)
        for(int j=0;j<m.numOfElemInDim(1);j++)
            for(int k=0;k<m.numOfElemInDim(2);k++)
                printf("(%d,%d,%d)=%03.0f\n",i,j,k,m.getValue(i,j,k));
}

matrix.h

#ifndef MATRIX_H
#define MATRIX_H

class Matrix
{
public:
    typedef double value_t;

    Matrix();
    Matrix(int numOfDim, int *nelem=NULL);
    ~Matrix();

    inline unsigned int numOfDim() const {return m_numOfDim;}

    int setNumOfDim(int numOfDim, int *nelem=NULL);

    inline  int numOfElemInDim(int dim) const
        {return (dim<numOfDim())?m_numOfElemInDim[dim]:-1; }

    int setNumOfElemInDim(int dim, int nelem);
    int setNumOfElemInDims(int el0, ...);

    int createMatrix(value_t *values);
    inline int createMatrix() { return createMatrix(NULL); }

    inline bool valAreIntAlloc() const {return m_valAreIntAlloc;}
    inline void setValAreIntAlloc(bool valAreIntAlloc)
        {m_valAreIntAlloc = valAreIntAlloc;}

    inline int position(int dim) const {return m_positions[dim];}
    inline void setPosition(int dim,int value)
        {m_positions[dim] = value;}

    inline void setPositions(int *positions)
        {for(int i=0;i<numOfDim();i++) setPosition(i,positions[i]);}

    void setPositions(int dim0, ...);

    inline value_t getValue() { return m_values[posInValueVector()]; }

    value_t getValue(int dim0, ...);

    inline void setValue(value_t value) { m_values[posInValueVector()]=value; }
    void setValue(value_t value, int dim0, ...);

private:
    int m_numOfDim;
    int * m_numOfElemInDim;
    int * m_positions;

    value_t * m_values;
    bool m_valAreIntAlloc;

    int posInValueVector();
};

#endif // MATRIX_H

matrix.cpp

#include <iostream>
#include <cstdarg>

#include "matrix.h"

#define __INIT__(v)\
m_numOfDim(v),\
m_numOfElemInDim(NULL),\
m_positions(NULL),\
m_values(NULL),\
m_valAreIntAlloc(false)

Matrix::Matrix():
    __INIT__(0)
{
}

Matrix::~Matrix()
{
    if (m_numOfElemInDim!=NULL)
        delete m_numOfElemInDim;

    if (m_positions!=NULL)
        delete m_positions;

    if (valAreIntAlloc() && m_values!=NULL)
        delete m_values;
}

Matrix::Matrix(int numOfDim, int *nelem):
    __INIT__(numOfDim)
{
    setNumOfDim(numOfDim,nelem);
}

int Matrix::setNumOfDim(int numOfDim, int *nelem)
{
    int retval=0;

    m_numOfDim = numOfDim;

    m_numOfElemInDim = new int[numOfDim];
    if (m_numOfElemInDim==NULL)
        return 1;

    m_positions = new int[numOfDim];
    if (m_positions==NULL)
        return 2;

    for(int i=0;i<m_numOfDim;i++) {
        if (setNumOfElemInDim(i,(nelem==NULL)?0:nelem[i])) {
            retval=-1;
            break;
        }
        setPosition(i,0);
    }

    return retval; //All ok!
}

int Matrix::setNumOfElemInDim(int dim,int nelem)
{
    int retval=-1;

    if (dim<numOfDim()) {
        m_numOfElemInDim[dim] = nelem;
        retval=0;
    }

    return retval;
}

int Matrix::setNumOfElemInDims(int el0, ...)
{
    va_list vl;
    va_start(vl,el0);

    setNumOfElemInDim(0,el0);
    for (int i=1;i<numOfDim();i++)
        setNumOfElemInDim(i,va_arg(vl,int));

    va_end(vl);
    return 0;
}

int Matrix::createMatrix(Matrix::value_t *values)
{
    int retval=1;

    if ( (m_values!=NULL || !numOfDim()) && valAreIntAlloc()==true ) {
        return 0;
    }

    if (values!=NULL) {
        m_values=values;
        setValAreIntAlloc(false);
    } else {
        setValAreIntAlloc(true);
        // Compute the number of elements for
        // the whole matrix
        for(int i=0;i<numOfDim();i++) {
            retval*=numOfElemInDim(i);
            if (!retval) {
                //Indicate that a dimension has a 0 value
                //as numOfElemInDim! The caller will be allowed
                //to know the such a dimension using: -retval-1;
                retval=-(i+1);
                break;
            }
        }
        if (retval>0) {
            m_values=new value_t[retval];
            if (m_values!=NULL)
                retval=0;
        }
    }

    //Returns:
    //1 if values is an external allocated memory,
    //0 if nothing has been allocated or the
    //m_values is already set as internal!
    //elsewhere the number of allocated elements.
    return retval;
}

void Matrix::setPositions(int dim0, ...)
{
    va_list vl;
    va_start(vl,dim0);

    setPosition(0,dim0);
    for (int i=1;i<numOfDim();i++)
        setPosition(i,va_arg(vl,int));

    va_end(vl);
}

Matrix::value_t Matrix::getValue(int dim0, ...)
{
    va_list vl;
    va_start(vl,dim0);

    setPosition(0,dim0);
    for (int i=1;i<numOfDim();i++)
        setPosition(i,va_arg(vl,int));

    va_end(vl);
    return getValue();
}

void Matrix::setValue(Matrix::value_t value, int dim0, ...)
{
    va_list vl;
    va_start(vl,dim0);

    setPosition(0,dim0);
    for (int i=1;i<numOfDim();i++)
        setPosition(i,va_arg(vl,int));

    va_end(vl);
    setValue(value);
}

int Matrix::posInValueVector()
{
    int pos=position(0);

    for(int i=1;i<numOfDim();i++)
        pos=pos*numOfElemInDim(i)+position(i);

    return pos;
}

I think the solution below, that is an answer at another my question, is a beautiful solution and implements all what I wanted, but I used a lot of code ... It's based on C++11 variadic templates.

A dubt I've about this code is the way in whose it manages the memory. My will would be the memory is a single block, I'm almost sure this code don't do that. However is a good and fast code!

template<typename T, int ...rest>
struct matrix;

template<typename T, int n>
struct matrix<T, n> {
    T data[n];
    matrix() {
        for (int i=0; i<n; i++) {
            data[i] = T(0);
        }
    }
    T& operator[](int index) { return data[index]; }
};

template<typename T, int n, int ...rest>
struct matrix<T, n, rest...> {
    matrix<T, rest...> data[n];
    matrix<T, rest...>& operator[](int index) { return data[index]; }
};

that can be used with:

matrix<double, 10, 9, 4> m;
for (int i=0; i<10; i++) {
    for (int j=0; j<9; j++) {
        for (int k=0; k<4; k++) {
            m[i][j][k] = i + j*2.718 + k*3.1416;
        }
    }
}

I've found a solution, but I don't fully enjoy it!

I've modified the createMatrix(), getValue() and setValue() methods and inserted two defines the accomplish the function with the ellipsis. The idea was to use a template. Below are the defines that I would like they be templates:

#define __MATRIX_GETVALUE(C,T,val,dim0...) \
    va_list vl; \
    va_start(vl,dim0); \
    C->setPositions(vl,dim0); va_end(vl);\
    val = *((T *)(m_values)+posInValueVector())

#define __MATRIX_SETVALUE(C,T,val,dim0...) \
    va_list vl; \
    va_start(vl,dim0); \
    C->setPositions(vl,dim0); va_end(vl);\
    *((T *)(m_values)+posInValueVector())=val

In the following the modified code:

main.cpp

#include <iostream>
#include <cstdio>

#include "matrix.h"

using namespace std;

int main()
{
    Matrix m(3);
    m.setNumOfCellForDims(4,5,6);

    puts("-----------------> DBL m(i,j,k)=i*100+j*10+k+111 -------------------");
    m.createMatrix(sizeof(double));
    for(int i=0;i<m.numOfCellInDim(0);i++)
        for(int j=0;j<m.numOfCellInDim(1);j++)
            for(int k=0;k<m.numOfCellInDim(2);k++)
                m.setValue((double)i*100+j*10+k+111,i,j,k); // matrix(i,j,k)=i*100+j*10+k

    //printout the values of all matrix(i,j,k) cells
    double valdbl;
    for(int i=0;i<m.numOfCellInDim(0);i++)
        for(int j=0;j<m.numOfCellInDim(1);j++)
            for(int k=0;k<m.numOfCellInDim(2);k++)
                printf("(%d,%d,%d)=%03.0f\n",i,j,k,m.getValue(valdbl,i,j,k));

    puts("-----------------> INT m(i,j,k)=i*100+j*10+k+222 -------------------");
    m.clearMatrix();
    m.setNumOfDim(3);
    m.setNumOfCellForDims(4,5,6);
    m.createMatrix(sizeof(int));

    for(int i=0;i<m.numOfCellInDim(0);i++)
        for(int j=0;j<m.numOfCellInDim(1);j++)
            for(int k=0;k<m.numOfCellInDim(2);k++)
                m.setValue((int)(i*100+j*10+k+222),i,j,k); // matrix(i,j,k)=i*100+j*10+k

    //printout the values of all matrix(i,j,k) cells
    int valint;
    for(int i=0;i<m.numOfCellInDim(0);i++)
        for(int j=0;j<m.numOfCellInDim(1);j++)
            for(int k=0;k<m.numOfCellInDim(2);k++)
                printf("(%d,%d,%d)=%03d\n",i,j,k,m.getValue(valint,i,j,k));
}

matrix.h

#ifndef MATRIX_H
#define MATRIX_H
#include <cstdarg>
#include <cstring>

#define __MATRIX_GETVALUE(C,T,val,dim0...) \
    va_list vl; \
    va_start(vl,dim0); \
    C->setPositions(vl,dim0); va_end(vl);\
    val = *((T *)(m_values)+posInValueVector())

#define __MATRIX_SETVALUE(C,T,val,dim0...) \
    va_list vl; \
    va_start(vl,dim0); \
    C->setPositions(vl,dim0); va_end(vl);\
    *((T *)(m_values)+posInValueVector())=val

class Matrix
{
public:
    Matrix();
    Matrix(int numOfDim, int *ncell=NULL);
    ~Matrix();

    void clearMatrix();

    inline unsigned int numOfDim() const {return m_numOfDim;}

    int setNumOfDim(int numOfDim, int *ncell=NULL);

    inline  int numOfCellInDim(int dim) const
        {return (dim<numOfDim())?m_numOfCellInDim[dim]:-1; }

    int setNumOfCellForDim(int dim, int ncell);
    int setNumOfCellForDims(int el0, ...);

    int createMatrix(int size, void *values);
    inline int createMatrix(int size) { return createMatrix(size,NULL); }

    inline bool valAreIntAlloc() const {return m_valAreIntAlloc;}
    inline void setValAreIntAlloc(bool valAreIntAlloc)
        {m_valAreIntAlloc = valAreIntAlloc;}

    inline int position(int dim) const {return m_positions[dim];}
    inline void setPosition(int dim,int value)
        {m_positions[dim] = value;}

    inline void setPositions(int *positions)
        {for(int i=0;i<numOfDim();i++) setPosition(i,positions[i]);}

    void setPositions(int dim0, ...);

    inline void * getValue() { return (char *)m_values+posInValueVector(); }

    inline double getValue(double &value)
    { return value=*(double *)(m_values)+posInValueVector(); }

    inline int getValue(int &value)
    { return value=*(int *)(m_values)+posInValueVector(); }

    //void * getValue(int dim0, ...);
    inline double getValue(double &value, int dim0, ...) {
        __MATRIX_GETVALUE(this,double,value,dim0);
        return value;
    }

    inline int getValue(int &value, int dim0, ...) {
        __MATRIX_GETVALUE(this,int,value,dim0);
        return value;
    }

    inline void setValue(double value)
        { *((double *)(m_values)+posInValueVector())=value; }
    inline void setValue(int value)
        { *((int *)(m_values)+posInValueVector())=value; }
    inline void setValue(void *value, int size)
        { memcpy((char *)m_values+posInValueVector(),(char *)value,size); }

    //void setValue(double value, int dim0, ...);
    inline void setValue(double value, int dim0, ...) {
        __MATRIX_SETVALUE(this,double,value,dim0);
    }
    inline void setValue(int value, int dim0, ...) {
        __MATRIX_SETVALUE(this,int,value,dim0);
    }

    inline int cellSize() const {return m_cellSize;}
    inline void setCellSize(int cellSize) {m_cellSize = cellSize;}

private:
    int m_numOfDim;
    int m_cellSize;

    int * m_numOfCellInDim;
    int * m_positions;

    void * m_values;
    bool m_valAreIntAlloc;

    int posInValueVector();

#ifdef MATRIX_CPP
    inline
#endif
    int setPositions(va_list &vl, const int &dim0);
};

#endif // MATRIX_H

matrix.cpp

#define MATRIX_CPP
#include <iostream>

#include "matrix.h"

#define __INIT__(v)\
m_numOfDim(v),\
m_cellSize(0), \
m_numOfCellInDim(NULL),\
m_positions(NULL),\
m_values(NULL),\
m_valAreIntAlloc(false)

Matrix::Matrix():
    __INIT__(0)
{
}

Matrix::~Matrix()
{
    clearMatrix();
}

void Matrix::clearMatrix()
{
    if (m_numOfCellInDim!=NULL)
        delete m_numOfCellInDim;
    m_numOfCellInDim=NULL;

    if (m_positions!=NULL)
        delete m_positions;
    m_positions=NULL;

    if (valAreIntAlloc() && m_values!=NULL)
        delete (char *)m_values;
    m_values=NULL;
}

Matrix::Matrix(int numOfDim, int *ncell):
    __INIT__(numOfDim)
{
    setNumOfDim(numOfDim,ncell);
}

int Matrix::setNumOfDim(int numOfDim, int *ncell)
{
    int retval=0;

    m_numOfDim = numOfDim;

    m_numOfCellInDim = new int[numOfDim];
    if (m_numOfCellInDim==NULL)
        return 1;

    m_positions = new int[numOfDim];
    if (m_positions==NULL)
        return 2;

    for(int i=0;i<m_numOfDim;i++) {
        if (setNumOfCellForDim(i,(ncell==NULL)?0:ncell[i])) {
            retval=-1;
            break;
        }
        setPosition(i,0);
    }

    return retval; //All ok!
}

int Matrix::setNumOfCellForDim(int dim,int ncell)
{
    int retval=-1;

    if (dim<numOfDim()) {
        m_numOfCellInDim[dim] = ncell;
        retval=0;
    }

    return retval;
}

int Matrix::setNumOfCellForDims(int el0, ...)
{
    va_list vl;
    va_start(vl,el0);

    setNumOfCellForDim(0,el0);
    for (int i=1;i<numOfDim();i++)
        setNumOfCellForDim(i,va_arg(vl,int));

    va_end(vl);
    return 0;
}

int Matrix::createMatrix(int size, void *values)
{
    int retval=1;

    setCellSize(size);

    if ( (m_values!=NULL || !numOfDim()) && valAreIntAlloc()==true ) {
        return 0;
    }

    if (values!=NULL) {
        m_values=values;
        setValAreIntAlloc(false);
    } else {
        setValAreIntAlloc(true);
        // Compute the number of cellents for
        // the whole matrix
        for(int i=0;i<numOfDim();i++) {
            retval*=numOfCellInDim(i);
            if (!retval) {
                //Indicate that a dimension has a 0 value
                //as numOfCellInDim! The caller will be allowed
                //to know the such a dimension using: -retval-1;
                retval=-(i+1);
                break;
            }
        }
        if (retval>0) {
            m_values=new char[retval*cellSize()];
            if (m_values!=NULL)
                retval=0;
        }
    }

    //Returns:
    //1 if values is an external allocated memory,
    //0 if nothing has been allocated or the
    //m_values is already set as internal!
    //elsewhere the number of allocated cellents.
    return retval;
}

void Matrix::setPositions(int dim0, ...)
{
    va_list vl;
    va_start(vl,dim0);

    setPositions(vl,dim0);
    va_end(vl);
}

int Matrix::setPositions(va_list &vl,const int &dim0)
{
    setPosition(0,dim0);
    for (int i=1;i<numOfDim();i++)
        setPosition(i,va_arg(vl,int));
}

int Matrix::posInValueVector()
{
    int pos=position(0);

    for(int i=1;i<numOfDim();i++)
        pos=pos*numOfCellInDim(i)+position(i);

    return pos;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM