2010-05-02 2 views
10

I 2 Matrix structs haben bedeutet gleiche Daten haben aber unterschiedliche Form, wie diese:Wie C Cast nur einen anderen Strukturtyp, wenn ihre Speichergröße gleich sind?

// 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; 

Die Speichergrößen gleich sind. Also ich glaube, dass es einen Weg gibt, diese Art ohne Zeigeroperationen oder kopieren, so zu konvertieren:

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

Ist das möglich? Zur Zeit gibt der Compiler die Meldung "Fehler: Konvertierung in nicht skalaren Typ angefordert" aus.

Antwort

16

Wahrscheinlich die beste Lösung wäre, Ihre beiden Strukturen zu einer Union zu kombinieren. Wenn Sie die gleichen Daten auf zwei verschiedene Arten interpretieren möchten, dann ist dies die offensichtliche Wahl. Die Alternative besteht darin, Pointer-Umwandlungen zu verwenden, aber das ist hässlich, bricht Aliasing-Regeln auf und kann Fehler verbergen, die sonst vom Compiler gemeldet würden.

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... 
// 
+1

Brilliant. Meiner Meinung nach erfordert dies eine Art Proxy-Variable. Führt es nicht zu etwas Kopieren? – Eonil

+0

Nein - definieren Sie einfach eine Union als typedef - siehe oben Beispiel. –

+1

Die Union ist die beste Idee. Die meisten Typ-Safe, einfachste Konvertierung, kein Overhead. – Puppy

1

Nun, könnten Sie einfach CATransform3DMakeScale wie folgt erklären:

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

C nicht geben, um sich Ihrer Verzögerung sicherstellen, dass die Original-Bibliothek übereinstimmt. Wenn das Speicherlayout identisch ist, funktioniert es. Es ist jedoch eine schlechte Programmierpraxis und Sie sollten einen längeren Kommentar hinzufügen, der Ihre Aktionen rechtfertigt. ;-)

Ansonsten gibt es keine Möglichkeit: Sie müssen Zeiger verwenden oder die Daten kopieren. Das würde funktionieren:

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

Wie Sie festgestellt haben, können Sie die Struktur nicht direkt übertragen. Sie können dies auch nicht tun:

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

weil C können Sie nur den & Operator auf einem L-Wert verwenden.

+1

Dies ist "Typ Punning" über Zeiger-Casts - es bricht Alias-Regeln und sollte eine Warnung mit jedem vernünftigen Compiler generieren. Siehe: http://en.wikipedia.org/wiki/Type_punning. Beachten Sie, dass der Union-Trick auch streng UB ist, aber im Allgemeinen als sicherer als die Pointer-Cast-Methode gilt. –

+1

@Paul R: Guter Punkt. Ich habe meine Antwort aktualisiert. –

Verwandte Themen