tags:

views:

322

answers:

2

I got a typical 'vector4' class with an operator float* to autocast it for gl*4fv as well as []. There's also 'const' version for optimizations for the compiler as well as const refrences, and this works fine:

typedef struct vec4
{
 ...

 // ----------------------------------------------------------------- //

  // Cast operator, for []
 inline operator float* () { 
  return (float*)this;
 }

  // Const cast operator, for const []
 inline operator const float* () const { 
  return (const float*)this;
 }

 // ----------------------------------------------------------------- //

 ...

  // Vertex / Vector 
 struct {
  float x, y, z, w;
 }; 

   // Color
 struct {
  float r, g, b, a;
 };
} vec4;

My problem is when I now coded a 'matrix4' class, with operator vec4* which supports extracting rows from the matrix, and also have the 'side-effect' of having matrix[][] operator which is nice.

typedef struct mat4
{
 ...

 // ----------------------------------------------------------------- //
   // Cast operator, for []
 inline operator vec4* () { 
  return (vec4*)this;
 }

  // Const cast operator, for const []
 inline operator const vec4* () const { 
  return (const vec4*)this;
 }
 // ----------------------------------------------------------------- //

 private:
  float f[16];
} mat4;

My question is, why doesn't the compiler detect the ability to convert a mat4 to float*? I would suspect that the heritage of mat4 -> vec4 -> float* is reasonable, but it doesn't seem so. It came to my mind that the compiler might see it as mat4 -> vec4* -> float* which is not defined, but that assumption was invalid, since defining the operator

inline operator const vec4 () const { 
 return (vec4)*this;
}

does not work, and calling glMultMatrixf(mat4(...)); (for example) produces the same error message as without the operator.

defining operator float* in mat4 is of course impossible, since that will eliminate the ability to use [][] (ambigious operators)

Any solutions for this? or do I have to manually cast to vec4 everytime I want to autocast to float*? Auto-casting is a really nice feature and it interpolates the code with OpenGL neatly.

+3  A: 

C++ can perform automatic conversions, but by the standard will not perform two consecutive automatic conversions.

It was deemed too conducive to unintentional bugs and ambiguities.

Three options that may work for you:

Explicitly perform the first cast yourself when you want a float* from a matrix.

FuncThatWantsFloatPointer( *static_cast<Vec4*>(MyMatrix) );

Implement a direct converstion to float* in your matrix class.

typedef struct mat4
{
        ...
        operator const float* () const
        { 
                return *static_cast<Vec4*>(*this);
        }

} mat4;

Implement operator[] in your matrix and vector classes, if you like using square bracket notation

typedef struct mat4
{
        ...
        const vec4& operator[] ( size_t index ) const
        { 
                return static_cast<Vec4*>(*this)[index];
        }

} mat4;
Shmoopty
And a workaround for this exists or no?
LiraNuna
You may make the first conversion explicit so only one conversion is implicit. (*(Vec4*)MyMatrix) can convert to float*
Shmoopty
You can also add an operator to your matrix class that converts directly to float* by converting it to a Vec4* first.
Shmoopty
@Shmoopty, both methods will not work. 'explicit' keyword is only for constructors, and adding float* will not allow me to use the fake operator [][] (mat[]vec[]float).
LiraNuna
I was not referring to the keyword. I just clarified in my answer. Hope that helps.
Shmoopty
+1  A: 

... One of those rules is that no sequence of conversions is allowed to contain more than one user-defined conversion (i.e., a call to a single arguement constructor or an implicit type conversion operator). - More Effective C++, Scott Meyers

You might want to overload operator[] for vec4 and mat4.

struct vec4
{
    float& operator[](int index) { return f[index]; }
    const float& operator[](int index) const { return f[index]; }

    operator float*() { return f; }
    operator const float*() const { return f; }

    float f[4];
};

struct mat4 
{
    vec4& operator[](int row) { return v[row]; }
    const vec4& operator[](int row) const { return v[row]; }

    operator float*() { return f; }
    operator const float*() const { return f; }

    union
    {
     vec4 v[4];
     float f[16];
    };
};

int main(void)
{
    mat4 m;
    ::memset(&m, 0, sizeof(mat4));
    m[0][1] = 1;
    cout << m[0][1] << endl; // it prints 1.

    return 0;
}
young
That will gimp down the structure - there's no '[const] float*' conversion at all, which would be nice for gl*4fv-like functions.I tried overloading the operator and that works fine for GCC, but MSVC barfs and errors. While I don't really care about MSVC, I still support portable code.
LiraNuna
I was just tryting to show how to overload operator[]. You can easily add const version of those operations. I tested the code with MSVC 2008 and it was just fine. Not sure about GCC, though.
young
operator const float*() const { return f; } // like this. :)
young
And that will eliminate the double [][]. (yours = mat[15], I want = mat[3][3]);
LiraNuna
Hmm, I don't understand exactly what you mean. You can still do this m[0][1] = 1; which is already in main() function.
young
No, because you cast to float - and float[] is invalid unary.
LiraNuna
LiraNuna