tags:

views:

589

answers:

2

I have 2 matrix structs means equal data but have different form like these:

// Matrix type 1.
typedef float Scalar;
typedef struct { Scalar e[4]; } Vector;
typedef struct { Vector e[4]; } Matrix;

// Matrix type 2 (you may know this if you're iPhone developer)
// Defines CGFloat as float for simple description.
typedef float CGFloat;
struct CATransform3D
   {
   CGFloat m11, m12, m13, m14;
   CGFloat m21, m22, m23, m24;
   CGFloat m31, m32, m33, m34;
   CGFloat m41, m42, m43, m44;
};
typedef struct CATransform3D CATransform3D;

Their memory sizes are equal. So I believe there is a way to convert these types without any pointer operations or copy like this:

// Implemented in external lib.
CATransform3D CATransform3DMakeScale (CGFloat sx, CGFloat sy, CGFloat sz);
Matrix m = (Matrix)CATransform3DMakeScale ( 1, 2, 3 );

Is this possible? Currently compiler prints an "error: conversion to non-scalar type requested" message.

+10  A: 

Probably the best solution would be to combine your two structs into a union. If you want to interpret the same data in two different ways then it's the obvious choice. The alternative is to use pointer casts, but that's ugly, breaks aliasing rules, and can hide errors that might otherwise be reported by the compiler.

typedef float Scalar;
typedef struct { Scalar e[4]; } Vector;
typedef struct { Vector e[4]; } Matrix;

struct CATransform3D
{
   CGFloat m11, m12, m13, m14;
   CGFloat m21, m22, m23, m24;
   CGFloat m31, m32, m33, m34;
   CGFloat m41, m42, m43, m44;
};
typedef struct CATransform3D CATransform3D;

typedef union
{
    CATransform3D t;
    Matrix m;
} UMatrix;

CATransform3D CATransform3DMakeScale (CGFloat sx, CGFloat sy, CGFloat sz);
UMatrix um;
um.t = CATransform3DMakeScale ( 1, 2, 3 );
//
// now you can just use um.m when you need to refer to the Matrix type...
//
Paul R
Brilliant. But in my opinion, it requires a kind of proxy variable. Doesn't it cause some copying?
Eonil
No - just define a union as a typedef - see above example.
Paul R
The union is the best idea. Most type-safe, simplest conversion, no overhead.
DeadMG
Thanks very much. I resolve my problem with this solution.
Eonil
+1  A: 

Well, you could just declare CATransform3DMakeScale like this:

Matrix CATransform3DMakeScale (CGFloat sx, CGFloat sy, CGFloat sz);

C doesn't type-check to make sure your deceleration matches the original library. If the memory layout is the same, it will work. However, it's bad coding practice and you should include a lengthy comment justifying your actions. ;-)

Otherwise, there's no way around it: you have to use pointers or copy the data. This would work:

CATransform3D m3d = CATransform3DMakeScale ( 1, 2, 3 );
Matrix m;
memcpy(&m, &m3d, sizeof m);

As you've discovered, you can't cast the structure directly. You also can't do this:

Matrix m = *(Matrix*) &CATransform3DMakeScale ( 1, 2, 3 );

because C only allows you to use the & operator on an l-value.

Daniel Stutzbach
This is `type punning` via pointer casts - it breaks aliasing rules and should generate a warning with any decent compiler. See: http://en.wikipedia.org/wiki/Type_punning. Note that the union trick is also strictly UB but is generally considered safer than the pointer cast method.
Paul R
@Paul R: Good point. I've updated my answer.
Daniel Stutzbach