Frage zu "besonderem" "Hack"
-
Hi,
habe eben beim durchstöbern der VALVE Source Engine SDK folgendes gefunden: http://pastebin.com/KfQjK4vAWie kann das SetRawColor und GetRawColor denn bitte funktionieren? Sowas habe ich noch nie gesehen... Eine Erklärung würde mich interessieren.
Gruß
-
Es wird davon ausgegangen, das im Speicherlayout des C++ Objekts, der Member _color an erster Stelle steht und via this direkt erreicht werden kann.
Diese Variante birgt einige Probleme, z.B. mit virtuellen Funktionen.
Generell ist halt das Problem, dass die Implementierungsdetails vorausgesetzt werden.Simon
-
und was ist der sinn von:
#ifndef COLOR_H #define COLOR_H #ifdef _WIN32 #pragma once #endif?
-
Das pragma once ist überflüssig (ifndef reicht) und ist der MS Weg für Include Guards.
-
asdfasdfasdfasdf schrieb:
und was ist der sinn von:
#ifndef COLOR_H #define COLOR_H #ifdef _WIN32 #pragma once #endif?
Manche Compiler haben bei pragma once auch noch spezielle Features, dass die Datei gar kein zweites Mal angefasst wird, was die Compilierungsgeschwindigkeit erhöht. Gute Compiler machen dies auch bei normalen Includeguards, die sie entsprechend erkennen.
-
SeppJ schrieb:
Gute Compiler machen dies auch bei normalen Includeguards, die sie entsprechend erkennen.
Wie kann man die erkennen, ohne die ganze Datei zu parsen? Ein #ifndef-#define am Anfang muss ja nicht zwingend ein Headerguard sein...
-
Danke für die Antworten! M.M.n. eine ziemlich "riskante" Sache. Wo wäre der Unterschied, wenn man einfach _color[0] schreiben würde? Es gäbe doch eigentlich keinen oder?!
Gruß
-
Kompiliererer schrieb:
SeppJ schrieb:
Gute Compiler machen dies auch bei normalen Includeguards, die sie entsprechend erkennen.
Wie kann man die erkennen, ohne die ganze Datei zu parsen? Ein #ifndef-#define am Anfang muss ja nicht zwingend ein Headerguard sein...
Beim ersten Mal muss man ohnehin die ganze Datei einlesen.
-
IMHo wäre es wirklich etwas sicherer
return *((int *)_color);zu schreiben. Aber sicher ist noch gar nichts.
Es ist nie gesagt, dass ein char genau 255 Werte annehmen kann. Besser (und genauso performant) ist daher, ein Bitfield zu verwenden.unsigned int r : 8; unsigned int g : 8; unsigned int b : 8; unsigned int a : 8;Leider sagt immer noch niemand, dass das hintereinander liegen muss, wenn du sicher sein willst, dass es klappt, musst du in deinem autoconf-Script (oder was du auch immer verwendest) zur Compile-Time testen, ob vier Bytes in einen Integer passen und musst die einzelnen Werte per Bitshifting setzen. Tönt zwar inperformant, aber genau das macht der Compiler intern.
Jetzt ist die Frage, ob das etwas bringt und wenn du sehr oft auf die r,g,b,a-Werte zugreifst, bringt es effektiv nichts, die Umrechnung zu vereinfachen und zu verschnellern, wenn noch mehr Zeit beim Zugriff auf die Werte verlierst.
Fazit: Unperformanter Hack, unportabler Hack, schlechter Hack. Eigentlich hat das nicht einmal die Bezeichnung Hack verdient, da der einzige Zweck eine Erleichterung der Lesbarkeit darstellt und Hacks eher andersrum funktionieren.
-
Man hätte hier auch einfach ne Union nehmen können.
Oder einen int/uint32_t Wert mit Rumshiften erzeugen/zerlegen.
Das Geshifte optimiert der Compiler im Endeffekt sowieso weg, von daher sollte das auch gleich performant sein.
Das wäre vermutlich die sauberste Variante.
-
Das ist der Versuch alle 4 Bytes auf einmal zu setzen/lesen. ((int)this) ist äquivalent zu reinterpret_cast<int&>(*this). Der Der this-Zeiger wird einfach als int-Zeiger behandelt. Streng genommen ist das aus drei Gründen nicht erlaubt:
1. Color ist kein POD
2. Verletzung von §3.10/15 (Strinct-Aliasing)
3. Folgendes kann nicht garantiert werden: alignof(Color)>=alignof(int)Richtig wär's mit memcpy und memset gewesen:
void SetRawColor(unsigned color32) { memcpy(_color,&color32,4); } unsingned GetRawColor() const { unsigned r; memcpy(&r,_color,4); return r; }oder alternativ das Speichern als int:
public: void SetRawColor(uint32_t color32) { color32_ = color32; } uint32_t GetRawColor() const { return color32_; } unsigned char& operator[](int idx) { // ist nach §3.10/15 erlaubt unsigned char* p = reinterpret_cast<unsigned char*>(&color32_); return p[idx]; } unsigned char operator[](int idx) const { // ist nach §3.10/15 erlaubt unsigned char* p = reinterpret_cast<unsigned char*>(&color32_); return p[idx]; } private: uint32_t color32_;u.s.w.
-
hustbaer schrieb:
Man hätte hier auch einfach ne Union nehmen können.
Das ist nicht erlaubt, weil immer nur auf den letzten geschriebenen Wert lesend zugegriffen werden darf.
-
Michael E. schrieb:
hustbaer schrieb:
Man hätte hier auch einfach ne Union nehmen können.
Das ist nicht erlaubt, weil immer nur auf den letzten geschriebenen Wert lesend zugegriffen werden darf.
Der berühmte Union Trick...
-
union { uint32_t color32; uint8_t color8[4]; }Weshalb ist das hier denn nun genau so "verboten"?
Warum darf nur auf den zuletzt geschriebenen Wert zugegriffen werden?
Weil du es sagst? Oder wurde das in der EULA der source sdk festgelegt. Oder hat das etwa religiöse Hintergründe? O_o
Oder gibt es da einen c++ basierenden Grund? Eventuell weil man nicht sicher sagen kann ob der Compiler die 8-bit Typen auf 32bit (oder 64) aligned?Es schaut mir jedenfalls alles besser aus als das Original im ersten Post.
-
Osbios schrieb:
Oder gibt es da einen c++ basierenden Grund?
Ja. Der C++ Standard garantiert nicht, dass das so funktioniert, wie Du Dir das denkst.
-
krümelkacker schrieb:
Osbios schrieb:
Oder gibt es da einen c++ basierenden Grund?
Ja. Der C++ Standard garantiert nicht, dass das so funktioniert, wie Du Dir das denkst.
Ok, wieder etwas dazu gelernt.
-
krümelkacker schrieb:
Osbios schrieb:
Oder gibt es da einen c++ basierenden Grund?
Ja. Der C++ Standard garantiert nicht, dass das so funktioniert, wie Du Dir das denkst.
Der ein oder andere Compiler kann natürlich diese Garantie geben. So ist es zB beim GCC. Der GCC erlaubt den union-hack explizit. Er ist aber wie gesagt nicht Standard.
-
Michael E. schrieb:
hustbaer schrieb:
Man hätte hier auch einfach ne Union nehmen können.
Das ist nicht erlaubt, weil immer nur auf den letzten geschriebenen Wert lesend zugegriffen werden darf.
Ja, ist nicht erlaubt. Funktioniert aber genau so gut oder schlecht wie einfach "this" zu casten, und ist IMO zumindest weniger verwirrend.