Code sowohl für 32bit als auch 64bit schreiben
Code sowohl für 32bit als auch 64bit schreiben
Wie schreibe ich am besten Code, der kompiliert auf 32bit Systemen für meine integer Variablen automatisch 4 Byte nutzt und kompiliert auf 64bit Systemen automatisch 8 Byte.
Ich nutze zum Beispiel in einer dll (ohne Qt) size_t für unsigned integer und ptrdiff_t für signed integer. Es sind entweder 32 oder 64bit Typen, je nach Plattform. Auch die Schnittstelle, die die dll exportiert verwendet diese Typen. Ist dies sinnvoll?
Wenn ja, müsste ich dann nicht in meiner Client Anwendung für die dll (mit Qt) statt int besser quintptr und qptrdiff verwenden?
Müsste ich nicht besser grundsätzlich quintptr und qptrdiff verwernden statt unsigned int und int? In ein paar Jahren steht man wieder vor dem Problem für 96bit oder 128bit Systeme.
Warum liefert ein QVector aber beispielweise für size() oder count() den Rückgabetyp int und nicht quintptr? Anderes Beispiel wäre die Funktion at(), die einen int erwartet.
Wenn ich mir dir STL anschaue, dann verwendet dort vector<T> die Typen size_type, welche sich der Plattform "anpassen"
Ich bin verwirrt.
Ich nutze zum Beispiel in einer dll (ohne Qt) size_t für unsigned integer und ptrdiff_t für signed integer. Es sind entweder 32 oder 64bit Typen, je nach Plattform. Auch die Schnittstelle, die die dll exportiert verwendet diese Typen. Ist dies sinnvoll?
Wenn ja, müsste ich dann nicht in meiner Client Anwendung für die dll (mit Qt) statt int besser quintptr und qptrdiff verwenden?
Müsste ich nicht besser grundsätzlich quintptr und qptrdiff verwernden statt unsigned int und int? In ein paar Jahren steht man wieder vor dem Problem für 96bit oder 128bit Systeme.
Warum liefert ein QVector aber beispielweise für size() oder count() den Rückgabetyp int und nicht quintptr? Anderes Beispiel wäre die Funktion at(), die einen int erwartet.
Wenn ich mir dir STL anschaue, dann verwendet dort vector<T> die Typen size_type, welche sich der Plattform "anpassen"
Ich bin verwirrt.
-
Christian81
- Beiträge: 7319
- Registriert: 26. August 2004 14:11
- Wohnort: Bremen
- Kontaktdaten:
quintptr ist wie der Name schon sagt ein Pointer auf einen Integer. Was willst Du damit anfangen? Warum sollte QVector einen Pointer auf einen Integer für size() zurückliefern?
Und size_t wird nicht verwendet weil sie einen Signed-Value haben wollten um mit -1 einen Fehler anzuzeigen.
Und Du solltest natürlich die gleichen Typen in deiner Anwendung verwenden wie in der DLL - zumindest wenn es möglich ist was bei Fremdanbietern nicht immer der Fall ist.
Und size_t wird nicht verwendet weil sie einen Signed-Value haben wollten um mit -1 einen Fehler anzuzeigen.
Und Du solltest natürlich die gleichen Typen in deiner Anwendung verwenden wie in der DLL - zumindest wenn es möglich ist was bei Fremdanbietern nicht immer der Fall ist.
MfG Christian
'Funktioniert nicht' ist keine Fehlerbeschreibung
'Funktioniert nicht' ist keine Fehlerbeschreibung
Das stimmt so nicht. Ein Auszug aus dem QtAssistant:
Angestossen wurde ich durch diesen Artikel:
http://www.codesynthesis.com/~boris/blo ... safe-code/
Die STL verwendet durchgängig size_type statt int. size_type ist ein unsigned Typ und 4 Byte groß auf 32bit Systemen und 8 Byte groß auf 64bit Systemen. Nehmen wir an, es gibt folgende Funktion:
Jetzt nehmen wir an, ein Qt-Programm ruft diese Funktion auf, was wäre wirklich korrekt, wenn man berücksichtigen müsste, dass alles sowohl auf 32bit als auch auf 64bit Systemen kompiliert werden soll.
Dies hier:
oder dies hier:
Letztendlich verbirgt sich mit quint32 und quint64 dahinter nichts anderes als das Verhalten, für einen integer-Typ 4 Byte auf einem 32bit System und 8 Byte auf einem 64bit System zu verwenden.On a system with 32-bit pointers, quintptr is a typedef for quint32; on a system with 64-bit pointers, quintptr is a typedef for quint64.
Note that qptrdiff is signed. Use quintptr for unsigned values
Angestossen wurde ich durch diesen Artikel:
http://www.codesynthesis.com/~boris/blo ... safe-code/
Die STL verwendet durchgängig size_type statt int. size_type ist ein unsigned Typ und 4 Byte groß auf 32bit Systemen und 8 Byte groß auf 64bit Systemen. Nehmen wir an, es gibt folgende Funktion:
Code: Alles auswählen
std::size_t testKlasse::testFunktion()
{
// testVector ist vom Type std::vector<double>
// Rückgabetyp von size() ist size_type, welcher
// 4 byte auf einem 32bit System und 8 byte auf einem
// 64bit System belegt
return testVector.size();
}Dies hier:
Code: Alles auswählen
// testObjekt ist eine Instanz von testKlasse
unsigned int size = testObjekt.testFunktion();
// müsste eigentlich auf einem 64bit System scheitern, wenn der vector mehr Elemente enthält als mit unsigned int abgebildet werden können
Code: Alles auswählen
// testObjekt ist eine Instanz von testKlasse
quintptr size = testObjekt.testFunktion();
// müsste immer funktionieren, unabhängig von x86, x86_64, x86_128(?)
Und was bedeutet das für beispielsweise QVector?
Die size(), count(), at(), ... Funktionen arbeiten alle mit int. Wenn ich das richtig verstanden habe bleibt aber int auf einem 64bit System ein 32bit Wert. Die Qt-Klassen werden also nur 32bit Werte zurückgeben und erwarten auf einem 64bit System? Kann ich mir irgendwie nicht vorstellen.
Bei der STL gibt es dieses Problem nicht, da sie nirgends auf int setzt, sondern auf einen typedef, der sich entsprechend der Plattform anpasst.
Die size(), count(), at(), ... Funktionen arbeiten alle mit int. Wenn ich das richtig verstanden habe bleibt aber int auf einem 64bit System ein 32bit Wert. Die Qt-Klassen werden also nur 32bit Werte zurückgeben und erwarten auf einem 64bit System? Kann ich mir irgendwie nicht vorstellen.
Bei der STL gibt es dieses Problem nicht, da sie nirgends auf int setzt, sondern auf einen typedef, der sich entsprechend der Plattform anpasst.
-
Christian81
- Beiträge: 7319
- Registriert: 26. August 2004 14:11
- Wohnort: Bremen
- Kontaktdaten:
Ich kapiere die Fragen nicht. Wenn testObjekt.testFunktion() einen Typ zurück gibst sollte man mit diesem Typ auch weiterarbeiten. Aber ich würde die Funktion so abändern dass sie entweder einen int32 oder int64 zurück gibt und nicht quintptr. Einen Vorteil davon gibts nämlich nicht.
Warum darf ein QVector nicht einfach nur Werte bis 2^31-1 erwarten? Willst Du mehr Werte darin speichern??
Warum darf ein QVector nicht einfach nur Werte bis 2^31-1 erwarten? Willst Du mehr Werte darin speichern??
MfG Christian
'Funktioniert nicht' ist keine Fehlerbeschreibung
'Funktioniert nicht' ist keine Fehlerbeschreibung
Hmm, dann versuch ich mal anders herum zu fragen:
1. Warum verwendet die STL für Indextypen, Größentypen, usw. nicht unsigned int? Und warum hat sich Qt dafür auf einen Typ festgelegt, der auf allen Plattformen immer 32bit besitzt. (Und jetzt möchte ich eigentlich nicht so ein Argument hören wie "2^31 - 1" muss reichen. Das trifft vielleicht heute zu...)
2. Warum muss ich mich zwischen quint32 und quin64 entscheiden? Ist ein quint64 auf einem 32 bit System nicht ineffizient, vor allem dann, wenn der Wertebereich gar nicht benötigt wird? Ist ein quint32 dagegen auf einem 64bit System nicht ebenfalls ineffizient?
Weil:
Nimm als Beispiel eine olle for-Schleife, die gerade mal 10 Iterationen durchläuft. Wohl praktisch jeder wird den Schleifenzähler als int deklarieren, obwohl unsigned char vollkommen ausreichen würde. "Man nimmt halt den Typ, der für den Prozessor zugeschnitten ist" und das sind nunmal 32bit Typen. Auf einem 64bit System würde dass dann eigentlich __int64 (unter Windows) sein. Aber eigentlich will ich mich weder umgewöhnen, noch meine Quelltexte anpassen, da finde ich die Lösung der STL mit size_t ziemlich gut, das Ding wächst mit der Architektur einfach mit, mal platt gesagt.
Oder bin ich irgendwie auf dem Holzweg? Dann klär mich auf.
1. Warum verwendet die STL für Indextypen, Größentypen, usw. nicht unsigned int? Und warum hat sich Qt dafür auf einen Typ festgelegt, der auf allen Plattformen immer 32bit besitzt. (Und jetzt möchte ich eigentlich nicht so ein Argument hören wie "2^31 - 1" muss reichen. Das trifft vielleicht heute zu...)
2. Warum muss ich mich zwischen quint32 und quin64 entscheiden? Ist ein quint64 auf einem 32 bit System nicht ineffizient, vor allem dann, wenn der Wertebereich gar nicht benötigt wird? Ist ein quint32 dagegen auf einem 64bit System nicht ebenfalls ineffizient?
Weil:
3. Was spricht gegen einen Typ, der mit den Prozessorregistern mitwächst? Pointer tun dies unweigerlich auch. Ausgenommen natürlich all die Situationen, wo ich explizit bestimmte Typen erwarte/benötige.(64-bit integer capability: All general-purpose registers (GPRs) are expanded from 32 bits to 64 bits, and all arithmetic and logical operations, memory-to-register and register-to-memory operations, etc. can now operate directly on 64-bit integers. Pushes and pops on the stack are always in 8-byte strides, and pointers are 8 bytes wide.)
Nimm als Beispiel eine olle for-Schleife, die gerade mal 10 Iterationen durchläuft. Wohl praktisch jeder wird den Schleifenzähler als int deklarieren, obwohl unsigned char vollkommen ausreichen würde. "Man nimmt halt den Typ, der für den Prozessor zugeschnitten ist" und das sind nunmal 32bit Typen. Auf einem 64bit System würde dass dann eigentlich __int64 (unter Windows) sein. Aber eigentlich will ich mich weder umgewöhnen, noch meine Quelltexte anpassen, da finde ich die Lösung der STL mit size_t ziemlich gut, das Ding wächst mit der Architektur einfach mit, mal platt gesagt.
Oder bin ich irgendwie auf dem Holzweg? Dann klär mich auf.
Zuletzt geändert von René am 3. September 2009 15:56, insgesamt 1-mal geändert.
Die Funktion gibt kein quintptr zurück sondern ein std::size_t, das ist nunmal der Rückgabetyp eines STL vectors für die Funktion size(). Und der ist nunmal unterschiedlich groß, je nach Plattform.Christian81 hat geschrieben:...Aber ich würde die Funktion so abändern dass sie entweder einen int32 oder int64 zurück gibt und nicht quintptr.
Also wie soll das deiner Meinung nach praktisch aussehen, dass die Funktion immer den gleichen Typ zurückgibt, wenn es aber vector<T>::size() nicht tut? Soll ich da immer und grundsätzlich mit einem static_cast anrücken um daraus beispielsweise einen 4 Byte Typ zu generieren? Das kann's doch nicht sein.
quintptr wäre auf Qt-Seite insofern eine Lösung, da dieser äquivalent zu std:size_t mitwächst.
-
Christian81
- Beiträge: 7319
- Registriert: 26. August 2004 14:11
- Wohnort: Bremen
- Kontaktdaten:
Was dazu führt, dass man über kurz oder lang nahezu immer size_t für unsigned int einsetzt, da man immer wieder auf solche Typen stossen wird bei der Verwendung der STL.
Jetzt bleibt noch die Frage, wie man signed Typen handhabt, die mitwachsen sollen/müssen. Dafür gibt es ptrdiff_t in der stdlib.h. Auch Qt bietet dafür wieder etwas Äquivalentes an, nämlich qptrdiff.
Fazit:
Wenn man eine DLL aufbaut, die intensiv Gebrauch von der STL macht, wird man unweigerlich auf diese "dynamischen" Typen setzen, da sie einem immer wieder begegnen und man nicht ständig casten möchte, was oft genug eine Fehlerquelle ist.
Wenn man nun ein Client-Programm zu dieser DLL schreibt, unter der Verwendung von Qt, dann wird man auch dort unweigerlich auf die entsprechenden Qt-Typen quintptr und qptrdiff setzen.
Was man damit bastelt ist Software, die sich sicher unter 32bit und 64bit kompilieren lassen wird.
Ich mein, mal ehrlich, wie oft hat man schon etwas wie
gesehen. Das gibt eine Compilerwarnung unter 64bit, wegen möglichen Datenverlustes.
Meine ursprüngliche Frage war eigentlich, sollte man nicht grundsätzlich lieber auf diese "dynamischen" Typen setzen und fixe Typen wie char, short und int nur wirklich dann einsezten wenn man eine explizite Byte-Größe benötigt. Mir scheint es fast so. Wird Qt immer noch für size(), at(), usw den 32bit Typ int verwenden, wenn wir irgendwann mal 128bit CPU's haben? Wird Qt vielleicht auch irgendwann mal auf sowas wie size_t umsteigen?
Jetzt bleibt noch die Frage, wie man signed Typen handhabt, die mitwachsen sollen/müssen. Dafür gibt es ptrdiff_t in der stdlib.h. Auch Qt bietet dafür wieder etwas Äquivalentes an, nämlich qptrdiff.
Fazit:
Wenn man eine DLL aufbaut, die intensiv Gebrauch von der STL macht, wird man unweigerlich auf diese "dynamischen" Typen setzen, da sie einem immer wieder begegnen und man nicht ständig casten möchte, was oft genug eine Fehlerquelle ist.
Wenn man nun ein Client-Programm zu dieser DLL schreibt, unter der Verwendung von Qt, dann wird man auch dort unweigerlich auf die entsprechenden Qt-Typen quintptr und qptrdiff setzen.
Was man damit bastelt ist Software, die sich sicher unter 32bit und 64bit kompilieren lassen wird.
Ich mein, mal ehrlich, wie oft hat man schon etwas wie
Code: Alles auswählen
const int count = std::vector<T>::size()Meine ursprüngliche Frage war eigentlich, sollte man nicht grundsätzlich lieber auf diese "dynamischen" Typen setzen und fixe Typen wie char, short und int nur wirklich dann einsezten wenn man eine explizite Byte-Größe benötigt. Mir scheint es fast so. Wird Qt immer noch für size(), at(), usw den 32bit Typ int verwenden, wenn wir irgendwann mal 128bit CPU's haben? Wird Qt vielleicht auch irgendwann mal auf sowas wie size_t umsteigen?
-
Christian81
- Beiträge: 7319
- Registriert: 26. August 2004 14:11
- Wohnort: Bremen
- Kontaktdaten:
Die dynamischen erzeugen nur Ärger und bringen nichts wie Du ja selbst festgestellt hast.
Aber das andere verstehe ich nicht - nur weil es 64Bit gibt - warum muss ich dann auch überall die 64Bit-Datentypen verwenden wenn es doch keinen Vorteil bietet? Nur weil sie eben da sind?
Aber das andere verstehe ich nicht - nur weil es 64Bit gibt - warum muss ich dann auch überall die 64Bit-Datentypen verwenden wenn es doch keinen Vorteil bietet? Nur weil sie eben da sind?
MfG Christian
'Funktioniert nicht' ist keine Fehlerbeschreibung
'Funktioniert nicht' ist keine Fehlerbeschreibung
Hmm, ich sehe es eigentlich genau anderes herum. Die fixen Typen von Qt werden eher lästiger als die dynamischen der STL. Ich verweise nochmals auf den Link http://www.codesynthesis.com/~boris/blo ... safe-code/
Im Grunde genommen wird es eigentlich erst problematisch wenn man beides vermischt.
Nerviges Beispiel:
Von einer auf der STL basierenden DLL erhalte ich bei einer Funktion als Rückgabetyp size_t. Ich möchte diesen numerischen Wert gerne mit einem QLabel anzeigen.
Doch dies führt zu einem Compilerfehler (Visual Studio Output):
Warum? Weil setNum() nur int oder double erwartet. Stattdessen muss ich mir da jetzt helfen mit
Was spricht gegen ein QLabel::setNum(const size_t value)? Oder mit Qt-Typen ausgedrückt:
QLabel::setNum(const quintptr value) für unsigned (entspricht size_t)
QLabel::setNum(const qptrdiff value) für signed (entspricht ptrdiff_t)
Im Grunde genommen wird es eigentlich erst problematisch wenn man beides vermischt.
Nerviges Beispiel:
Von einer auf der STL basierenden DLL erhalte ich bei einer Funktion als Rückgabetyp size_t. Ich möchte diesen numerischen Wert gerne mit einem QLabel anzeigen.
Code: Alles auswählen
const size_t result = [Funktion mit Rückgabetyp size_t]
QLabel label;
label.setNum(result);Code: Alles auswählen
Error 1 error C2668: 'QLabel::setNum' : ambiguous call to overloaded functionCode: Alles auswählen
const size_t result = [Funktion mit Rückgabetyp size_t]
QLabel label;
label.setText(QString::number(result));QLabel::setNum(const quintptr value) für unsigned (entspricht size_t)
QLabel::setNum(const qptrdiff value) für signed (entspricht ptrdiff_t)
-
Christian81
- Beiträge: 7319
- Registriert: 26. August 2004 14:11
- Wohnort: Bremen
- Kontaktdaten:
Hmm, nochmal anders herum.
Sagen wir mal, ich biete eine DLL an, die ausschließlich auf den Datentyp int setzt für Funktionen mit index-, size-, offset-, usw Parameter. Praktisch so, wie Qt es macht.
Ich kann nicht wissen, wie ein anderer C++ Entwickler meine DLL verwenden wird. Vielleicht wird er für sein Programm auf Boost oder die STL setzen.
Jetzt stell dir folgende 2 Situationen vor.
1. Er wird irgendwann einen Rückgabewert meiner Funktion, welcher vom Typ int ist mit einem seiner Werte vergleichen wollen, vielleicht innerhalb eines Schleifendurchlaufs auf kleiner gleich oder sowas in der Art. Mit hoher Wahrscheinlichkeit ist sein Wert aber vom Typ size_t, zum Beispiel die Länge eines STL-Containers. Er wird (schon unter 32bit) eine Compilerwarnung erhalten, weil er eine Ungleichrelation zwischen einem signed (meinem int) und einem unsigned (seinem size_t) durchführt.
2. Er wird irgendwann mit hoher Wahrscheinlichkeit auf eine Situation treffen, wo er mir einen index oder size Parameter übergeben möchte, der bei ihm vom Typ size_t ist, ich aber als Typ int erwarte. Unter 64bit wird er mit meiner DLL Compilerwarnungen wegen möglichen Datenverlustes erhalten.
Er wird sich vielleicht über die DLL ärgern, weil sie stur auf int setzt. Oder? Wie sollte man am besten die Schnittstelle der DLL aufbauen, so das beide möglichst wenig Probleme haben? Ist Qt's Weg, konsequent auf signed int zu setzen wirklich der Richtige? Wenn ja, warum? Und warum machen es Bibliotheken wie die STL oder Boost anders?
Sagen wir mal, ich biete eine DLL an, die ausschließlich auf den Datentyp int setzt für Funktionen mit index-, size-, offset-, usw Parameter. Praktisch so, wie Qt es macht.
Ich kann nicht wissen, wie ein anderer C++ Entwickler meine DLL verwenden wird. Vielleicht wird er für sein Programm auf Boost oder die STL setzen.
Jetzt stell dir folgende 2 Situationen vor.
1. Er wird irgendwann einen Rückgabewert meiner Funktion, welcher vom Typ int ist mit einem seiner Werte vergleichen wollen, vielleicht innerhalb eines Schleifendurchlaufs auf kleiner gleich oder sowas in der Art. Mit hoher Wahrscheinlichkeit ist sein Wert aber vom Typ size_t, zum Beispiel die Länge eines STL-Containers. Er wird (schon unter 32bit) eine Compilerwarnung erhalten, weil er eine Ungleichrelation zwischen einem signed (meinem int) und einem unsigned (seinem size_t) durchführt.
2. Er wird irgendwann mit hoher Wahrscheinlichkeit auf eine Situation treffen, wo er mir einen index oder size Parameter übergeben möchte, der bei ihm vom Typ size_t ist, ich aber als Typ int erwarte. Unter 64bit wird er mit meiner DLL Compilerwarnungen wegen möglichen Datenverlustes erhalten.
Er wird sich vielleicht über die DLL ärgern, weil sie stur auf int setzt. Oder? Wie sollte man am besten die Schnittstelle der DLL aufbauen, so das beide möglichst wenig Probleme haben? Ist Qt's Weg, konsequent auf signed int zu setzen wirklich der Richtige? Wenn ja, warum? Und warum machen es Bibliotheken wie die STL oder Boost anders?
-
Christian81
- Beiträge: 7319
- Registriert: 26. August 2004 14:11
- Wohnort: Bremen
- Kontaktdaten:
Gib mir bitte ein sinnvolles Beispiel damit ich kapiere was Du meinst - die ganze Diskussion ist imho komplett überflüssig. Wenn muss ein Nutze meiner Library ein size_t aus einem Container an die Library übergeben? Ich würde da eher sagen dass die Library Müll ist... 
MfG Christian
'Funktioniert nicht' ist keine Fehlerbeschreibung
'Funktioniert nicht' ist keine Fehlerbeschreibung