I'm writing a general purpose templated library for vector and matrix math. I want to be able to initialise a matrix with a 2D array. A matrix consists of an array of column vectors. A vector can take a pointer to a 1D array of initial values. I've tested that this works. But the similar constructor for the matrix causes a sigsegv. As far as I can see, all the array math is fine. From the stack trace, the value for initArray in Vec<4,float>::Vec is bogus. It should be pointing to the first element of one of the inner arrays of the 2D array passed into Mat<4,4,float>::Mat in main.
Here's a copy of the call stack at the time of the error:
template <unsigned int X, unsigned int Y, typename T>
Mat<X, Y, T>::Mat(const T** initArray) {
for (unsigned int i=0; i<X; ++i)
cols[i] = Vec<Y, T>(initArray[i]);
}
Vector contructor
Code:
template <unsigned int N, typename T>
Vec<N, T>::Vec(const T* initArray) {
for (unsigned int i=0; i<N; ++i)
els[i] = initArray[i];
}
And the interfaces for vectors and matrices
Code:
template <unsigned int N, typename T = float>
class Vec {
public:
Vec(); // Init elements to default value of type T
Vec(const T* initArray); // Init from array of N elements of type T
Vec(const T& initVal); // Init all elements to initVal
Vec(const Vec& copy);
~Vec();
Vec& operator= (const Vec& rhs);
T& operator[] (unsigned int i);
const T& operator[] (unsigned int i) const;
// Cast to any other type of vector (any dimensions, any element type)
template <unsigned int n, typename t>
operator Vec<n, t> () const;
private:
T els[N];
};
Code:
template <unsigned int X, unsigned int Y, typename T = float>
class Mat {
public:
Mat();
Mat(const T** initArray); // Init by array T [X][Y]
Mat(const Vec<Y, T>* initArray); // Init by array of column vectors
Mat(const Mat& copy);
~Mat();
Mat& operator= (const Mat& rhs);
Vec<Y, T>& operator[] (unsigned int);
const Vec<Y, T>& operator[] (unsigned int) const;
private:
// Store elements as X columns of Y elements of T
Vec<Y, T> cols[X];
};
Mat4 m1((const float**)mat_init); // Error here
Mat4 m2((const float**)mat_init);
You've encountered an unintuitive but perfectly reasonable issue: a "2D array of T" is not the same as a T**. The fact that you needed an explicit cast to make the compile work should have been your first clue on this matter.
Basically, with a 2D array, the memory layout doesn't include any room for pointers. It doesn't need to; the dimensions are known at compile time, so the compiler can convert multi-dimensional accesses into 1D accesses at compile time.
With a T**, though, you've explicitly got to have an array of pointers. It's a different underlying assumption.
I would also point out that there are several good vector/matrix math libraries out there already: Boost.uBLAS, Eigen, MTL, etc.
An array of arrays can be converted to a pointer to an array, but a pointer to an array is not a pointer to a pointer. You should cater for this by having a pointer to an array instead of a pointer to a pointer as the parameter.
C + C++ Compiler: MinGW port of GCC
Build + Version Control System: SCons + Bazaar
If you really want to get confused, try and figure out why the compiler will refuse to implicitly convert a T** to a const T**.
Or explicitly as a matter of fact.
Is your question related to IO?
Read this C++ FAQ LITE article at parashift by Marshall Cline. In particular points 1-6.
It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.
Bookmarks