Union Hack



  • Guten Tag,

    jetzt habe ich hier ein dummes Problem. In meiner Geometrie-Lib, hatte ich Rechtecke gewollt, wo ich sowohl direkten Zugriff auf die elementaren Koordinaten bzw. Größen hatte, wie auch auf den Ursprungspunkt und die kombininierte Größe.

    Also so (ohne templates jetzt):

    struct BSize {
       int SX,SY;
    };
    
    struct Size : public BSize
    {
    //...
    };
    
    struct BPoint {
        int X,Y;
    };
    
    struct Point : public BPoint
    {
    //...
    };
    
    struct Rect {
    //...
    
        union {
           struct { int X, int Y };
           BPoint P;
        };
    
        union {
           struct { int SX, int SY };
           BSize S;
        };
    };
    

    So, das funktioniert auch alles toll, bis man auf die Idee kommt, dass man gerne Konstante Punkte und Größen definieren/erzeugen möchte. Dann bräuchte man auf einmal einen Konstruktor in BSize/BPoint und damit sind sie nicht mehr dafür gut, als Element in dem Union zu dienen.

    Fällt euch eine Abhilfe ein? Eigentlich möchte ich nur einfachen Zugriff auf X, Y, SX, SY sowie den zusammengesetzten P und S haben, aber mir ist C++ hier im Weg -.-.

    Viele Grüße,
    Deci



  • Getter und Setter mit Referenzen? X und Y sind ja in BPoint enthalten, SX und SY in BSize. Warum nochmals im union definieren? Den union kannst du gleich weglassen 😉


  • Mod

    Sinn und Zweck eine union ist weder das Sparen von Schreibarbeit noch Hacken. Deine Beschreibung klingt eher so, als sollten Size und Point Zugriffsmöglichkeiten auf die Koordinaten anbieten. Rect benutzt diese dann und stellt gewünschtenfalls diese Funktionen der Außenwelt zur Verfügung.

    P.S.: Der Sinn und Zweck einer Union ist eigentlich Speichersparen. Was heutzutage sicherlich eher selten vorkommen sollte. Der übliche Hack mit unions wird auf den meisten Systemen ebenfalls funktionieren, zumindest mit PODs. Mache (alle?) Compiler garantieren sogar, dass es funktioniert. Ich würde es in C++ aber trotzdem nicht benutzen, denn üblicherweise schadest du dir selber, wenn du das Typensystem aushebelst.



  • Vielleicht, aber auch nur vielleicht kannst du eine Methode anbieten die da was castet:

    struct Rect {
    	struct { int X, int Y };
    	BPoint getP() { return *reinterpret_cast<BPoint*>(&X);}
    
    	struct { int SX, int SY };
    	BSize getP() { return *reinterpret_cast<BSize*>(&SX);}
    };
    

    So in der Richtung. Ich meine, Membervariablen liegen direkt hintereinander im Speicher. Das heisst, wenn du per "Array" auf X zugreifst, dann wäre X[0] X selbst und X[1] wäre Y.

    Valve macht das so bei einer Klasse für Farben in der Source Engine.
    Etwa so, falls ich das richtig im Kopf habe

    class Colo
    {
    	byte r, g, b, a;
    
    	int getColor()
    	{
    		return *(reinterpret_cast<int*>(&r));
    	}
    };
    

    Die Vor-/Nachteile und Stolperfallen kannst du dir ja denken.



  • Naja, ich möchte halt schreiben können:

    Point p1 = r.O;
       Point p2 /* = irgendwas */;
    
       r.S = p2 - p1;
       r.S() = p2 - p1; // wie hässlich ist das denn bitte?
    

    Jetzt könnte ich natürlich Getter und Setter dahinfriemeln, die eben ein Size<TX&,TY&>-Proxy zurückgeben (das dann bitte bitte direkt vom Compiler wieder geschluckt wird), aber dann schreibe ich im User-Code überall Klammern, wo ich in den Ausdrücken keine Klammern sehen will, weil sie mir die mathematischen Formeln zumüllen. Hätte C++ Properties, wäre das ja alles kein Problem -.-.


  • Mod

    Mach S public, gib dem Typen von S passende Zuweisungsoperatoren.



  • Meinst du ein solches S, wie ich es in der letzten Zeile meines letzten Beispiels aufgezeigt habe? Also ein Getter?


  • Mod

    Ich bin mir nicht sicher, was du mir mit dem Beispiel zeigen wolltest. Wenn du willst, dass man r.S = p2 - p1; schreiben kann, dann mach S ein public-Member. Ich sehe nicht, wozu du da überhaupt eine union brauchst. Wenn man zusätzlich noch r.S.x = 2; schreiben können soll, dann mach eben das x auch public. Kurz: Wenn du willst, dass man an den Innereien deiner Klasse rumfummeln kann, dann mach sie eben auch public und fang nicht an, deine eigenen Klassen zu hacken.



  • Nunja, dann würde ich überall, wo ich den elementaren Zugriff brauche, schreiben müssen:

    r.S.SX = foo;
    

    Da mag ich den "tradeoff"

    r.S() = foo;
       r.SX  = bar;
    

    lieber.
    Irgendwie wäre ein Konstrukt wie

    struct Bla {
      struct { int a; } Blubb;
      using b = Blubb.a;
    };
    

    toll 😉


Anmelden zum Antworten