views:

443

answers:

4

Hi,

I currently have a 4x4 matrix class in C++ and I store each value as a float:

Matrix4d::Matrix4d(const float& m00, const float& m01, const float& m02, const float& m03,
                   const float& m10, const float& m11, const float& m12, const float& m13,
                   const float& m20, const float& m21, const float& m22, const float& m23,
                   const float& m30, const float& m31, const float& m32, const float& m33)
{
    _m00 = m00;
    _m01 = m01;
    _m02 = m02;
    _m03 = m03;
    _m10 = m10;
    _m11 = m11;
    _m12 = m12;
    _m13 = m13;
    _m20 = m20;
    _m21 = m21;
    _m22 = m22;
    _m23 = m23;
    _m30 = m30;
    _m31 = m31;
    _m32 = m32;
    _m33 = m33;
}

My question is, how can I return a float array of this data? I have no problem creating the array in the class for example:

float arrayToReturn[16] = { m00, m01, m02, m03, ... m33 };

However I can not return this value from the class. I've read about returning a pointer to the array, but have had no luck with it.

Thanks in advanced. - James

+4  A: 
  1. Don't pass floats by const reference, pass them by value.

  2. I assume you want to return the array so you can do indexing? Then don't return an array from you matrix class. Instead, overload the [] operator or something.

  3. Also, I wouldn't use 16 member variables but one array instead. Makes indexing a lot easier.

Here is how I would probably do it:

class Matrix4d
{
    float matrix[4][4];

public:

    Matrix4d(float m00, float m01, float m02, float m03,
             float m10, float m11, float m12, float m13,
             float m20, float m21, float m22, float m23,
             float m30, float m31, float m32, float m33)
    {
        matrix[0][0] = m00;
        matrix[0][1] = m01;
        matrix[0][2] = m02;
        matrix[0][3] = m03;
        matrix[1][0] = m10;
        matrix[1][1] = m11;
        matrix[1][2] = m12;
        matrix[1][3] = m13;
        matrix[2][0] = m20;
        matrix[2][1] = m21;
        matrix[2][2] = m22;
        matrix[2][3] = m23;
        matrix[3][0] = m30;
        matrix[3][1] = m31;
        matrix[3][2] = m32;
        matrix[3][3] = m33;
    }

    float* operator[](int i)
    {
        return matrix[i];
    }

    const float* operator[](int i) const
    {
        return matrix[i];
    }
};

int main()
{
    Matrix4d test(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 16);
    test[3][2] = 15;
}
FredOverflow
+1, especially for the array suggestion. @james: along these lines, unless you're writing a high performance app (such as for 3-D game or for crypto), consider using the `vector` class. It implements much you'd otherwise have to.
outis
Thanks for the input, I'm creating a 3d game for a mobile device! The only reason I made my own matrix class was so I could return a 1d array and call glMultMatrixf, but also so I could calculate my matrix equations using it. It's running fine now! @Fred Why shouldn't I pass floats by const?
ing0
Because the amount of information necessary to pass a reference to a float is never smaller than a float itself, and the additional level of indirection can hurt performance. Passing a float by const reference does not buy you anything.
FredOverflow
Ok, thanks for that. I was only doing it from what I've been taught at uni!
ing0
I agree 2D arrays are probably a better way of storing it, thanks.
ing0
You should have been taught "pass primitives by value and objects that are too expensive to copy by const reference" ;-)
FredOverflow
+1  A: 

You can use a union to describe your class.

union
{
    struct
    {
       float _m00;
       float _m01;
       ...
    };
    float _m[16];
};

You can then return _m.

also getting cols can be useful so:

union
{
    struct
    {
       float _m00;
       float _m01;
       ...
    };
    float _m[4*4];
    float _cols[4][4];
};
Charles Beattie
+1  A: 

This would work if your internal array looked like float array[4][4]:

float** Matrix4d::getMatrix();

If your internal array was a one-dimensional array:

float* Matrix4d::getMatrix();

But both cases expose the internal workings of your class to the outside world, with makes your code less safe and harder to maintain.

It would be better to create a copy constructor, a () operator, and an assignment operator for your Matrix4d class and just pass that around. You'd be less likely to have runtime errors due to bad memory management or data corruption.

Your () operator would look like this:

float& operator()( unsigned int xIndex, unsigned int yIndex )
{
  //return the right attribute
}

You would call it like this for setting values:

aMatrix(0,0) = 2.0;

or this for retrieving:

float attributeCopy = aMatrix(0,0);

It works both ways.

EDIT: Forgot that the [] operator only took one argument. Changed the operator to the () operator a.k.a the functional operator.

thebretness
That operator doesn't exist in the C++ language.
Jasper Bekkers
No, that doesn't work. `a[x, 0]` will be equivalent to `a[0]` (if `x` has not overloaded its comma operator). I'm also unsure about your `float**` way: It will definitely not work to `return arrayToReturn;` with that function declaration.
Johannes Schaub - litb
You're right. Updated the answer.
thebretness
Yes. The `float**` would work only if your internal array was a 2D array
thebretness
Updated the answer to clarify return values of the getMatrix function.
thebretness
@thebretness, no doing `float x[4][4]; return x;` would definitely *not* work with that function declaration. You need to change the return type to `float(*)[4]`. For example `identity<float[4]>::type *getMatrix();`.
Johannes Schaub - litb
thebretness, your implementation doesn't work well with const matrices. Also, the automatically generated copy constructor and assignment operator do exactly what is required. Defining them manually isn't necessary.
FredOverflow
I understand the objections. However, since the internal type was fungible in this question, I was trying to describe good interfaces without assuming what the members looked like (which is good OO design, anyway). Feel free to edit the question, but any interface that exposes your members is generally deficient.
thebretness
How does your `operator()` not expose the internals? Aren't you returning a reference to a member?
FredOverflow
It's not exposing how the internals are structured or allocated. Often, when you are using matrices, you are doing matrix math, and having properly aligned data is important for good runtime efficiency. That is something the user shouldn't have to be aware of. You could be even more OO, use traditional getters and setters, and hide the underlying data types, but I find the readability tradeoff isn't worth it. It depends on what you want.
thebretness
+1  A: 

Three options I can think of.

The first is return a std::vector rather than an array. The user can always get pointer to the internal array by &v[0].

std::vector<float> Matric4d::getData() { 
  std::vector<float> d(16); 
  d[0]=_m00; 
  ... 
  return d;
}

The second is return a boost::array. (I think there's a tr1::array from the upcoming C++0x standard if your compiler supports something like that) Again its easy to get to the internal array.

The third is the hackiest but may best if you need speed. If you're storing your floats contiguously you could just return a pointer to the first entry. (Note you need to be careful about the details of your class otherwise you end up with "undefined behaviour". However, many/most/all compilers will still "do the right thing" anyway.) Oh and you need to be careful as the pointer will only be valid while the matrix still exists.

float * Matrix4d::getData() { return &_m00; }
Michael Anderson
Indeed. Array indexing is just pointer arithmetic in disguise. Doing pointer arithmetic on a pointer pointing to a single element (as opposed to an array) is undefined behavior.
FredOverflow